유니티 일기

탑다운 2D RPG - 쯔꾸르식 액션 구현하기

mky 2025. 8. 28. 18:34

골드메탈님의 유튜브 강의를 보고 배운 것을 정리하였습니다.

1. 플레이어 십자 이동

using UnityEngine;

public class PlayerAction : MonoBehaviour
{
    public float moveSpeed;
    float h;
    float v;
    bool isHorizonMove;

    Rigidbody2D rigid;

    void Awake()
    {
        rigid = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        // Move Value
        h = Input.GetAxisRaw("Horizontal");
        v = Input.GetAxisRaw("Vertical");

        // Check Button Down or Up
        bool hDown = Input.GetButtonDown("Horizontal");
        bool vDown = Input.GetButtonDown("Vertical");
        bool hUp = Input.GetButtonUp("Horizontal");
        bool vUp = Input.GetButtonUp("Vertical");

        // Check Horizontal Move
        if (hDown || vUp)
            isHorizonMove = true;
        else if (vDown || hUp)
            isHorizonMove = false;
    }

    void FixedUpdate()
    {
        //Move
        Vector2 moveVec = isHorizonMove ? new Vector2(h, 0) : new Vector2(0, v); // 대각선 이동 방지
        rigid.linearVelocity = moveVec * moveSpeed;
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.name == "BorderLine")
        {
            Debug.Log("범위 밖으로 나갈 수 없습니다.");
        }

    }
}

 

2. 애니메이션

- 방향 전환이 바로 되도록 Any State 사용

- Settings에서 TransitionvDuration = 0으로 하면 깔끔

- 하나의 방향에 서있기, 걷기 두 개 State 구성

- Transition을 연속적으로 태우면 애니메이션이 작동되지 않음 -> Transition을 한번만 주기

- 방향 변화 매개변수를 추가하여 한번만 실행되도록 변경

- 양쪽 버튼 누른 상태로 하나만 버튼 업이면 문제 발생 -> 현재 AxisRaw 값에 따라 수평, 수직 판단하여 해결!

- Any State의 Can transition to self : 자기 자신으로의 트랜지션을 허용하는 옵션

  - 애니메이션의 첫 프레임만 계속 보이는 문제점 발견 -> 해당 옵션 체크 해제

using UnityEngine;

public class PlayerAction : MonoBehaviour
{
    public float moveSpeed;
    float h;
    float v;
    bool isHorizonMove;

    Rigidbody2D rigid;
    Animator anim;

    void Awake()
    {
        rigid = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
    }

    void Update()
    {
        // Move Value
        h = Input.GetAxisRaw("Horizontal");
        v = Input.GetAxisRaw("Vertical");

        // Check Button Down or Up
        bool hDown = Input.GetButtonDown("Horizontal");
        bool vDown = Input.GetButtonDown("Vertical");
        bool hUp = Input.GetButtonUp("Horizontal");
        bool vUp = Input.GetButtonUp("Vertical");

        // Check Horizontal Move
        if (hDown)
            isHorizonMove = true;
        else if (vDown)
            isHorizonMove = false;
        else if (hUp || vUp)
            isHorizonMove = h != 0;

        // Animation
        if (anim.GetInteger("hAxisRaw") != h)
        {
            anim.SetBool("isChange", true);
            anim.SetInteger("hAxisRaw", (int)h);
        }
        else if (anim.GetInteger("vAxisRaw") != v)
        {
            anim.SetBool("isChange", true);
            anim.SetInteger("vAxisRaw", (int)v);
        }
        else
            anim.SetBool("isChange", false);
    }

    void FixedUpdate()
    {
        //Move
        Vector2 moveVec = isHorizonMove ? new Vector2(h, 0) : new Vector2(0, v); // 대각선 이동 방지
        rigid.linearVelocity = moveVec * moveSpeed;
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.name == "BorderLine")
        {
            Debug.Log("범위 밖으로 나갈 수 없습니다.");
        }

    }
}

 

3. 조사 액션

- 현재 바라보고 있는 방향 값을 가진 변수(벡터)가 필요

