유니티 게임 개발/Jumping Rabbit

[유니티] Jumping Rabbit #2 Platform Effector 2D, 점프 세분화 및 확장

노랑꼬리 2022. 11. 6. 21:20

※ 구현 목표

Platform Effector 2D 를 사용한 발판 제작, 애니메이션 세분화, 점프 중에만 방향 조절 가능

 

 

1. Platform Effector 2D

 

플랫폼 이펙터는 단방향 충돌, 측면 마찰, 바운스 제거 등 플랫폼 게임에서 자주 사용되는 기능을

유니티에서 컴포넌트로 제공해주는 것이다.

 

이번에는 여기서 단방향 충돌로 올라갈 때는 충돌하지 않고 캐릭터가 딛고 있을 때만 충돌 작용을 적용하려 한다.

 

Use Collider Mask : 특정 콜라이더만 충돌판정을 할지 여부, 비활성화시 모든 콜라이더와 충돌

 ㄴ Collider Mask : 충돌 판정을 할 콜라이더 지정

Rotational Offset : 충돌 판정의 방향 지정 (ex. 단방향 충돌일 경우 0이면 위쪽만 충돌, 180이면 아랫쪽만 충돌 판정)

Use One Way : 콜라이더가 단방향 충돌 할지 여부

 ㄴ Use One Way Grouping : 오브젝트에 여러 콜라이더가 있다면 하나의 콜라이더처럼 인식하고 처리하게 할 지 여부

 ㄴ Surface Arc : 단방향 충돌 판정 범위 각도

Use Side Friction : 측면에 마찰력 사용 여부

Use Side Bounce : 측면에 반발력 사용 여부

Side Arc : 측면 판정 범위 각도

 

 

이번에 사용할 것은 기본적인 플랫폼 발판이므로 초기 세팅 (위 사진)만 적용한다.

 

적용 결과

 

2. 애니메이션 세분화

 

기존 Idel 과 Jump 만 존재하던 애니메이션을 기본 점프, 롱 점프 두가지로 바꾸었다.

이 때 롱 점프는 점프 게이지 모으는 모션 (무릎굽힘) / 올라갈 때 / 떨어질 때 세가지로 분할 하였다.

 

이에 맞게 파라미터를 추가하였다.

isJump : 공중 유무

readyToJump : 게이지를 축적 유무

isDown : 추락 유무

 

 

 

3. 점프 기능 확장 구현

※ 토끼 점프를 구현하기 위해 걸어서 이동 불가, 점프시 해당 방향으로만 이동 가능 형태로 구현하고자 한다.

 

1) 지상에서 방향 전환

 

private SpriteRenderer rend;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  void groundFlip()
  {
    // 지상에 있을 때
    if (!anim.GetBool("isJump"))
    {
      if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
      {
        rend.flipX = true;
        Flip = true;
      }
      if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
      {
        rend.flipX = false;
        Flip = false;
      }
    }
  }
cs

지상에 있을 때만 좌우반전이 가능하도록 하였다.

또한 바라보는 방향으로만 앞으로 갈 수 있도록 Flip 변수를 저장해둔다.

 

 

2) 공중에서 바라보는 방향으로 이동 가능

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  void Move()
  {
    if (anim.GetBool("isJump"))
    {
      if (Flip == true && ((Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))))
      {
        rbody.AddForce(Vector3.left * 0.4f, ForceMode2D.Force);
      }
      else if (Flip == false && ((Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))))
      {
        rbody.AddForce(Vector3.right * 0.4f, ForceMode2D.Force);
      }
    }
  }
cs

※ ForceMode2D

 

ForceMode2D.Force : 질량을 사용하여 힘을 준다. 즉, 기본적인 물리법칙에서 힘의 추가이다. (ex. 물체를 밀때 힘)

ForceMode2D.Impulse : 질량을 사용하여 순간적인 충격을 준다. 폭발적인 힘을 나타낼 때 사용한다. 현재 프로젝트에서는 점프에서 사용되었다.

 

 

4. 전체 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Player : MonoBehaviour
{
  private Rigidbody2D rbody;
 
  // 점프 파워 계수
  public float JumpPower = 2;
  // 최대 점프 게이지
  private float JumpTimeLimit = 3.0f;
  // 점프 게이지 (최소 1.0f)
  private float JumpTime = 1.0f;
 
  private SpriteRenderer rend;
  public bool Flip;
  Animator anim;
 
 
  void Start()
  {
    rbody = GetComponent<Rigidbody2D>();
    rbody.constraints = RigidbodyConstraints2D.FreezeRotation;
    rend = GetComponentInChildren<SpriteRenderer>();
    anim = GetComponent<Animator>();
  }
 
 
  void Update()
  {
    Jump();
    groundFlip();
    Move();
  }
 
  void FixedUpdate()
  {
    JumpGauge();
    RayDetect();
  }
 
  // 점프 입력
  void Jump()
  {
    // 점프 키를 떼면 점프 실행
    if (Input.GetKeyUp(KeyCode.Space) && !anim.GetBool("isJump"))
    {
      anim.SetBool("isDown"false);
      anim.SetBool("readyToJump"false);
      // 점프 계산식 = JumpPower(점프 계수) * JumpTime(점프 게이지(시간비례))
      rbody.AddForce(Vector3.up * JumpPower * JumpTime, ForceMode2D.Impulse);
      anim.SetBool("isJump"true);
      // 최소 점프 수치
      JumpTime = 1.0f;
    }
  }
 
  void groundFlip()
  {
    // 지상에 있을 때
    if (!anim.GetBool("isJump"))
    {
      if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
      {
        rend.flipX = true;
        Flip = true;
      }
      if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
      {
        rend.flipX = false;
        Flip = false;
      }
    }
  }
 
  // 공중에 있을 때
  void Move()
  {
    if (anim.GetBool("isJump"))
    {
      if (Flip == true && ((Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))))
      {
        rbody.AddForce(Vector3.left * 0.4f, ForceMode2D.Force);
      }
      else if (Flip == false && ((Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))))
      {
        rbody.AddForce(Vector3.right * 0.4f, ForceMode2D.Force);
      }
    }
  }
 
  // 점프 게이지 모으기
  void JumpGauge()
  {
    // 점프 키를 입력할 때 점프 중이 아니라면 (바닥에 있다면)
    if (Input.GetKey(KeyCode.Space) && !anim.GetBool("isJump"))
    {
      // 점프 게이지 증가 (최대치 존재)
      if (JumpTimeLimit > JumpTime)
        JumpTime += 0.1f;
    }
 
    if (JumpTime > 1.1f)
    {
      anim.SetBool("readyToJump"true);
    }
  }
 
  // Raycast로 바닥 탐지
  void RayDetect()
  {
    if (rbody.velocity.y < 0)
    {
      anim.SetBool("isDown"true);
      // Ray 그리기
      Debug.DrawRay(rbody.position, Vector3.down, new Color(010));
      // 아래로 Ray 쏘기, Layer가 "Platform"인 경우만 탐지
      RaycastHit2D rayHit = Physics2D.Raycast(rbody.position, Vector3.down, 1, LayerMask.GetMask("Platform"));
 
      // 충돌된 경우
      if (rayHit.collider != null)
      {
        // 적중된 개체가 레이길이의 0.5보다 작은 경우 
        if (rayHit.distance < 0.5f)
        {
          anim.SetBool("isJump"false);
        }
      }
    }
  }
}
 
cs