탑다운 2D RPG - 쯔꾸르식 액션 구현하기
골드메탈님의 유튜브 강의를 보고 배운 것을 정리하였습니다.
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("범위 밖으로 나갈 수 없습니다.");
}
}
}