게임 제작기

[2D RPG] 프로젝트 세팅, 플레이어 이동

빵어 2024. 11. 22. 18:12

폴더 설정 및 에셋 추가

가장 먼저 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을 조정해주면 된다

Custom Pivot의 Y를 0.04으로 조정했다

 

피봇과 맞춰진 스프라이트

 

 

 

다시 플레이어 이동으로 돌아와서

Project Settings - Input System Package에서

Create Setting Asset을 누른다음

Supported Devices에 사용할 디바이스를 넣어준다.

 

이 게임은 Keyboard와 Mouse가 필요하니 그 둘을 넣어줬다.

 

 

 

Input Action을 설정해줬다.

Player 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!");
    }
}