[TIL] Unity - SerializeField , InputSystem

MINO·2024년 5월 8일
0
post-thumbnail

2024-05-08

새로운 팀과 함께 새로운 주차가 시작되었다.

Unity 게임 개발 입문 강의를 통해,
2D 탑다운 형식의 게임을 따라 만들어보고,
개인 프로젝트 탑뷰 게임을 만들어보는 것이 이번 주차의 목표이다.


SerializeField

스크립트에서 Private 로 선언된 필드를 직렬화하기 위해 사용한다.

  • 직렬화 : 데이터 구조나 GameObject 상태를 Unity가 나중에 저장하고 재구성할 수 있는 형식으로 변환하는 자동 프로세스

SerializeField 를 사용하는 이유

유니티 스크립트에서 public 필드만 직렬화 할 수 있다.
그러나, [SerializeField] 를 사용하여 private 필드도 직렬화 할 수 있다.

( 스크립트에서 private 로 지정한 변수는 Unity 인스펙터 창에서 볼 수 없다. )

public int publicVariable;
private int privateVariable;
[SerializeField] int serializeFieldVariable;

private void Start()
{
	publicVariable = 10; // 외부에서 접근 가능
    privateVariable = 20; // 클래스 내부에서만 접근 가능
    serializeFieldVariable = 30; // 인스펙터에서 접근 가능

이외에도 직렬화를 커스터마이징 하고 싶을 때, 코드를 더 쉽게 업데이트하기 위해 등이 있다.

참고 블로그 - SerializeField


예시

Square 오브젝트에 아래의 InputManager.cs 스크립트와 Rigidbody2D 컴포넌트를 추가해줘서
방향키로 오브젝트를 이동할 수 있게 만들었다.

InputManager.csInspector 창

[SerializeField] 로 선언해준 private float speed1 과 private float speed2 가 있는데,

Inspector 창의 InputManager 스크립트를 보면 Speed1 에만 접근할 수 있다.


결과물


InputSystem

기존 InputManager 문제점

위에서 사용한 InputManager 방법은
다양한 플랫폼 (플레이스테이션, 닌텐도 스위치, 모바일) 에 대응하거나,
키를 변경하는 리바인딩이 쉽지 않다.

확장성과 유지보수를 위해 기능별로 클래스를 나누는 방법인 단일 책임 원칙(Single Responsibility Principle) 을 통해 InputSystem 을 도입하였다.


New InputSystem 핵심 개념

  • Input Action : 입력 행동을 정의
    • 점프, 공격, 이동 등의 행동을 정의하고, 이러한 행동을 유발하는 키나 버튼을 지정
  • Input Action Asset : 여러 개의 입력 행동을 그룹화하는 방법
    • 재사용 가능한 입력 설정을 만들어 게임 내의 다른 캐릭터나 메뉴에 적용할 수 있음
  • Player Input Component : 입력 행동을 처리하고 해당 게임 오브젝트에 메시지를 보내는 컴포넌트

New InputSystem 장점

  • Cross-Platform Compatibility : 다양한 플랫폼과 입력 장치에 대해 지원
  • Rebinding : 플레이어가 게임 내에서 입력 설정을 변경할 수 있도록 지원
  • Multiplayer Support : 여러 플레이어가 동일한 장치에서 게임을 플레이하거나, 각각의 장치에서 게임을 플레이할 때 입력을 쉽게 처리

패키지 추가

Input System 을 사용하기 위해서는 패키지를 추가해줘야 한다.

Window - Package Manager - Unity Registry - Input System - Install


사용 방법

예시로 WASD 방향으로 이동시키고, 마우스를 향해 바라보는 플레이어를 만들 것이다.

  1. Control Scheme - Add Control Scheme - 리스트에 Keyboard 와 Mouse 를 추가
  1. Action Map : Action 들의 집합 - Player 추가
    Actions : 동작이나 행동 - Look , Move 추가 (Type - Value - Vector 2)
    Fire (Type - Value - Any)
  1. Binding 하기
    (Move 의 경우, W/S/A/D 를 합쳐서 Vector 2 를 만드는 것이기 때문에 바인딩을 활용해야 함
    Look 의 경우, 마우스의 위치가 곧 Vector 2 이기 때문에, 마우스 포인트를 바인딩 해야 함
    )
  1. Save Asset 을 클릭하여 저장

Action Type설명
Value일반적인 상태에서 사용. 눌렀을 때, 누르고 있을 때, 뗄 때 등 다양한 상황에 대응
Button눌렀을 때 발생하는 액션에 활용. Control Type 이 Button 으로 고정됨
Pass-ThroughValue 와 비슷하지만, 명확성를 사용하지 않음

코드

TopDownController : 캐릭터의 이동과 회전을 처리하는 핵심 컨트롤러 역할

public class TopDownController : MonoBehaviour
{
    public event Action<Vector2> OnMoveEvent;
    public event Action<Vector2> OnLookEvent;


    public void CallMoveEvent(Vector2 direction)
    {
        OnMoveEvent?.Invoke(direction);
    }

    public void CallLookEvent(Vector2 direction)
    {
        OnLookEvent?.Invoke(direction);
    }

}

PlayerInputController : 플레이어의 입력을 처리하는 역할

using UnityEngine;
using UnityEngine.InputSystem;
using static UnityEngine.RuleTile.TilingRuleOutput;

public class PlayerInputController : TopDownController
{
    private Camera camera;
    private void Awake()
    {
        camera = Camera.main;
    }

    public void OnMove(InputValue value)
    {
        // Debug.Log("OnMove" + value.ToString());
        Vector2 moveInput = value.Get<Vector2>().normalized;
        CallMoveEvent(moveInput);
    }

    public void OnLook(InputValue value)
    {
        // Debug.Log("OnLook" + value.ToString());
        Vector2 newAim = value.Get<Vector2>();
        Vector2 worldPos = camera.ScreenToWorldPoint(newAim);
        newAim = (worldPos - (Vector2)transform.position).normalized;

    }

}

TopDownMovement : 실제 캐릭터의 이동을 구현하는 역할

using UnityEngine;

// TopDownMovement는 캐릭터와 몬스터의 이동에 사용될 예정입니다.
public class TopDownMovement : MonoBehaviour
{
    private TopDownController movementController;
    private Rigidbody2D movementRigidbody;

    private Vector2 movementDirection = Vector2.zero;

    private void Awake()
    {
        // 같은 게임오브젝트의 TopDownController, Rigidbody를 가져올 것 
        movementController = GetComponent<TopDownController>();
        movementRigidbody = GetComponent<Rigidbody2D>();
    }

    private void Start()
    {
        // OnMoveEvent에 Move를 호출하라고 등록함
        movementController.OnMoveEvent += Move;
    }

    private void FixedUpdate()
    {
        // 물리 업데이트에서 움직임 적용
        ApplyMovement(movementDirection);
    }

    private void Move(Vector2 direction)
    {
        // 이동방향만 정해두고 실제로 움직이지는 않음.
        // 움직이는 것은 물리 업데이트에서 진행(rigidbody가 물리니까)
        movementDirection = direction;
    }

    private void ApplyMovement(Vector2 direction)
    {
        direction = direction * 5;

        movementRigidbody.velocity = direction;
    }
}

결과물

Player 라는 오브젝트를 생성하여, Rigidbody2D 와 Player Input 컴포넌트, Player Input Controller.cs 와 Top Down Movement.cs 를 추가해주었다.



TIL 마무리

갑자기 난이도가 확 올라간 느낌이다.

단순히 기능 구현을 목표로 공부하는 것이 아닌
현업에서 다양한 플랫폼에 지원하기 위해서 새로운 기술을 도입하고 응용하는 법을 배운 것 같아
신기하기도 하지만 아직까진 잘 와닿지 않는다.

개인 프로젝트를 진행하면서, 다시 강의를 들으며 복습해봐야겠다.

profile
안녕하세요 게임 개발하는 MINO 입니다.

0개의 댓글