아니 이거 버츄얼 카메라 transform이나 버츄얼 카메라 부모 요소 transform 변경해도 자꾸 관전 대상에서 멀찍이 떨어져만 있는데 스크립트에서 고쳐야 되는거임?
이 문제는 Cinemachine Virtual Camera의 Body 설정, 특히 Transposer 또는 Composer와 관련이 있을 가능성이 높습니다. Cinemachine의 Follow 기능을 사용할 때, 가상 카메라가 대상에서 일정한 거리를 유지하도록 설정되어 있기 때문에, 대상에 가까이 있지 않고 멀찍이 떨어져 있을 수 있습니다.
해결 방법:
1. Cinemachine Virtual Camera의 Body 설정 확인 (Transposer or Composer)
Cinemachine Virtual Camera에서 Body가 Transposer로 설정되어 있다면, Follow Offset 값에 따라 카메라가 대상에서 일정한 거리를 유지합니다.
이 값을 조정하여 카메라가 대상에 가까이 있거나, 적절한 위치에서 대상을 바라보도록 설정할 수 있습니다.
내가 짠 코드와 팀장님이 짜주신 기초 코드를 참고해서 초안 코드 작성해달라고 함.
## 코드 1 : SpectatorManager.cs
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class SpectatorManager : Singleton<SpectatorManager>
{
private List<GameObject> RemainingPlayers = new List<GameObject>(); // 남아있는 플레이어
public GameObject SpectatingTarget; // 내가 관전 중인 플레이어
private bool isSpectating = false;
public GameObject prefab;
private PlayerInput playerInput;
private InputAction lookAction;
// 본인이 죽으면 호출되는 함수
public void StartSpectating()
{
Debug.Log("Start Spectating");
foreach (Transform child in transform)
{
child.gameObject.SetActive(true);
}
isSpectating = true;
RemainingPlayers = new List<GameObject>();
RemainingPlayers.AddRange(GameObject.FindGameObjectsWithTag("Player"));
}
// 남아있는 플레이어 중에서 다음 플레이어로 변경
private void UpdateSpectatingPlayer()
{
if (RemainingPlayers.Count == 0)
{
Debug.Log("No players to spectate");
return;
}
SpectatingTarget = RemainingPlayers[0];
GetComponent<SpectatorCamera>().SetSpectatingTarget(SpectatingTarget);
Debug.Log("Remaining Players: " + SpectatingTarget.name);
}
// 누군가 죽었을 때 호출되는 함수
public void RemoveRemainingPlayer(GameObject player)
{
if (isSpectating)
{
RemainingPlayers.Remove(player);
// 죽은 친구가 지금 관전 중인 친구면 카메라를 옮겨야 함
if (player == SpectatingTarget)
UpdateSpectatingPlayer();
}
}
// 마우스 클릭했을 때 호출되는 함수
private void MoveToAnotherPlayer(int idx_move)
{
if (RemainingPlayers.Count == 0)
return;
int idx = RemainingPlayers.IndexOf(SpectatingTarget);
idx += idx_move;
if (idx < 0)
idx = RemainingPlayers.Count - 1;
else if (idx >= RemainingPlayers.Count)
idx = 0;
SpectatingTarget = RemainingPlayers[idx];
GetComponent<SpectatorCamera>().SetSpectatingTarget(SpectatingTarget);
Debug.Log("Move to another player: " + SpectatingTarget.name);
}
// 마우스 왼쪽 클릭 시 이전 플레이어로 전환
public void OnPrevPlayer(InputAction.CallbackContext context)
{
if (isSpectating)
{
MoveToAnotherPlayer(-1); // 왼쪽 클릭은 이전 플레이어
}
}
// 마우스 오른쪽 클릭 시 다음 플레이어로 전환
public void OnNextPlayer(InputAction.CallbackContext context)
{
if (isSpectating)
{
MoveToAnotherPlayer(1); // 오른쪽 클릭은 다음 플레이어
}
}
}
## 코드 2 : ExampleSpectator.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class SpectateCamera : MonoBehaviour
{
public float mouseSensitivity = 100f;
public Transform[] playerBodys;
public Transform cameraBody;
float xRotation = 0f;
// Input Actions 변수
private PlayerInput playerInput;
private InputAction lookAction;
private InputAction changeSpectatePlayerAction;
private int currentPlayerNum = 0;
void Awake()
{
// PlayerInput 컴포넌트에서 InputAction 가져오기
playerInput = GetComponent<PlayerInput>();
lookAction = playerInput.actions["Look"];
//playerBody = GetComponentInParent<CharacterController>().transform;
changeSpectatePlayerAction = playerInput.actions["ChangeSpectate"];
}
void Start()
{
Cursor.lockState = CursorLockMode.Locked;
}
// Update is called once per frame
void Update()
{
if (changeSpectatePlayerAction.triggered)
{
currentPlayerNum += 1;
if (currentPlayerNum >= playerBodys.Length) currentPlayerNum = 0;
}
this.transform.position = playerBodys[currentPlayerNum].transform.position;
// Look 액션으로 마우스 움직임 입력 받기
Vector2 mouseDelta = lookAction.ReadValue<Vector2>();
float mouseX = mouseDelta.x * mouseSensitivity * Time.deltaTime;
float mouseY = mouseDelta.y * mouseSensitivity * Time.deltaTime;
// 상하 회전 제한
xRotation -= mouseY;
xRotation = Mathf.Clamp(xRotation, -90f, 90f);
// 카메라 상하 회전
//transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);
// 플레이어 좌우 회전
cameraBody.Rotate(Vector3.up * mouseX);
}
}
요청 사항
나는 빈 게임 오브젝트인 Spectator에 자식으로 Virtual Camera와 CinemachineBrain을 달아놨어. Spectator는 프리팹화해놓을거고, 유저가 죽으면 코드 1의 StartSpectating()을 실행하고, 현재 내가 준 코드 1을 가지고 있는 SpectatorManager라는 오브젝트에서 Spectator를 instatntiate할거야. 그러면 Spectator가 생성되어 코드 2에서 관전하는 방식대로 관전을 시작하게 할 거야.
그러기 위해서
- 코드 1을 어떻게 수정해야 할지
- 코드 2를 참고하여 Spectator에 붙일 코드를 어떻게 작성해야 할지 알려줘
이하는 gpt가 짜준 코드
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class SpectatorManager : Singleton<SpectatorManager>
{
public GameObject spectatorPrefab; // Reference to your Spectator prefab
private bool isSpectating = false;
private List<GameObject> remainingPlayers = new List<GameObject>();
private GameObject spectatorInstance;
// Called when the player dies
public void StartSpectating()
{
Debug.Log("Start Spectating");
isSpectating = true;
// Get the list of remaining players
remainingPlayers = new List<GameObject>(GameObject.FindGameObjectsWithTag("Player"));
// Instantiate the Spectator prefab
spectatorInstance = Instantiate(spectatorPrefab);
// Pass the list of remaining players to the Spectator script
SpectatorCamera spectatorCamera = spectatorInstance.GetComponent<SpectatorCamera>();
if (spectatorCamera != null)
{
spectatorCamera.SetRemainingPlayers(remainingPlayers);
}
else
{
Debug.LogError("SpectatorCamera component not found on Spectator prefab.");
}
}
// Called when a player dies
public void RemoveRemainingPlayer(GameObject player)
{
if (isSpectating)
{
remainingPlayers.Remove(player);
// Notify the SpectatorCamera script
if (spectatorInstance != null)
{
SpectatorCamera spectatorCamera = spectatorInstance.GetComponent<SpectatorCamera>();
if (spectatorCamera != null)
{
spectatorCamera.RemovePlayer(player);
}
}
}
}
}
SpectatorManager.cs
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using Cinemachine;
public class SpectatorCamera : MonoBehaviour
{
public float mouseSensitivity = 100f;
private List<GameObject> remainingPlayers = new List<GameObject>();
private GameObject spectatingTarget;
private int currentPlayerIndex = 0;
private float xRotation = 0f;
// Input Actions
private PlayerInput playerInput;
private InputAction lookAction;
private InputAction prevPlayerAction;
private InputAction nextPlayerAction;
private CinemachineVirtualCamera virtualCamera;
void Awake()
{
playerInput = GetComponent<PlayerInput>();
lookAction = playerInput.actions["Look"];
prevPlayerAction = playerInput.actions["PrevPlayer"];
nextPlayerAction = playerInput.actions["NextPlayer"];
virtualCamera = GetComponentInChildren<CinemachineVirtualCamera>();
}
void Start()
{
Cursor.lockState = CursorLockMode.Locked;
}
// Called by SpectatorManager to set the list of remaining players
public void SetRemainingPlayers(List<GameObject> players)
{
remainingPlayers = players;
if (remainingPlayers.Count > 0)
{
currentPlayerIndex = 0;
spectatingTarget = remainingPlayers[currentPlayerIndex];
UpdateCameraTarget();
}
else
{
Debug.LogWarning("No players left to spectate.");
}
}
void Update()
{
// Handle switching between players
if (prevPlayerAction.triggered)
{
SwitchToPrevPlayer();
}
if (nextPlayerAction.triggered)
{
SwitchToNextPlayer();
}
// Handle look around
HandleLook();
}
private void HandleLook()
{
Vector2 mouseDelta = lookAction.ReadValue<Vector2>();
float mouseX = mouseDelta.x * mouseSensitivity * Time.deltaTime;
float mouseY = mouseDelta.y * mouseSensitivity * Time.deltaTime;
xRotation -= mouseY;
xRotation = Mathf.Clamp(xRotation, -90f, 90f);
// Rotate the camera vertically
virtualCamera.transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);
// Rotate the spectator horizontally
transform.Rotate(Vector3.up * mouseX);
}
private void UpdateCameraTarget()
{
if (spectatingTarget != null)
{
// Set the virtual camera's Follow and LookAt targets
virtualCamera.Follow = spectatingTarget.transform;
virtualCamera.LookAt = spectatingTarget.transform;
Debug.Log("Spectating: " + spectatingTarget.name);
}
else
{
Debug.LogWarning("Spectating target is null.");
}
}
private void SwitchToPrevPlayer()
{
if (remainingPlayers.Count == 0)
return;
currentPlayerIndex--;
if (currentPlayerIndex < 0)
currentPlayerIndex = remainingPlayers.Count - 1;
spectatingTarget = remainingPlayers[currentPlayerIndex];
UpdateCameraTarget();
}
private void SwitchToNextPlayer()
{
if (remainingPlayers.Count == 0)
return;
currentPlayerIndex++;
if (currentPlayerIndex >= remainingPlayers.Count)
currentPlayerIndex = 0;
spectatingTarget = remainingPlayers[currentPlayerIndex];
UpdateCameraTarget();
}
// Called by SpectatorManager when a player dies
public void RemovePlayer(GameObject player)
{
remainingPlayers.Remove(player);
if (spectatingTarget == player)
{
if (remainingPlayers.Count > 0)
{
currentPlayerIndex = currentPlayerIndex % remainingPlayers.Count;
spectatingTarget = remainingPlayers[currentPlayerIndex];
UpdateCameraTarget();
}
else
{
// No players left to spectate
spectatingTarget = null;
virtualCamera.Follow = null;
virtualCamera.LookAt = null;
Debug.LogWarning("No players left to spectate.");
}
}
}
}
SpectatorCamera.cs
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class SpectatorManager : Singleton<SpectatorManager>
{
public GameObject spectatorPrefab; // Reference to your Spectator prefab
private bool isSpectating = false;
private List<GameObject> remainingPlayers = new List<GameObject>();
private GameObject spectatorInstance;
private void Awake()
{
StartSpectating();
}
// 자신이 죽었을 때 호출
public void StartSpectating()
{
Debug.Log("Start Spectating");
isSpectating = true;
spectatorInstance = Instantiate(spectatorPrefab);
}
// 플레이어가 죽었을 때 호출
public void RemoveRemainingPlayer(GameObject player)
{
if (isSpectating)
spectatorInstance.GetComponent<SpectatorCamera>().RemovePlayer(player);
}
}
SpectatorManager.cs
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using Cinemachine;
public class SpectatorCamera : MonoBehaviour
{
public float mouseSensitivity = 100f;
private List<GameObject> remainingPlayers = new List<GameObject>();
private GameObject spectatingTarget;
private int currentPlayerIndex = 0;
private float xRotation = 0f;
private PlayerInput playerInput;
private InputAction lookAction;
private InputAction prevPlayerAction;
private InputAction nextPlayerAction;
private CinemachineVirtualCamera virtualCamera;
void Awake()
{
playerInput = GetComponent<PlayerInput>();
lookAction = playerInput.actions["Look"];
prevPlayerAction = playerInput.actions["PrevPlayer"];
nextPlayerAction = playerInput.actions["NextPlayer"];
remainingPlayers = new List<GameObject>(GameObject.FindGameObjectsWithTag("Player"));
virtualCamera = GetComponentInChildren<CinemachineVirtualCamera>();
}
void Start()
{
Cursor.lockState = CursorLockMode.Locked;
virtualCamera.Follow = this.transform;
}
// 관전 대상 리스트 초기화
public void SetRemainingPlayers(List<GameObject> players)
{
remainingPlayers = players;
if (remainingPlayers.Count > 0)
{
currentPlayerIndex = 0;
spectatingTarget = remainingPlayers[currentPlayerIndex];
}
else
{
Debug.LogWarning("No players left to spectate.");
}
}
// Update로 관전 대상 변경 감지, 버츄얼 카메라 위치 업데이트
void Update()
{
// Handle switching between players
if (prevPlayerAction.triggered)
{
SwitchToPrevPlayer();
}
if (nextPlayerAction.triggered)
{
SwitchToNextPlayer();
}
UpdateVirtualCameraPosition();
}
// 버츄얼 카메라 위치 업데이트
private void UpdateVirtualCameraPosition()
{
this.transform.position = spectatingTarget.transform.position;
Vector2 mouseDelta = lookAction.ReadValue<Vector2>();
float mouseX = mouseDelta.x * mouseSensitivity * Time.deltaTime;
float mouseY = mouseDelta.y * mouseSensitivity * Time.deltaTime;
xRotation -= mouseY;
xRotation = Mathf.Clamp(xRotation, -90f, 90f);
transform.Rotate(Vector3.up * mouseX);
}
// 관전 대상 변경
private void SwitchToPrevPlayer()
{
if (remainingPlayers.Count == 0)
return;
currentPlayerIndex--;
if (currentPlayerIndex < 0)
currentPlayerIndex = remainingPlayers.Count - 1;
spectatingTarget = remainingPlayers[currentPlayerIndex];
}
private void SwitchToNextPlayer()
{
if (remainingPlayers.Count == 0)
return;
currentPlayerIndex++;
if (currentPlayerIndex >= remainingPlayers.Count)
currentPlayerIndex = 0;
spectatingTarget = remainingPlayers[currentPlayerIndex];
}
// 어떤 플레이어가 죽었을 때 SpectatorManager가 호출
public void RemovePlayer(GameObject player)
{
remainingPlayers.Remove(player);
if (spectatingTarget == player)
{
currentPlayerIndex = currentPlayerIndex % remainingPlayers.Count;
spectatingTarget = remainingPlayers[currentPlayerIndex];
}
}
}
SpectatorCamera.cs