๐Ÿ“”(2D) Player ์ปจํŠธ๋กค๋Ÿฌ ์ฝ”๋“œ

BamgasiJMยท2026๋…„ 4์›” 11์ผ

Unity GenArt

๋ชฉ๋ก ๋ณด๊ธฐ
9/41
post-thumbnail

How to Use

์ปจํŠธ๋กค๋Ÿฌ ์ฝ”๋“œ๋ฅผ Player์— ์ ์šฉํ•  ๋•Œ ํ‚ค๋ฐ”์ธ๋”ฉ์„ ์œ„ํ•œ Input Action Asset์„ ๋งŒ๋“ค์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

Input Action Asset "Wํ‚ค์™€ ์œ„์ชฝ ํ™”์‚ดํ‘œ๊ฐ€ Move๋‹ค" โ€” ํ‚ค๋ฐ”์ธ๋”ฉ ์„ค์ •
PlayerController2D.cs "Move ์ž…๋ ฅ๊ฐ’์œผ๋กœ ์บ๋ฆญํ„ฐ๋ฅผ ์›€์ง์ธ๋‹ค" โ€” ๋กœ์ง

1. Input Action Asset ์ƒ์„ฑ

Project ์ฐฝ ์šฐํด๋ฆญ โ†’ Create > Input Actions
ํŒŒ์ผ ์ด๋ฆ„์„ ์ •ํ™•ํžˆ PlayerInputActions ๋กœ ์ง€์ •

2. Asset ์—ด๊ธฐ

์ƒ์„ฑ๋œ PlayerInputActions ํŒŒ์ผ ๋”๋ธ”ํด๋ฆญ โ†’ Input Action ์—๋””ํ„ฐ ์˜คํ”ˆ

3. Action Map ์„ค์ •

์ขŒ์ธก Action Maps ํŒจ๋„์˜ ์ด๋ฆ„์„ Player ๋กœ ๋ณ€๊ฒฝ
(List is empty์˜ ๊ฒฝ์šฐ + ๋ˆŒ๋Ÿฌ์„œ ์ƒ์„ฑ)

4. Move Action ์„ค์ •

์ค‘์•™ Actions ํŒจ๋„์—์„œ ๊ธฐ๋ณธ ์ƒ์„ฑ๋œ Action ์ด๋ฆ„์„ Move ๋กœ ๋ณ€๊ฒฝ
์šฐ์ธก Properties์—์„œ

Action Type โ†’ Value
Control Type โ†’ Vector 2

5. ๋ฐ”์ธ๋”ฉ ์ถ”๊ฐ€ โ€” WASD

Move ์•„๋ž˜ + ํด๋ฆญ โ†’ Add Up/Dow/Left/Right Composite ์„ ํƒ โ†’ 2D Vector ํ•ญ๋ชฉ ์ƒ์„ฑ
๊ฐ ๋ฐฉํ–ฅ ํด๋ฆญ ํ›„ ์šฐ์ธก Path์—์„œ ๋ฐ”์ธ๋”ฉ: (Listen์„ ํด๋ฆญ ํ›„ ํ•ด๋‹น ํ‚ค ๋ˆ„๋ฅด๊ธฐ)

Up โ†’ W [Keyboard]
Down โ†’ S [Keyboard]
Left โ†’ A [Keyboard]
Right โ†’ D [Keyboard]

6. ๋ฐ”์ธ๋”ฉ ์ถ”๊ฐ€ โ€” ํ™”์‚ดํ‘œํ‚ค

Move ์•„๋ž˜ + ํด๋ฆญ โ†’ Add 2D Vector Composite ํ•œ ๋ฒˆ ๋” ์ถ”๊ฐ€

Up โ†’ Up Arrow [Keyboard]
Down โ†’ Down Arrow [Keyboard]
Left โ†’ Left Arrow [Keyboard]
Right โ†’ Right Arrow [Keyboard]

๋ฐ”์ธ๋”ฉ์ด ๋๋‚œ ํ›„ Save Asset ํด๋ฆญํ•˜์—ฌ ์„ค์ • ์ €์žฅ

7. C# ํด๋ž˜์Šค ์ž๋™ ์ƒ์„ฑ

Asset์—์„œ PlayerInputActions ์„ ํƒ โ†’ Inspector์—์„œ

  • Generate C# Class ํ•ญ๋ชฉ์— ์ฒดํฌ
  • C# Class File์˜ ๊ฒฝ๋กœ๋ฅผ ํ™•์ธ ํ›„ Apply ํด๋ฆญ

PlayerInputActions.cs ํŒŒ์ผ์ด ์ž๋™ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. PlayerController2D.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;
}

profile
Coding Art with Blender / oF / Processing / p5.js / nannou

0๊ฐœ์˜ ๋Œ“๊ธ€