- Debug.DrawRay() : 디버깅용으로 장면(Scene)에 선을 그려주는 메서드, 실제 게임화면에는 표시x

  - 매개변수

    - Vector3 start : 시작 위치 (월드 좌표)

    - Vector3 dir : 방향과 길이 (벡터)

    - Color color : 선의 색상 (기본값: 흰색)

    - float duration : 몇 초 동안 그릴지 (기본값: 0초 → 한 프레임만 표시됨)

    - bool depthTest = true : 다른 오브젝트 뒤에 가려질지 여부 (true면 가려짐)

- DrawRay로 미리 보고 RayCast를 구현하면 쉬움

//Ray
Debug.DrawRay(rigid.position, dirVec*0.7f, new Color(0, 1, 0));
RaycastHit2D rayHit = Physics2D.Raycast(rigid.position, dirVec, 0.7f, LayerMask.GetMask("Object"));

- RaycastHit2D : 2D 물리 광선(Raycast)**을 쏘았을 때, 무언가에 맞았다면 그 정보를 담는 구조체

- Physics2D.Raycast : 2D 물리 광선(Raycast)**을 쏘는 함수. 원하는 레이어의 오브젝트만 검사

- 조사 가능한 오브젝트를 다른 Layer로 설정

- RayCast된 오브젝트를 변수로 저장하여 활용

using UnityEngine;

public class PlayerAction : MonoBehaviour
{
    public float moveSpeed;
    float h;
    float v;
    bool isHorizonMove;
    Vector3 dirVec; // 현재 바라보고 있는 방향 값을 가진 변수가 필요
    GameObject scanObject; // 상호작용 중인 오브젝트

    Rigidbody2D rigid;
    Animator anim;

    void Awake()
    {
        rigid = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
    }

    void Update()
    {
        // Move Value
        h = Input.GetAxisRaw("Horizontal");
        v = Input.GetAxisRaw("Vertical");

        // Check Button Down or Up
        bool hDown = Input.GetButtonDown("Horizontal");
        bool vDown = Input.GetButtonDown("Vertical");
        bool hUp = Input.GetButtonUp("Horizontal");
        bool vUp = Input.GetButtonUp("Vertical");

        // Check Horizontal Move
        if (hDown) // 만약 수평버튼을 눌렀다면
            isHorizonMove = true;
        else if (vDown) // 만약 수직버튼을 눌렀다면
            isHorizonMove = false;
        else if (hUp || vUp) // 수평버튼이나 수직버튼을 떼어도 여전히 수평 방향으로 입력이 들어오고 있다면 수평 이동 중이라고 판단하는 로직
            isHorizonMove = h != 0;
        /*if (h != 0)
            isHorizonMove = true;
        else
            isHorizonMove = false;*/

        // Animation
        if (anim.GetInteger("hAxisRaw") != h) // 현재 애니메이터에 저장된 수평 값(hAxisRaw)과 실제 입력값 h가 다르면
        {
            anim.SetBool("isChange", true); // 애니메이션 상태 변경
            anim.SetInteger("hAxisRaw", (int)h); // 애니메이터에 수평 값 업데이트
        }
        else if (anim.GetInteger("vAxisRaw") != v)
        {
            anim.SetBool("isChange", true);
            anim.SetInteger("vAxisRaw", (int)v);
        }
        else
            anim.SetBool("isChange", false); // 바뀌지 않았음

        //Direction
        if (vDown && v==1)
            dirVec = Vector3.up;
        else if (vDown && v == -1)
            dirVec = Vector3.down;
        else if (hDown && h == -1)
            dirVec = Vector3.left;
        else if (hDown && h == 1)
            dirVec = Vector3.right;

        // Scan Object
        if(Input.GetButtonDown("Jump") && scanObject != null) 
            Debug.Log("this is : " + scanObject.name);

    }

    void FixedUpdate()
    {
        //Move
        Vector2 moveVec = isHorizonMove ? new Vector2(h, 0) : new Vector2(0, v); // 대각선 이동 방지
        rigid.linearVelocity = moveVec * moveSpeed;

        //Ray
        Debug.DrawRay(rigid.position, dirVec*0.7f, new Color(0, 1, 0));
        RaycastHit2D rayHit = Physics2D.Raycast(rigid.position, dirVec, 0.7f, LayerMask.GetMask("Object"));

        if(rayHit.collider != null)
            scanObject = rayHit.collider.gameObject;
        else
            scanObject = null;
        
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.name == "BorderLine")
        {
            Debug.Log("범위 밖으로 나갈 수 없습니다.");
        }

    }
}