소소한 나의 하루들

2d 플랫포머(7) - 플레이어 피격 이벤트 구현하기 본문

개발/유니티

2d 플랫포머(7) - 플레이어 피격 이벤트 구현하기

소소한 나의 하루 2024. 1. 24. 19:04

출처: https://youtube.com/playlist?list=PLO-mt5Iu5TeYI4dbYwWP8JqZMC9iuUIW2&feature=shared

 

📚 유니티 기초 강좌

유니티 게임 개발을 배우고 싶은 분들을 위한 기초 강좌

www.youtube.com

Player가 몬스터한테, 혹은 함정 지형에 닿았을 때 피격 이벤트를 만들어본다.

지금 Player는 몬스터에게 닿아도 아무런 반응없이 밀려나기만 한다.이제 닿으면 피격되는 것으로 한다.


#1. 함정맵 추가

Project 창에서 가시(spike) sprite를 찾는다.

Tile Palette에다가 가시 sprite를 추가한다. (드래그로 타일추가할 수 있다)

그리고 이렇게 Scene에 가시를 Tilemap으로 추가해준다.

지금 지형 오브젝트가 있는 Tilemap에다가 가시를 깔아두는 것은 맞지않다. 왜냐하면 가시도 으로 간주해야 하기 때문이다.

따라서 추가했던 가시를 지우고, 다시 Hierarchy 창에 타일맵 오브젝트를 하나 더 추가한다.

물론 새로 추가한 타일맵 ‘Spikes’ 오브젝트도 Tilemap Collider 2D 컴포넌트를 추가해준다.

 

Spikes 오브젝트에 가시를 설치한다.

3d에서 충돌 이벤트에서 Tag를 사용했었다. 따라서 ‘누구와 충돌했느냐’를 인지하기 위해서는 Tag를 사용하는 것이 좋다.

 

현재 사용가능한 Tag의 목록을 보고, 적당한 것이 없으면 위처럼 새로 Enemy라는 태그를 추가한 다음 Spikes 오브젝트의 Tag를 Enemy로 설정한다.

그리고 레이어도 새로 Enemy라는 레이어를 생성해서, Spikes 오브젝트에 적용해준다.

: 태그와 레이어 설정

Enemy 오브젝트의 태그와 레이어도 모두 Enemy로 설정해준다.

 

Player 오브젝트는 ‘Player’와 ‘PlayerDamaged’라는 레이어 2개를 생성한다. (Player는 레이어 2개를 사용할 예정) *레이어의 ‘번호’만 사용할 예정이니까 순서는 달라도 된다.


#2. 레이어 설정

보통 게임에서 몬스터는 함정을 그냥 지나간다. 즉 Enemy끼리는 서로 물리충돌이 일어나도록 하고싶지 않다.

Edit 메뉴>Project Setting 창에서 Physics 2D를 본다.

아래쪽에 삼각 체크박스처럼 되어있는 것이 바로 물리 레이어이다. 여기서 충돌을 할 것인지 안할 것인지를 정할 수 있다.

 

여기서 Enemy와 Enemy끼리는 충돌하면 안된다는 의미로 체크를 해제한다.

이제 Enemy 오브젝트와 Tilemap의 Spikes 오브젝트 간 레이어 위치(어떤 것이 더 앞에 보이고, 뒤에서 가려질지)는 transition의 z축을 조절하거나 Order in Layer를 설정하면 된다. (z축이 -쪽으로 커지면 camera에 가까워짐)

 

Player와 PlayerDamaged 레이어를 생성했었는데, 이것의 의미는 무엇이냐면 플랫포머 게임을 할 때 함정이나 몬스터 등에 피격되면 약 1~2초 정도의 무적 시간이 주어진다. (피격 시 약간의 무적시간)

그것을 위해서 PlayerDamaged라는 레이어를 생성한 것이다.

따라서 PlayerDamaged와 Enemy의 체크박스도 해제한다. (무적효과를 위해 적 레이어와 충돌 해제)

이렇게 레이어 간 물리 충돌을 해제시켜주면, OnCollitionEnter도 동작하지 않는다.

이렇게 기본적으로 Player 오브젝트가 Enemy 레이어와 충돌한다고 두고, 스크립트를 짜본다.

 

충돌은 OnCollisionEnter을 활용해서 코드를 짠다. (지금은 2d이므로 2D를 사용)

if(collision.gameObject.tag == "Enemy")

지금 player와 충돌한 오브젝트의 tag가 Enemy라면


#3. 무적시간

이제 실제 해야할 로직을 만든다.

보통 기능이 담긴 함수는 따로 만든다.

 private void OnCollisionEnter2D(Collision2D collision)
 {
     if (collision.gameObject.tag == "Enemy")
         OnDamaged(collision.transform.position);
 }

 void OnDamaged(Vector2 targetPossition)
 {
     //Change Layer (Imortal Active)
     gameObject.layer = 12;

     //View Alpha
     spriteRenderer.material.color = new Color(1, 1, 1, 0.4f);

     //Reaction Force
     int dirc = transform.position.x - targetPossition.x > 0 ? 1 : -1;
     rigid.AddForce(new Vector2(dirc, 1) * 7, ForceMode2D.Impulse);
 }

