왜.... 오셨나요?

부끄러워요

유니티개발 TIL

6주차 4일 TIL_유니티 입문(3D프로젝트_new InputSystem의 고찰)

와피했는데 2025. 8. 7. 00:40

들어가기 앞서


우리가 InputSystem을 사용할때 고민하는 문제중 하나가 Behavior옵션에 따라 코드도 다르고 움직임을 테스트할때 어떤게 더 효율적인지 성능적으로 괜찮은지 애매할때가 있다


R&D


new InputSystem의  Behavior옵션에 대한 고찰
1. Send Messages
동작 ● GameObject.SendMessage("OnJump", context) 처럼, string 이름의 메서드를 해당 게임오브젝트에 호출
장점 매우 간단하게 메서드 이름만 맞추면 연결 가능
단점 ● 리플렉션 기반으로 동작 => 문자열 비교 + 메서드 검색 비용
오타가 나면 런타임 에러 없이 무시되기 때문에 디버깅이 어렵다
용도(예시) ● 레거시 코드 연동, 빠른 프로토타이핑

2. Broadcast Messages
동작 ● SendMessage와 유사하지만, 호출된 게임오브젝트의 모든 자식에도 메시지를 전파
장점 계층 구조 전체에 동일 이벤트 전파
단점 ● 더 많은 수의 오브젝트를 검색·호출하므로 추가 비용
용도(예시) ● 부모만 액션을 알고 있고, 자식 오브젝트 여러 개가 반응해야 할 때

3. Invoke Unity Events
동작 Input Actions에 연결된 UnityEvent(인스펙터에서 드래그해서 연결한 메서드)들을 호출함
장점 에디터에서 직관적으로 연결 가능(드래그&드롭).
디자이너·아티스트와 협업할 때 편리.
단점 ● UnityEvent의 내부 구현(O(1)이라지만)과 리플렉션이 섞여 있어, 매 프레임 수백 번 호출 시 약간의 오버헤드가 생길 수 있습니다.(키입력 중 다른 키 입력시 즉각 반응이 안되는 상황이 발생할 수 있음=> 버벅임)
용도(예시) ● 씬에 배치된 UI나 간단한 트리거 이벤트 연결에 좋음.

4. Invoke C# Events
동작 Player Input 컴포넌트 내부에서 노출하는 onActionTriggered 같은 C# 이벤트(delegate) 를 += 로 구독한 콜백들을 호출
장점 완전한 타입 안정성(type-safe) 과 컴파일 타임 체크
UnityEvent 대비 저렴한 호출 비용
단점 ● 인스펙터에 표시되지 않아, 코드로 구독/해제 로직을 직접 작성해야 함
용도(예시) ● 퍼포먼스가 민감한 게임 플레이 로직(매프레임 이동, 연사 입력 등)에 추천

 

<정리>

방식 호출비용 타입 디버깅 용이성
Invoke C# Events 매우 낮음 (delegate) ✔ (컴파일 타임) 좋음
Invoke Unity Events 낮음 (UnityEvent) 부분적 (런타임 체킹) 보통
Send Messages 중간 (reflection) ❌ (문자열) 어려움
Broadcast Messages 중간↑ (reflection+재귀) 매우 어려움
Send Messages (UI) 중간 (UI raycast) UI 인터페이스 기반 용도 제한

★따봉 GPT야 정리 고마워! 

● 시사점 
직접 경험한 결과 Send Messages가 제일 움직임에 문제가 없었다 그리고 다른 키 바인딩, UI등도 고려하면 관리만 잘하면 다른 옵션들중 제일 밸런스가 좋은 옵션인듯 하다
이후 프레임이 제일 중요시 되는 FPS 장르에 관여된다면 Invoke C# Events로 구현을 시도해 볼듯하다

프로젝트 구현 단계


Rigidbody로 움직임 구현

MovePosition 채용

    //==============[Move]==============
    void Move()
    {
        Vector3 movement = new Vector3(dir.x, 0f, dir.z).normalized;
        if (movement.sqrMagnitude > 1f) movement.Normalize();
        Vector3 delta = movement * curSpeed * Time.fixedDeltaTime;
        rb.MovePosition(rb.position + delta);
    }

    public void OnMove(InputValue value)
    {
        dir.x = value.Get<Vector2>().x;
        dir.z = value.Get<Vector2>().y;
    }
