[2D RPG] 프로젝트 세팅, 플레이어 이동
폴더 설정 및 에셋 추가
가장 먼저 Project 탭에 사용할 폴더를 숫자를 붙여 만들었다.
그 다음엔 사용할 에셋을 임포트해서
원하는 에셋을 찾아가기 쉽게, 맞는 폴더(Maps 등)에 넣었다.
에셋은 일단 플레이어 1, 맵 1, 몬스터 2 개의 에셋을 임포트했다.
해상도 설정
어떤 해상도에서든 게임이 잘 동작할 수 있게 설정했다.
Canvas를 하나 만들고,
Canvas 컴포넌트 안 Render Mode를 Screen Space - Camera로 설정한 후
Render Camera 항목에 일단 Main Camera를 넣었다.
Canvas Scaler 컴포넌트에
UI Scale Mode 항목을 Scale With Screen Size로 설정하고,
Reference Resolution을 1920 X 1080로 설정해 이 해상도로 작업하여, 다른 해상도도 이 해상도를 기준으로 Scale이 조정되어 일관성있게 화면이 표시되도록 설정했다.
마지막으로 Screen Match Mode를 Match Width Or Height로 설정한 후,
가로로 긴 게임이라 Match를 Width쪽으로 맞춰줬다.
플레이어
Player 오브젝트를 만들고
Sprite Renderer 컴포넌트를 추가, 에셋 스프라이트를 하나 넣어줬다.
플레이어 에셋 스프라이트가 좀 작게 보여 스프라이트의 Pixels Per Unit을 32에서 26으로 조정했다.
플레이어 이동
Input System을 사용하기 위해 Package Manager - Unity Registry에서 Input System을 임포트했다.
Input System을 임포트하면 자동으로 유니티 프로젝트를 재시작해준다.
Input Action을 Create하고 Default Map - Player로 설정한다.
Rigidbody 2D와 Capsule Collider 2D 컴포넌트를 추가해줬다.
+ 스프라이트 피봇 맞추기
콜라이더를 추가하고 조정하는데 피봇이 캐릭터의 발과 일치하지 않는 걸 발견했다.
이 경우엔 스프라이트의 Sprite Editor에서 Custom Pivot을 조정해주면 된다
다시 플레이어 이동으로 돌아와서
Project Settings - Input System Package에서
Create Setting Asset을 누른다음
Supported Devices에 사용할 디바이스를 넣어준다.
이 게임은 Keyboard와 Mouse가 필요하니 그 둘을 넣어줬다.
Input Action을 설정해줬다.
Input Action Asset 컴포넌트에서 Generate C# Class 를 눌러 자동으로 Input Action Class가 생성되도록 한다.
Class를 생성하는 이유는 입력 액션과 바인딩 이름을 코드에서 직접 참조할 수 있기 때문에,
문자열 기반 접근으로 인해 생기는 실수를 방지하고,
입력 관련 로직의 캡슐화로 유지보수도 효율적으로 할 수 있기 때문이다.
Input Action Class 기반으로 작성한 Player 코드
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class Player : MonoBehaviour
{
private MainInputAction _playerInput;
private Rigidbody2D _rb;
[SerializeField] private float _moveSpeed;
[SerializeField] private float _jumpForce;
private Vector2 _moveInput;
private bool _isJumping = false;
private void Awake()
{
_moveInput = Vector2.zero;
_playerInput = new MainInputAction();
_rb = GetComponent<Rigidbody2D>();
}
private void OnEnable()
{
_playerInput.Enable();
_playerInput.Player.Move.performed += OnMovePerformed;
_playerInput.Player.Move.canceled += OnMoveCanceled;
_playerInput.Player.Attack.performed += OnAttackPerformed;
_playerInput.Player.Jump.performed += OnJumpPerformed;
}
private void OnDisable()
{
_playerInput.Player.Move.performed -= OnMovePerformed;
_playerInput.Player.Move.canceled -= OnMoveCanceled;
_playerInput.Player.Attack.performed -= OnAttackPerformed;
_playerInput.Player.Jump.performed -= OnJumpPerformed;
_playerInput.Disable();
}
private void FixedUpdate()
{
Vector2 velocity = new Vector2(_moveInput.x * _moveSpeed, _rb.velocity.y);
_rb.velocity = velocity;
if (_isJumping)
{
_rb.AddForce(Vector2.up * _jumpForce, ForceMode2D.Impulse);
_isJumping = false;
}
}
private void OnMovePerformed(InputAction.CallbackContext context)
{
// Move 입력값 가져오기
_moveInput = context.ReadValue<Vector2>().normalized;
}
private void OnMoveCanceled(InputAction.CallbackContext context)
{
// Move 입력값 초기화
_moveInput = Vector2.zero;
}
private void OnAttackPerformed(InputAction.CallbackContext context)
{
// 공격 로직
Debug.Log("Attack!");
}
private void OnJumpPerformed(InputAction.CallbackContext context)
{
// 점프 로직
_isJumping = true;
Debug.Log("JUMP!");
}
}