맞았으니까 OnDamaged 함수를 만들고 호출한다.

.gameObjectgameObject.layer = 11;

자기자신의 레이어를 11번 레이어로 설정한다. (11번 레이어: PlayerDamaged)

한번 피격됐기 때문에, 무적이라는 것을 유저에게 보여줘야한다.

spriteRender를 사용해서 색을 바꾼다.

color함수를 사용한다. (red, green, blue, alpha) 4번째 변수는 '알파값(투명도)'이다.

 

그리고 한 대 맞았으니까, 튕겨나가는 것도 구현해놓는다.

보통 피격당한 위치에서 뒤쪽으로 살짝 튀어오르며 물러난다.

(부딪힌 목표물에서 멀어지는 방향으로 살짝 튀어오르며 물러남)

int direc = transform.position.x - targetPosition.x > 0 ? 1 : -1;

현재위치 x좌표 - 피격당한 x좌표 : 양수라면 1 음수라면 -1 양수 = 현재위치가 피격위치보다 오른쪽에 위치 → 오른쪽으로 튀어오름 음수 = 현재위치가 피격위치보다 왼쪽에 위치 → 왼쪽으로 튀어오름

실행해보면 한번 피격당할 때 뒤로 튕겨나며 투명하게 변한다.

그런데 한번 피격 당했기 때문에, 투명하게 변하면서 무적상태가 계속 지속된다.

따라서 무적모드는 약 1~2초정도 하고 다시 원래대로 풀어야할 것이다.


#4. 무적 해제

무적 해제 함수로 OffDamaged를 생성한다. player의 레이어를 다시 10(Player 레이어)으로 되돌리고, 투명도도 다시 1로 되돌려주면 된다.

그러면 OffDamaged를 OnDamaged 내에서 호출하면 될까?

아니다. 저렇게 해버리면 딜레이 시간없이 OnCollisoinEnter2D에서 OnDamaged를 호출하자마자 차례로 로직이 실행되고 바로 OffDamaged함수가 실행될 것이기 때문이다.

 

따라서 시간 차를 둬야하기 때문에 딜레이를 주는 함수 Invoke 함수를 활용한다.

Invoke함수로 무적 시간을 결정하면 된다.

 

Static은 체크를 해제해야한다.


#5. 애니메이션

지금 피격에 관한 애니메이션은 없기 때문에, 점프에 관한 애니메이션으로 대체한다. (마지막 2개 프레임 선택하여 Player 오브젝트에 적용 후 Player_Damaged 애니메이션 생성)

Animation 창을 연 후, Player_Damaged 애니메이션을 클릭한다.

첫번째 프레임을 삭제하고, 두번째 프레임을 복사해서 0.3초 간격으로 위치시킨다. (Alt+마우스 휠: 타임라인 줌인/줌아웃)

 

Animator 창에서 Any State와 Exit는 아직 안써봤다. 이것들이 어떤 것인지 알아보도록 한다.

그리고 Parameters 창에서 이번에는 Trigger를 추가해본다. (DoDamaged로 이름설정)

Trigger는 방아쇠 역할의 매개변수인데, 값이 없다는 것이 특징이다.

그래서 Any State에서 Player_Damaged로 이동하려면, DoDamaged라는 트리거를 땡기면 된다.

Any State에서 Player_Damaged 애니메이션으로 향하는 Transition 화살표에 DoDamaged라는 트리거를 추가한다.

 

그렇다면 Player_Damaged에서 Exit로 나가는 것은?

그대로 두면 된다. 그런데 Has Exit Time을 사용해야한다. 이 시간은 아까 Player_Damaged의 시간을 0.417초로 설정했었는데, 이 시간을 활용한다.

Has Exit Time을 사용하기 때문도 있고, 보통 피격 애니메이션은 반복재생되면 안되니까 Loop Time을 체크 해제한다.

“반복재생하지 않고, 한번만 4.17초동안 재생되다가 알아서 나갈거야”

따라서 Any State에서 Player_Damaged로 들어가는 순간에만 방아쇠를 당겨주면 된다.

이제 스크립트에 작성해준다.

 

Parameter 앞에다가 Set만 붙이면 된다.

SetTrigger 함수를 사용한다. 그리고 대소문자 주의해서 입력한다.

Any State는 어떠한 애니메이션이 재생되고있는, 어떠한 상황에 상관없이 Condition만 맞으면 그냥 바로 실행 후 복귀하는 것이다.

AnyState→Exit : 현재 상태 상관없이 실행 후 원래대로(Entry) 복귀한다

어떠한 상태에서도 발생할 수 있는 애니메이션이라면, Any State와 Exit를 활용하면 된다.

Comments