기록 보관소

[Unity/유니티] 기초-탑다운 2D RPG: 쯔꾸르식 액션 구현하기[B21] 본문

유니티 프로젝트/탑다운 2D RPG

[Unity/유니티] 기초-탑다운 2D RPG: 쯔꾸르식 액션 구현하기[B21]

JongHoon 2022. 2. 17. 23:37

개요

유니티 입문과 독학을 위해서 아래 링크의 골드메탈님의 영상들을 보며 진행 상황 사진 또는 캡처를 올리고 배웠던 점을 요약해서 적는다.

현재는 영상들을 보고 따라하고 배우는 것에 집중할 것이며, 영상을 모두 보고 따라한 후에는 개인 프로젝트를 설계하고 직접 만드는 것이 목표다.

https://youtube.com/playlist?list=PLO-mt5Iu5TeYI4dbYwWP8JqZMC9iuUIW2 

 

유니티 강좌 기초 채널 Basic

유니티 개발을 처음 시작하시는 입문자 분들을 위한 기초 채널. [ 프로젝트 ] B00 ~ B12 (BE1) : 유니티 필수 기초 B13 ~ B19 (BE2) : 2D 플랫포머 B20 ~ B26 (BE3) : 2D 탑다운 대화형 RPG B27 ~ B37 (BE4) : 2D 종스크롤

www.youtube.com


탑다운 2D RPG: 쯔꾸르식 액션 구현하기[B21]

1. 플레이어 십자 이동

//PlayerAction 스크립트 파일

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerAction : MonoBehaviour {
    public float Speed;

    Rigidbody2D rigid;
    float h;
    float v;
    bool isHorizonMove;

	void Awake () {
        rigid = GetComponent<Rigidbody2D>();
	}
	
	void Update () {
        //이동 값 설정
        h = Input.GetAxisRaw("Horizontal");
        v = Input.GetAxisRaw("Vertical");

        //이동 버튼 확인
        bool hDown = Input.GetButtonDown("Horizontal");
        bool vDown = Input.GetButtonDown("Vertical");
        bool hUp = Input.GetButtonUp("Horizontal");
        bool vUp = Input.GetButtonUp("Vertical");

        //이동 방향 체크
        if (hDown || vUp)   //수평
            isHorizonMove = true;
        else if (vDown || hUp)  //수직
            isHorizonMove = false;
    }

    void FixedUpdate() {
        //수평, 수직 이동 결정
        Vector2 moveVec = isHorizonMove ? new Vector2(h, 0) : new Vector2(0, v);
        rigid.velocity = moveVec * Speed;
    }
}
  • 이전 글에서 대략적으로 만들어두었던 플레이어 이동은 일반적인 탑다운 2D RPG와는 달리 대각선 이동도 가능하다. 그래서 bool형 변수를 사용해서 입력받는 키에 따라 수직 또는 수평 이동만 가능하도록 했고, 만약 두 키가 동시에 눌러질 경우를 생각해서 GetButtonUp, 즉 반대 방향 버튼을 땠을 때의 경우에도 bool형 변수를 true 또는 false로 해준다.

2. 애니메이션 설정

Player_Down_Idle 애니메이션 생성
Player_Down_Walk 애니메이션 생성
Player_Left_Idle 애니메이션 생성
Player_Left_Walk 애니메이션 생성
Player_Right_Idle 애니메이션 생성
Player_Right_Walk 애니메이션 생성
Player_Up_Idle 애니메이션 생성
Player_Up_Walk 애니메이션 생성
애니메이션 생성 후의 Player의 Animator 창

  • 다수의 애니메이션을 만들고, 이제는 Animator 창을 사용해서 애니메이션 전환을 설정해주어야한다

Player의 Animator 창. 각 애니메이션의 Transition 작업을 해주었다.

  • 우선 방향 전환이 바로 되도록 Any State에 연결한다.
  • 수직, 수평값을 받을 Int 형 매개변수 hAxisRaw와 vAxisRaw를 생성해서 각 Transition의 Conditions에서 사용한다. 만약 각 방향에 따라서 hAxisRaw와 vAxisRaw의 값이 0과 크거나/작거나 같다고 설정하면 된다.
    • AnyState -> Player_Up_Walk : vAxisRaw Greater 0
    • Player_Up_Walk -> Player_Up_Idle : vAxisRaw Equals 0
    • AnyState -> Player_Down_Walk : vAxisRaw Less 0
    • Player_Down_Walk -> Player_Down_Idle : vAxisRaw Equals 0
    • AnyState -> Player_Right_Walk : hAxisRaw Greater 0
    • Player_Right_Walk -> Player_Right_Idle : hAxisRaw Equals 0
    • AnyState -> Player_Left_Walk : hAxisRaw Less 0
    • Player_Left_Walk -> Player_Left_Idle : hAxisRaw Equals 0

Settings의 Transition Duration을 0으로 설정

  • 추가로 이전에는 아래 애니메이션 전환 시간을 직접 조정했지만, 이는 경고를 발생시킬수도 있어서 그냥 간단하게 0을 입력하는 것이 좋다.
