유니티 2D 캐릭터 충돌 - yuniti 2D kaeligteo chungdol

<전체 소스코드>

using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerMove : MonoBehaviour{ Rigidbody2D rigid; SpriteRenderer spriteRenderer; Animator anim; public float maxSpeed; public float jumpPower; public int jumpCount = 0; public int maxJumpCount = 2; // 방향을 가져오기 위한 변수 Vector3 dirVec; // 레이로 스캔하는 물체 받아오는 변수 GameObject scanObject; void Awake() { rigid = GetComponent<Rigidbody2D>(); spriteRenderer = GetComponent<SpriteRenderer>(); anim = GetComponent<Animator>(); } void Update(){ // 버튼에서 손을 떼면 움직임 멈춤 if(Input.GetButtonUp("Horizontal")){ rigid.velocity = new Vector2(rigid.velocity.normalized.x * 0.2f, rigid.velocity.y); } // Flip if (Input.GetButton("Horizontal")) spriteRenderer.flipX = Input.GetAxisRaw("Horizontal") == 1; // 걷는 애니메이션 if (Mathf.Abs(rigid.velocity.x) < 0.2f || jumpCount > 0) //속도가 0이라면 = 단위벡터가 0 anim.SetBool("isWalk", false); else anim.SetBool("isWalk", true); //바라보는 방향 float h = Input.GetAxisRaw("Horizontal"); if(h == -1) dirVec = Vector3.left; else if(h == 1) dirVec = Vector3.right; // 점프 if(Input.GetKeyDown(KeyCode.Space) && jumpCount < maxJumpCount){ rigid.velocity = Vector2.up * jumpPower; jumpCount++; } } void FixedUpdate(){ // 좌우 이동 float h = Input.GetAxisRaw("Horizontal"); rigid.AddForce(Vector2.right * h, ForceMode2D.Impulse); // 최대 스피드 maxSpeed 를 넘지 못하게 함 if(rigid.velocity.x > maxSpeed) rigid.velocity = new Vector2(maxSpeed, rigid.velocity.y); else if(rigid.velocity.x < -maxSpeed) rigid.velocity = new Vector2(-maxSpeed, rigid.velocity.y); // 플레이어 아래로 쏘는 ray, 내려올때만 if(rigid.velocity.y < 0){ Debug.DrawRay(rigid.position, Vector3.down, new Color(1, 0, 0)); RaycastHit2D downRay = Physics2D.Raycast(rigid.position, Vector3.down, 1, LayerMask.GetMask("Ground")); if(downRay.collider != null && downRay.distance < 0.5f){ Debug.Log("땅"); jumpCount = 0; } } // 조사를 하기 위한 Ray // 씬 상에서 빨간색 선을 쏴서 레이에 닿으면 상호작용 가능하게 개조하면 됨 Debug.DrawRay(rigid.position, dirVec * 1.5f, new Color(1, 0, 0)); RaycastHit2D rayHit2 = Physics2D.Raycast(rigid.position, dirVec, 1.5f, LayerMask.GetMask("Object")); if(rayHit2.collider != null) { scanObject = rayHit2.collider.gameObject; } else { scanObject = null; } } }

<FixedUpdate와 Update 차이>

   - Update : 프레임 단위로 호출되는 함수, 단발적인 키 입력을 받기 좋음

   - FixedUpdate : Update와 달리 일정한 시간으로 호출되는 함수, 지속적인 키 입력을 받기 좋음

 업데이트 함수는 프레임 단위로 호출되기 때문에 물리 엔진과 다른 시간을 가진다고 합니다. 그래서 물리 충돌과 관련한 기능들은 FixedUpdate에서 구현해야 한다고 합니다.

<변수 설명>

Rigidbody2D rigid; SpriteRenderer spriteRenderer; Animator anim;

 위 3개는 각각 플레이어의 리지드바디, 스프라이트랜더러, 애니메이션 컨트롤러를 가져오기 위한 변수로 Awake 함수에서 초기화 되었습니다.

public float maxSpeed; public float jumpPower; public int jumpCount = 0; public int maxJumpCount = 2;

 maxSpeed의 경우 플레이어의 최대 이동 속도를 의미하며 jumpPower은 AddForce에서 Vector2.up에 곱해지게 됩니다.

 jumpCount와 maxJumpCount는 각각 현재 플레이어의 점프 횟수와 최대 횟수를 의미합니다. maxJumpCount 변수 조작으로 점프 횟수를 조정할 수 있으며 이후 아이템 등을 통해서 늘어날 여지가 있게 public으로 선언했습니다.

// 방향을 가져오기 위한 변수 Vector3 dirVec; // 레이로 스캔하는 물체 받아오는 변수 GameObject scanObject;

 위 두 변수는 플레이어 앞에 달려 있는 Ray의 방향을 정해주기 위해 있으며 dirVec에 방향이 달리게 되며 scanObject에는 Ray에 닿은 물체를 가져오게 됩니다.

<플레이어 이동>

1. 좌 우 이동

void Update(){ // 버튼에서 손을 떼면 움직임 멈춤 if(Input.GetButtonUp("Horizontal")){ rigid.velocity = new Vector2(rigid.velocity.normalized.x * 0.2f, rigid.velocity.y); } } void FixedUpdate(){ // 좌우 이동 float h = Input.GetAxisRaw("Horizontal"); rigid.AddForce(Vector2.right * h, ForceMode2D.Impulse); // 최대 스피드 maxSpeed 를 넘지 못하게 함 if(rigid.velocity.x > maxSpeed) rigid.velocity = new Vector2(maxSpeed, rigid.velocity.y); else if(rigid.velocity.x < -maxSpeed) rigid.velocity = new Vector2(-maxSpeed, rigid.velocity.y); }

 기본적으로 FixedUpdate에서 키보드 입력을 받습니다. 유니티 프로젝트 설정에 있는 Horizontal을 이용해 해당하는 키 (방향키, AD)를 입력받으면 rigid.AddForce를 통해 오른쪽으로 힘을 가해줍니다. 왼쪽의 경우 변수 h가 음수가 되어서 왼쪽으로 이동하게 됩니다.

 일단 저 AddForce의 경우 물체에 계속해서 힘을 가하는것이라 속도가 점점 빨라지게 됩니다. 때문에 아래 if문을 통해 maxSpeed란 변수보다 빠를 수 없게 제어를 해 줍니다.

 Update에 있는 코드는 키보드에서 손을 떼는 순간 속도를 급격하게 줄여 마찰과 공기저항 등을 통해 플레이어가 빠르게 정지할 수 있도록 해주는 코드입니다.

2. 점프

void Update(){ // 점프 if(Input.GetKeyDown(KeyCode.Space) && jumpCount < maxJumpCount){ rigid.velocity = Vector2.up * jumpPower; jumpCount++; } } void FixedUpdate(){ // 플레이어 아래로 쏘는 ray, 내려올때만 if(rigid.velocity.y < 0){ Debug.DrawRay(rigid.position, Vector3.down, new Color(1, 0, 0)); RaycastHit2D downRay = Physics2D.Raycast(rigid.position, Vector3.down, 1, LayerMask.GetMask("Ground")); if(downRay.collider != null && downRay.distance < 0.5f){ Debug.Log("땅"); jumpCount = 0; } } }

 좌우 이동의 경우 키보드를 지속적으로 누르는 반면 범프의 경우 단발적으로 누르는 이벤트라 Update에서 키 입력을 받는다고 합니다. (from. 유튜브) 점프를 했을 시 플레이어를 위로 띄우게 되고 이 때 jumpPower만큼 힘을 가하게 됩니다.

 점프 횟수의 경우 maxJumpCount로 컨트롤하고 있는데 키 입력 점프시 jumpCount 변수를 1씩 증가시키고 키 입력에서 jumpCount < maxJumpCount인지 조건을 넣어줬습니다. 프로젝트에서는 maxJumpCount를 2로 두어서 2단 점프까지 할 수 있습니다.

jumpCount는 플레이어가 땅에 닿으면 0으로 초기화가 되며 플레이어가 땅에 닿는건 RaycastHit2D를 통해서, 이 Ray는 플레이어가 공중에서 떨어질때만 나오게 됩니다.

+ 점프 관련 개선점

    - 지금의 Ray는 단일 ray로 플레이어 중심에서 내려오게 됩니다. 때문에 플랫폼 끝에 걸치는 경우 점프 초기화가 안되는 경우가 예상됩니다.

<애니메이션 전환>

 애니메이션의 경우 스프라이트의 애니메이션이 다양하지 않아 좌우 전환, 달리기 모션만 처리했습니다.

1. Flip

void Update(){ // Flip if (Input.GetButton("Horizontal")) spriteRenderer.flipX = Input.GetAxisRaw("Horizontal") == 1; //바라보는 방향 float h = Input.GetAxisRaw("Horizontal"); if(h == -1) dirVec = Vector3.left; else if(h == 1) dirVec = Vector3.right; }

 SpriteRenderer를 통해서 좌우 회전을 하게 되는데 인스펙터창의 Flip X가 눌려진게 true (==1) 눌리지 않은게 false (==0) 현재 스크립트가 담긴 스프라이트들에 따라 1로 만들지 -1로 만들지 살펴봐야 합니다.

 아래 바라보는 방향의 경우 좌, 우 조사를 하기 위한 Ray가 플레이어 방향을 바라볼 수 있게끔 dirVec 변수에 담아주는 과정이며 dirVec는 전역변수입니다.

2. 애니메이션 전환

void Update(){ // 걷는 애니메이션 if (Mathf.Abs(rigid.velocity.x) < 0.2f || jumpCount > 0) //속도가 0이라면 = 단위벡터가 0 anim.SetBool("isWalk", false); else anim.SetBool("isWalk", true); }

 애니메이션의 경우 idle, walk 2가지를 준비했으며 isWalk 변수가 true가 되면 걷는 애니메이션으로 변경되게 했습니다.

 속도가 0이거나 jumpCount가 양수 (점프를 한 상태) 일 경우 isWalk는 false가 되어 idle 모션이 되고 외의 경우에는 걷는 모션이 됩니다.

<벽과의 충돌처리>

 벽의 경우 material을 추가해 마찰을 0으로 만들어 플레이어가 매달릴 수 없게 만들었습니다.

Toplist

최신 우편물

태그