● 시사점 
이전 2D프로젝트때는 velocity를 이용하여 일정한 속도의 움직임을 제어했는데 3D로 넘어오면서 중력과 마찰력있는 환경에서 velocity는 일정한 속도로 이동하지만 키입력이 없을때도 어느정도의 힘이 있는 상태로 미끌려 나가는 현상이 발견되어 제어를 위해 다른 품이 MovePosition의 비해 많이 들어간다 그리고 acceleration이나 velocity 갖은경우는 레이싱 종류나 마찰력이 크게 작용하는 게임에서 쓰일 만한 물리이동인것 같아 되도록 마찰력에 크게 관여되지 않고 다른 오브젝트와 충돌만 인식할수있게 MovePosition 채용하게됨


마우스를 통해 카메라 움직임 구현

오브젝트 기준 x축과 y축이 쓰인 연산이 다름

    //==============[ViewMouse]==============

    public void OnLook(InputValue value)
    {
        mouseDir = value.Get<Vector2>();

        // 마우스 움직임 기반 회전 처리
        float mouseX = mouseDir.x * lookSensitivity;
        float mouseY = mouseDir.y * lookSensitivity;

        // 수직 회전 - 카메라만 회전
        camCurXRot -= mouseY;
        camCurXRot = Mathf.Clamp(camCurXRot, minXLook, maxXLook);
        camara.localRotation = Quaternion.Euler(camCurXRot, 0f, 0f);

        // 수평 회전 - 플레이어 본체 회전
        transform.Rotate(Vector3.up * mouseX);

    }

 

왜 x축 따로 y축 따로 회전을 하게 되었는가
만약 두축(x,y)을 하나의 Transform에 모두 처리하면 짐벌락(Gimbal Lock) 같은 문제가 생기고, 상하 회전 시 캐릭터가 기울어지거나 비틀림이 발생할 가능성이 있기애 이러한 문제를 방지하고자 따로 구현됨


점프와 바닥감지 구현

● Raycast 채용하여 바닥감지

   //==============[Jump]==============
   void OnJump()
   {
       if(IsGround())
       rb.AddForce(Vector2.up * curJumpP, ForceMode.Impulse);
   }


   //==============[Ray]==============

   bool IsGround()
   {
       Ray[] rays = new Ray[4]
       {
           new Ray(transform.position + (transform.forward * 0.2f) + (transform.up * 0.01f), Vector3.down),
           new Ray(transform.position + (-transform.forward * 0.2f) + (transform.up * 0.01f), Vector3.down),
           new Ray(transform.position + (transform.right * 0.2f) + (transform.up * 0.01f), Vector3.down),
           new Ray(transform.position + (-transform.right * 0.2f) + (transform.up * 0.01f), Vector3.down),
       };

       for (int i = 0; i < rays.Length; i++)
       {
           if (Physics.Raycast(rays[i], 0.1f, LayerMask.GetMask("Ground")))
           {
               return true;
           }
       }
       return false;
   }
Raycast 이후 변경 계획
4곳을 쏘고있지만 이후 overlapsphere를 사용하여 각도를 보정하여 부채꼴 모양의 형태로 감지할수있게 디벨롭이 가능해 보임


버그수정


발생 CharacterManager에 Player클래스가 배치되지 않아 null에러가 남
문제 순서적 문제로 CharacterManager가 생성되기전에 Player클래스에서 GameManager.Character.Player = this; 구문이 출력되어 생긴 순서문제인듯함
해결 스크립트 실행 순서를 변경하여 해결함

GameManager클래스명 위에 DefaultExecutionOrder를 추가 및 조정하여 다른 script들 보다 먼저 실행되게 설정
(아래처럼 설정)

[DefaultExecutionOrder(-100)] <= 추가
public class GameManager : MonoBehaviour

마치며.


 

3D로 넘어오게 되면서 물리판정에 대해서 더욱 신경을 쓰는것 같다 2D는 시점적 허용으로 물리적 사용에 대한 연관이 크게 작용하지 않아서 부수적인 요소를 신경을 덜 썼던것 같은데 3D세상은 아무래도 2D 보단 더 신경써야할것이 쉽게 z축이 생긴것 부터 시작해서 많아진 느낌이다