//PlayerAction 스크립트 파일

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerAction : MonoBehaviour {
    public float Speed;

    Rigidbody2D rigid;
    Animator anim;
    float h;
    float v;
    bool isHorizonMove;

	void Awake () {
        rigid = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
	}
	
	void Update () {
        //이동 값 설정
        h = Input.GetAxisRaw("Horizontal");
        v = Input.GetAxisRaw("Vertical");

        //이동 버튼 확인
        bool hDown = Input.GetButtonDown("Horizontal");
        bool vDown = Input.GetButtonDown("Vertical");
        bool hUp = Input.GetButtonUp("Horizontal");
        bool vUp = Input.GetButtonUp("Vertical");

        //이동 방향 체크
        if (hDown || vUp)   //수평
            isHorizonMove = true;
        else if (vDown || hUp)  //수직
            isHorizonMove = false;

        //애니메이션 전환
        anim.SetInteger("hAxisRaw", (int)h);
        anim.SetInteger("vAxisRaw", (int)v);
    }

    void FixedUpdate() {
        //수평, 수직 이동 결정
        Vector2 moveVec = isHorizonMove ? new Vector2(h, 0) : new Vector2(0, v);
        rigid.velocity = moveVec * Speed;
    }
}
  • 설정이 끝나면 PlayerAction 스크립트 파일에 애니메이터 코드를 추가하고 테스트 해본다.

상하좌우 Idle로 잘 바뀌지만, Walk 애니메이션이 작동하지 않는다.

  • Walk 애니메이션이 작동하지 않는 문제가 발생하는 이유는 매개변수 hAxisRaw와 vAxisRaw에 값이 계속해서 주어졌기 때문이다. 따라서 if문과 bool형 매개변수를 추가해서 해당 문제를 해결해야한다.

bool형 매개변수 isChange 추가
모든 Walk에서 isChange는 true일때로 설정

//PlayerAction 스크립트 파일

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerAction : MonoBehaviour {
    public float Speed;

    Rigidbody2D rigid;
    Animator anim;
    float h;
    float v;
    bool isHorizonMove;

	void Awake () {
        rigid = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
	}
	
	void Update () {
        //이동 값 설정
        h = Input.GetAxisRaw("Horizontal");
        v = Input.GetAxisRaw("Vertical");

        //이동 버튼 확인
        bool hDown = Input.GetButtonDown("Horizontal");
        bool vDown = Input.GetButtonDown("Vertical");
        bool hUp = Input.GetButtonUp("Horizontal");
        bool vUp = Input.GetButtonUp("Vertical");

        //이동 방향 체크
        if (hDown)   //수평
            isHorizonMove = true;
        else if (vDown)  //수직
            isHorizonMove = false;
        else if (hUp || vUp)
            isHorizonMove = h != 0;

        //애니메이션 전환
        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() {
        //수평, 수직 이동 결정
        Vector2 moveVec = isHorizonMove ? new Vector2(h, 0) : new Vector2(0, v);
        rigid.velocity = moveVec * Speed;
    }
}
  • AnyState -> Player_****_Walk 방향의 Transition들은 모두 isChange = true일때로 설정한다. 그리고 위 코드처럼 스크립트 파일을 수정한다.
  • 양쪽 버튼을 누른 상태로 하나만 때게되면 가만히 있는 상태로 걷는 문제가 발생해서 이동 방향 체크 항목도 조금 바뀌었다. 이전의 if문에서 hUp과 vUP을 따로 else if문으로 분리하고, 현재 AxisRaw 값에 따라 수평, 수직을 판단하여 해결하도록하였다.

이제 좌우 키가 동시에 눌려도 문제가 없고, Walk 애니메이션도 잘 작동한다.


3. 조사 액션

NPC Ludo를 생성하고 레이어를 Object로 설정
NPC Luna도 생성하고, 레이어를 Object로 설정한다.

  • 이전의 2D 플랫포머 게임처럼 플레이어를 중심으로한 Ray를 만들어조사/대화 등의 액션을 할 수 있도록 할 것이다.
  • 그래서 NPC외에도 박스나 테이블같은 조사 가능한 오브젝트의 레이어를 다르게 설정하여 플레이어의 Ray가 닿았을때 구분할 수 있도록 한다.
//PlayerAction 스크립트 파일

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerAction : MonoBehaviour {
    public float Speed;

    Rigidbody2D rigid;
    Animator anim;
    Vector3 dirVec;
    GameObject scanObject;
    float h;
    float v;
    bool isHorizonMove;

	void Awake () {
        rigid = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
	}
	
	void Update () {
        //이동 값 설정
        h = Input.GetAxisRaw("Horizontal");
        v = Input.GetAxisRaw("Vertical");

        //이동 버튼 확인
        bool hDown = Input.GetButtonDown("Horizontal");
        bool vDown = Input.GetButtonDown("Vertical");
        bool hUp = Input.GetButtonUp("Horizontal");
        bool vUp = Input.GetButtonUp("Vertical");

        //이동 방향 체크
        if (hDown || vUp)   //수평
            isHorizonMove = true;
        else if (vDown || hUp)  //수직
            isHorizonMove = false;
        else if (hUp || vUp)
            isHorizonMove = h != 0;

        //애니메이션 전환
        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);

        //레이 방향 판단하기
        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;

        //오브젝트 스캔 출력
        if (Input.GetButtonDown("Jump") && scanObject != null)
            Debug.Log("this is : " + scanObject.name);
    }

    void FixedUpdate() {
        //수평, 수직 이동 결정
        Vector2 moveVec = isHorizonMove ? new Vector2(h, 0) : new Vector2(0, v);
        rigid.velocity = moveVec * Speed;

        //레이 사용하기
        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;    //RayCast된 오브젝트를 변수로 저장
        }
        else
            scanObject = null;
    }
}

Ludo를 보고 스페이스바를 누르자 인식하고 출력했다
Luna를 보고 스페이스바를 누르니 마찬가지로 출력된다.
다른 물체를 보고 스페이스바를 눌려도 아무런 반응이 없다.

  • Ray를 사용해서 Layer가 "Object"인 것에만 반응하도록 설정하고, 그 값을 변수에 저장해서 Ray가 닿고 있을때 Jump, 즉 스페이스바를 누르면 콘솔창에 출력되도록 설정했다.