
์ปจํธ๋กค๋ฌ ์ฝ๋๋ฅผ Player์ ์ ์ฉํ ๋ ํค๋ฐ์ธ๋ฉ์ ์ํ Input Action Asset์ ๋ง๋ค์ด์ผํฉ๋๋ค.
Input Action Asset"Wํค์ ์์ชฝ ํ์ดํ๊ฐ Move๋ค" โ ํค๋ฐ์ธ๋ฉ ์ค์
PlayerController2D.cs"Move ์ ๋ ฅ๊ฐ์ผ๋ก ์บ๋ฆญํฐ๋ฅผ ์์ง์ธ๋ค" โ ๋ก์ง
Project ์ฐฝ ์ฐํด๋ฆญ โ Create > Input Actions
ํ์ผ ์ด๋ฆ์ ์ ํํ PlayerInputActions ๋ก ์ง์
์์ฑ๋ PlayerInputActions ํ์ผ ๋๋ธํด๋ฆญ โ Input Action ์๋ํฐ ์คํ
์ข์ธก Action Maps ํจ๋์ ์ด๋ฆ์ Player ๋ก ๋ณ๊ฒฝ
(List is empty์ ๊ฒฝ์ฐ + ๋๋ฌ์ ์์ฑ)
์ค์ Actions ํจ๋์์ ๊ธฐ๋ณธ ์์ฑ๋ Action ์ด๋ฆ์ Move ๋ก ๋ณ๊ฒฝ
์ฐ์ธก Properties์์
Action Type โ Value
Control Type โ Vector 2
Move ์๋ + ํด๋ฆญ โ Add Up/Dow/Left/Right Composite ์ ํ โ 2D Vector ํญ๋ชฉ ์์ฑ
๊ฐ ๋ฐฉํฅ ํด๋ฆญ ํ ์ฐ์ธก Path์์ ๋ฐ์ธ๋ฉ: (Listen์ ํด๋ฆญ ํ ํด๋น ํค ๋๋ฅด๊ธฐ)
Up โ W [Keyboard]
Down โ S [Keyboard]
Left โ A [Keyboard]
Right โ D [Keyboard]
Move ์๋ + ํด๋ฆญ โ Add 2D Vector Composite ํ ๋ฒ ๋ ์ถ๊ฐ
Up โ Up Arrow [Keyboard]
Down โ Down Arrow [Keyboard]
Left โ Left Arrow [Keyboard]
Right โ Right Arrow [Keyboard]
๋ฐ์ธ๋ฉ์ด ๋๋ ํ Save Asset ํด๋ฆญํ์ฌ ์ค์ ์ ์ฅ
Asset์์ PlayerInputActions ์ ํ โ Inspector์์
Generate C# Class ํญ๋ชฉ์ ์ฒดํฌApply ํด๋ฆญPlayerInputActions.cs ํ์ผ์ด ์๋ ์์ฑ๋ฉ๋๋ค. PlayerController2D.cs๊ฐ ์๋ ํด๋์ ํจ๊ป ๋ฃ์ต๋๋ค.
Unity 6+ / New Input System / Rigidbody2D / Top-down 2D
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
public class PlayerController2D : MonoBehaviour
{
// ===================================================
// Inspector Fields
// ===================================================
[Header("์ด๋ ์ค์ ")]
public float moveSpeed = 3.5f;
[Header("์ปดํฌ๋ํธ ์ฐธ์กฐ")]
[SerializeField] private Rigidbody2D rb;
[SerializeField] private Animator animator;
// ===================================================
// Animator Parameter Hashes
// ๋งค ํ๋ ์ ๋ฌธ์์ด ํด์ ๋ณํ ๋น์ฉ์ ์์ ๊ธฐ ์ํด ๋ฏธ๋ฆฌ ์บ์ฑ
// ===================================================
private static readonly int HashMoveX = Animator.StringToHash("moveX");
private static readonly int HashMoveY = Animator.StringToHash("moveY");
private static readonly int HashIsMoving = Animator.StringToHash("isMoving");
private static readonly int HashLastMoveX = Animator.StringToHash("lastMoveX");
private static readonly int HashLastMoveY = Animator.StringToHash("lastMoveY");
// ===================================================
// Runtime State
// ===================================================
private PlayerInputActions input;
// ํ์ฌ ํ๋ ์์ ์ด๋ ์
๋ ฅ๊ฐ (FixedUpdate์์ ๋ฌผ๋ฆฌ ์ด๋์ ์ฌ์ฉ)
private Vector2 moveInput = Vector2.zero;
// ๋ง์ง๋ง์ผ๋ก ์ด๋ํ ๋ฐฉํฅ โ ์ ์ง ์ idle ๋ฐฉํฅ ๊ฒฐ์ ์ ์ฌ์ฉ
// ์ด๊ธฐ๊ฐ down: ๊ฒ์ ์์ ์ ์บ๋ฆญํฐ๊ฐ ์๋๋ฅผ ๋ฐ๋ผ๋ณด๋ ์ํ๋ก ์์
private Vector2 lastMoveDirection = Vector2.down;
// ===================================================
// Unity Lifecycle
// ===================================================
private void Awake()
{
// Inspector์์ ํ ๋น๋์ง ์์์ ๊ฒฝ์ฐ GetComponent๋ก fallback
// [RequireComponent]๊ฐ ์์ผ๋ฏ๋ก null ๋ฐํ ์์์ด ๋ณด์ฅ๋จ
rb = rb != null ? rb : GetComponent<Rigidbody2D>();
animator = animator != null ? animator : GetComponent<Animator>();
// ํ๋ทฐ: ์ค๋ ฅ ๋ถํ์, ๋ฌผ๋ฆฌ ํ์ ๊ณ ์
rb.gravityScale = 0f;
rb.freezeRotation = true;
// ์
๋ ฅ ์์คํ
์ธ์คํด์ค ์์ฑ
input = new PlayerInputActions();
}
// ์ค๋ธ์ ํธ ํ์ฑํ ์ ์
๋ ฅ ํ์ฑํ
private void OnEnable()
{
input.Enable();
}
// ์ค๋ธ์ ํธ ๋นํ์ฑํ ์ ์
๋ ฅ ๋นํ์ฑํ (์ฌ ์ ํ, ์ผ์์ ์ง ๋ฑ ๋์)
private void OnDisable()
{
input.Disable();
}
private void Update()
{
// --- ์
๋ ฅ ์ฝ๊ธฐ ---
// New Input System: ReadValue๋ Polling ๋ฐฉ์์ผ๋ก ํ์ฌ ์
๋ ฅ ์ํ๋ฅผ ์ฆ์ ๋ฐํ
// WASD์ ํ์ดํํค๋ Input Action Asset์์ ๋์ผ Action์ ๋ฐ์ธ๋ฉ๋์ด ์์ผ๋ฏ๋ก
// ์ด๋ ํค๋ฅผ ๋๋ฌ๋ ๋์ผํ Vector2 ๊ฐ์ผ๋ก ๋ค์ด์ด
Vector2 rawInput = input.Player.Move.ReadValue<Vector2>();
// --- ๋๊ฐ์ ์๋ ๋ณด์ ---
// ์: W + D ๋์ ์
๋ ฅ ์ rawInput = (1, 1), sqrMagnitude = 2
// normalized ์์ด ์ด๋ํ๋ฉด ๋๊ฐ์ ๋ฐฉํฅ์ด ์ํ/์์ง๋ณด๋ค โ2 โ 1.41๋ฐฐ ๋น ๋ฆ
// sqrMagnitude > 1 ์กฐ๊ฑด: ๋จ์ผ ์ถ ์
๋ ฅ(ํฌ๊ธฐ = 1)์ ๋ถํ์ํ normalized ์ฐ์ฐ ์คํต
if (rawInput.sqrMagnitude > 1f)
rawInput = rawInput.normalized;
moveInput = rawInput;
// --- ๋ง์ง๋ง ์ด๋ ๋ฐฉํฅ ๊ฐฑ์ ---
// ์
๋ ฅ์ด ์์ ๋๋ง ๊ฐฑ์ โ ์ ์ง ์ ์ง์ ๋ฐฉํฅ์ ์ ์ง
// Animator์ idle ๋ธ๋ ๋ ํธ๋ฆฌ์์ ์ด๋ ๋ฐฉํฅ idle์ ์ฌ์ํ ์ง ๊ฒฐ์ ํ๋ ๋ฐ ์ฌ์ฉ
bool isMoving = moveInput.sqrMagnitude > 0f;
if (isMoving)
lastMoveDirection = moveInput;
UpdateAnimation(moveInput, isMoving);
}
private void FixedUpdate()
{
// MovePosition: Rigidbody2D์ sweep ๊ธฐ๋ฐ ์ด๋
// linearVelocity ์ง์ ํ ๋น๋ณด๋ค ์์ ์ถฉ๋์ฒด ํฐ๋๋ง์ ์์
// Time.fixedDeltaTime์ ๊ณฑํด ๋ฌผ๋ฆฌ ์คํ
๊ฐ๊ฒฉ์ ์ ํํ ๋์
Vector2 targetPosition = rb.position + moveInput * moveSpeed * Time.fixedDeltaTime;
rb.MovePosition(targetPosition);
}
// ===================================================
// Animation
// ===================================================
// Animator ํ๋ผ๋ฏธํฐ ์ผ๊ด ์
๋ฐ์ดํธ
// ๋ธ๋ ๋ ํธ๋ฆฌ ๊ตฌ์ฑ:
// moveX / moveY โ ์ด๋ ์ค ๋ฐฉํฅ๋ณ walk ์ ๋๋ฉ์ด์
// isMoving โ idle โ walk ์ ํ ํธ๋ฆฌ๊ฑฐ
// lastMoveX / lastMoveY โ ์ ์ง ์ ๋ง์ง๋ง ๋ฐฉํฅ์ idle ์ ๋๋ฉ์ด์
private void UpdateAnimation(Vector2 currentMove, bool isMoving)
{
animator.SetFloat(HashMoveX, currentMove.x);
animator.SetFloat(HashMoveY, currentMove.y);
animator.SetBool(HashIsMoving, isMoving);
animator.SetFloat(HashLastMoveX, lastMoveDirection.x);
animator.SetFloat(HashLastMoveY, lastMoveDirection.y);
}
// ===================================================
// Public API
// ===================================================
// ๋๋ฐฑ ๋ฑ ์ธ๋ถ์์ ์๊ฐ ํ์ ๊ฐํ ๋ ์ฌ์ฉ
// AddForce ํ moveInput์ ์ด๊ธฐํํ์ง ์์ผ๋ฏ๋ก
// ๋๋ฐฑ ์ค์๋ ํ๋ ์ด์ด ์
๋ ฅ์ด ์ฆ์ ์ฌ๊ฐ๋จ
public void AddImpulse(Vector2 force)
{
rb.AddForce(force, ForceMode2D.Impulse);
}
// ์ฝ๊ธฐ ์ ์ฉ ํ๋กํผํฐ โ ์ธ๋ถ ์์คํ
(์นด๋ฉ๋ผ, UI ๋ฑ)์์ ์ํ ์ฐธ์กฐ์ฉ
public Vector2 MoveInput => moveInput;
public Vector2 LastMoveDirection => lastMoveDirection;
public bool IsMoving => moveInput.sqrMagnitude > 0f;
}