카테고리 없음

2025.05.28 8주차 수요일

mochoa 2025. 5. 28. 21:23

주요 구현 내용 요약

1.Unity에서 3인칭과 1인칭을 전환 가능한 캐릭터 카메라 시스템

2. Unity 인게임 메뉴 버튼 UI 구현하기

 

1.Unity에서 3인칭과 1인칭을 전환 가능한 캐릭터 카메라 시스템

목표

Unity에서 3인칭과 1인칭을 전환 가능한 캐릭터 카메라 시스템을 구현하고자 함.
Cinemachine을 사용하여 CM_3rdPerson (FreeLook), CM_1stPerson (VirtualCamera) 두 가지 시점을 CameraSwitcher를 통해 전환하는 구조를 구현.

 

시도한 작업

  • CameraSwitcher.cs 를 통해 firstPersonCam, thirdPersonCam 전환 코드 구현
  • PlayerController.cs 에서 1인칭 시 Look() 처리 직접 수행 (cameraTarget 회전 + transform.Rotate)
  • CM_1stPerson에 Follow, LookAt을 빈 오브젝트로 지정 (플레이어 머리 근처 위치)
  • 1인칭 전환 시 cameraTarget.position = cameraTargetPosition.position; 으로 위치 갱신

발생한 문제

  1. UnassignedReferenceException: cameraTarget, cameraTargetPosition 등 인스펙터에서 참조 누락
  2. firstPersonCam 관련 NullReferenceException → CameraSwitcher 내부에서 해당 필드가 빠져있던 문제
  3. 1인칭 카메라가 "한 번 움직인 후 멈추는 현상" 발생
    → Cinemachine VirtualCamera가 Follow, LookAt 대상을 잃었거나, 위치 갱신이 잘못되었을 가능성
  4. 3인칭에서 전환할 때 FreeLook의 LookAt Intensity가 비정상적으로 적용됨 → 전환 시 값 초기화 문제로 추정

느낀 점

  • Cinemachine의 VirtualCamera는 Follow/LookAt 대상을 정확히 지정하지 않으면 예측 불가능한 결과를 보인다.
  • 1인칭은 FreeLook 없이 직접 회전 처리해야 하므로 Transform 직접 회전 제어가 필요하다.
  • 시점 전환은 CameraSwitcher에서 관리하되, 실제 위치/회전 처리는 PlayerController에서 별도로 처리하는 구조가 깔끔함.
  • 디버깅할 땐 Debug.Log()로 Follow/LookAt 대상 실시간 추적이 중요했다.

다음 할 일

  • firstPersonCam.Follow, LookAt이 null로 바뀌는 원인 추적 → CameraSwitcher.SwitchToFirstPerson()에서 강제 재할당 테스트
  • cameraTarget이 너무 빠르게 위치를 이동하는지 또는 Rigidbody와 충돌하는지 확인
  • 1인칭용 빈 오브젝트를 플레이어 외부에 두는 대신, 독립적으로 따라가는 방식 고려 (e.g., empty CameraRig 오브젝트 활용)
  • 필요 시 Cinemachine 대신 완전 수동 1인칭 카메라 구현 고려

 


 

2. Unity 인게임 메뉴 버튼 UI 구현하기

목표

게임 도중 언제든지 메뉴(☰) 버튼을 눌러서

  • 일시정지된 메뉴를 열고
  • 돌아가기 / 메인화면 / 게임 종료
    를 선택할 수 있도록 인게임 메뉴 시스템을 구현했다.

단, 시작 시 시네마틱(Intro Timeline)이 재생되는 동안에는
☰ 메뉴 버튼이 보이지 않고 작동하지 않도록 제어했다.


기본 구조

구성 요소 설명
menuButton 플레이 중 우측 상단 등에서 누를 수 있는 메뉴 버튼 (☰)
menuPanel 메뉴 버튼을 누르면 열리는 메뉴 UI
CameraIntroController.cs Timeline 재생/종료를 관리하며 메뉴 버튼 활성/비활성 제어
InGameMenuController.cs 메뉴 UI의 동작(열기/닫기/종료 등)을 제어

UI 계층 구조 예시 (Hierarchy)

Canvas
├── menuButton (☰)
├── menuPanel (SetActive: false)
│   ├── BackButton
│   ├── GoToMainButton
│   └── QuitButton

 

핵심 스크립트

1. CameraIntroController.cs

인트로 Timeline이 재생 중일 땐 메뉴 버튼을 숨기고,
끝났을 때 활성화한다.

public class CameraIntroController : MonoBehaviour
{
    public PlayableDirector director;
    public GameObject player;
    public GameObject uiRoot;
    public GameObject menuButton;

    public static bool IsIntroPlaying { get; private set; } = true;

    void Start()
    {
        player.SetActive(false);
        uiRoot.SetActive(false);
        menuButton.SetActive(false); // 메뉴 버튼 비활성화

        IsIntroPlaying = true;
        director.stopped += OnFinished;
        director.Play();
    }

    void OnFinished(PlayableDirector d)
    {
        IsIntroPlaying = false;
        player.SetActive(true);
        uiRoot.SetActive(true);
        menuButton.SetActive(true); // 인트로 끝나면 활성화
    }
}

2.  InGameMenuController.cs

☰ 버튼을 누르면 메뉴를 열고,
돌아가기, 메인화면, 종료 기능을 제공한다.
인트로 중에는 메뉴를 열 수 없도록 막는다.

public class InGameMenuController : MonoBehaviour
{
    public GameObject menuPanel;
    public string mainMenuSceneName = "TitleScene";
    public GameObject menuButton;

    private bool isMenuOpen = false;

    public void OnClickMenuButton()
    {
        if (CameraIntroController.IsIntroPlaying)
        {
            Debug.Log("[메뉴] 인트로 중에는 메뉴를 열 수 없습니다.");
            return;
        }

        menuPanel.SetActive(true);
        isMenuOpen = true;
        Time.timeScale = 0f;
    }

    public void OnClickBack()
    {
        menuPanel.SetActive(false);
        isMenuOpen = false;
        Time.timeScale = 1f;
    }

    public void OnClickGoToMain()
    {
        Time.timeScale = 1f;
        SceneManager.LoadScene(mainMenuSceneName);
    }

    public void OnClickQuit()
    {
#if UNITY_EDITOR
        UnityEditor.EditorApplication.isPlaying = false;
#else
        Application.Quit();
#endif
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape) && isMenuOpen)
        {
            OnClickBack();
        }
    }
}

 


배운 점

  • Intro Timeline 중에는 메뉴 버튼 UI와 메뉴 입력을 모두 막아야 버그가 안 생긴다.
  • Time.timeScale = 0f를 사용해 게임을 일시정지할 수 있다.
  • PlayableDirector.stopped 이벤트로 Timeline 종료를 감지할 수 있다.
  • SetActive()를 적절히 활용하면 UI 흐름을 유연하게 제어할 수 있다.