유니티 3D 넉백 - yuniti 3D neogbaeg


유니티2D

피격처리 프로젝트 파일

2020. 4. 18. 20:11

Hurt.unitypackage

0.07MB

유니티 3D 넉백 - yuniti 3D neogbaeg
프로젝트를 임포트 후에 인스펙터에서 변수의 값을 초기화하고 실행해보세요!

안녕하세요 곰돌입니다!

이번 프로젝트는 피격처리에 관하여입니다.

피격되었을 때 넉백이 일어나면서 캐릭터를 일시적으로 무적상태로 만들며

피격 사운드까지 제어하는 것을 다룹니다.

요새 일이 생겨서 자주 못올리지만 그래도 최대한 꾸준히 올릴 수 있게 노력해 보겠습니다!

그럼 조금이라도 도움이 되었으면 좋겠네요.

영상에 사용된 에셋들은 전부 저작권이 자유로운 것들로 만들어졌습니다.

유니티 3D 넉백 - yuniti 3D neogbaeg


어제 하루 쉬었다. 🤬🤬 

그렇다 그냥 쉬었다. 😉😉 대신 요즘에 클린 코드라는 책을 읽고 있는데, 깨끗하게 코드를 작성해야 유지보수 측면에서도 유리하고, 경쟁력이 있는 개발자가 될 수 있다는 것을 학교에서도 배웠고, 또한 교수님께서 종강하기전에 꼭 읽어보라며 추천해주신 책이여서 며칠전에 구입해서 처음으로 읽어보았다.

아직 초반부지만 저자도 엄청난 고급 기술이 아닌 코딩을 하면서 쉽게 놓칠 수 있는 부분들에 대해서 다시 짚어볼 수 있는 그런 책이라고 적혀있었고, 실제로 읽어보니까 누구나 다 알법한 내용들인데 진짜 코딩에 목메어서 쉽게 간과될 수 있는 부분들을 적어놓았다.

실제로도 학교 프로젝트로 웹 사이트를 구축해보았지만 많은 사람들이 각자만의 스타일대로 코딩을 하고, 그것을 머지해놓으면 나중에 버그를 고칠 때 너무나 큰 어려움이 있었고, 그렇기 때문에 시간을 들여서 내가 주석을 일일히 달았던 적도 있었다.

그 결과 버그를 수정할 때 내 주석을 보고 많은 부분 쉽게 고칠 수 있었다는 피드백을 받았던 적도 존재하고 코드도 돌아간다고 좋아할 것이 아니라 정리가 필요하다는 것을 그제서야 깨달았다. (그 전에는 나 혼자서 코딩하니까 남의 코드를 이해할 필요가 없었다.).

아무튼 이런 내용으로 깨끗한 코드를 작성하는 방식을 알려주는 책이다.

각설하고 유니티로 다시 돌아가도록 하자.


10. 적😈 공격하기


우선 미리 만들어 두었던 적 NPC 스프라이트를 가지고 진행하도록 하겠다.

스크립트를 작성하기 전에 먼저 적 NPC 스프라이트에 Tag를 설정해주어야 한다. Add Tag를 통해서 설정이 안되어있다면 Enermy로 하나 만들어서 설정해준다.

유니티 3D 넉백 - yuniti 3D neogbaeg
Enermy 스프라이트의 태그를 설정해준다.

따라서 이렇게하고 이전에 Enermy 스프라이트에 넣어두었던 스크립트 파일을 연다. (적 AI 만들 때 사용했던 스크립트 파일.) 그리고 플레이어 스크립트 파일도 연다.

그리고는 간단하게 로직을 생각해본다. 우리는 캐릭터가 적 NPC 위에서 점프로 머리(?)를 밟았을 때 해당 캐릭터가 적 NPC를 공격했다고 간주하고 적 NPC를 사라지게 할 예정이다. 그렇게하기 위해서는 다음과 같다.

🟡 플레이어 캐릭터가 Y의 값이 0보다 작아야 한다. (위에서 아래로 떨어지는 거니까, 점프의 반대)

🟡 적 NPC의 Y값보다 플레이어 캐릭터의 Y값이 더 커야 된다. (그래야 위에서 밟는 행위가 가능하기 때문이다.)

따라서 스프라이트와 스프라이트가 부딪혔다는 것은 Collider의 충돌로 판단한다. 그래서 우선 플레이어 스크립트에 

OnColliderOnCollisionEnter2D 메서드를 만들어준다. 그리고나서 다음과 같이 코딩한다.

    //물리 충돌
    void OnCollisionEnter2D(Collision2D collision)
    {
        if(collision.gameObject.tag == "Enermy") {
            //Attack
            if(rigid.velocity.y < 0 && transform.position.y > collision.transform.position.y) {
                OnAttack(collision.transform);
            }           
        }
    }

따라서 아까 Enermy에 태그를 만들어 준 이유는 플레이어 캐릭터가 부딪힌 스프라이트의 태그가 Enermy일 경우에만 해당 로직을 실행해주게 하기 위해서이다. 그리고 안에 if문의 내용을 보면 아까 생각한 로직이 그대로 들어가 있는 것을 볼 수 있다. 그리고는 OnAttack이라는 메서드에 충돌한 스프라이트의 transform을 넘기면서 위치, 스케일, 회전 등의 정보를 넘겨줍니다. (OnAttack으로 분리해서 코딩을 하게되면 보다 깔끔한 코딩이 가능해서 메서드를 만듦)

OnAttack의 내용은 다음과 같다.

    void OnAttack(Transform enermy) 
    {
        //Reaction Force
        rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);

        //Enermy Die
        EnermyMove enermyMove = enermy.GetComponent<EnermyMove>();
        enermyMove.OnDamaged();
    }

따라서 rigid.AddForce는 캐릭터가 적 NPC를 밟았을 때 밟았다는 느낌을 주기 위해서 위로 살짝 튀어오르는 효과를 준 것이다. 그리고는 적 NPC에 저장된 스크립트를 호출하고, 해당 스크립트에 있는 onDamaged()라는 메서드를 호출해서 Enermy가 피격되었을때의 이벤트를 처리해준다.

그리고 적 스크립트로 돌아가서 onDamaged 메서드를 만들고 그 안에 다음과 같이 코딩한다.

    public void OnDamaged() 
    {
        //Sprite Alpha
        render.color = new Color(1, 1, 1, 0.4f);
        //Sprite FlipY
        render.flipY = true;
        //Collider Disable
        capsuleCollier.enabled = false;
        //Die effect
        rigid.AddForce(Vector2.up * 5, ForceMode2D.Impulse);
        //Destroy enermy
        Invoke("DeActive", 5);
    }
    
    void DeActive() 
    {
        this.gameObject.SetActive(false);
    }

우선 SpriteRender에서 색을 바꾸고(RGB가 1씩 반영돼서 검은색이 된다.), 투명도를 주어서 해당 캐릭터가 죽었다는 것을 유저가 알 수 있도록 해준다.

그리고는 Y축 반전을 주어 좀 더 죽었음을 생동감있게 표현해주고 Collider를 해제하면서 밑으로 떨어질 수 있도록 한다. 밑으로 떨어지기 전에 Y축으로 살짝 튀어 올랐다가 떨어지게끔 해주고, 마지막으로 DeActive를 호출해서 뷰에서 사라질 수 있도록 한다.

이렇게 코딩하고 실행해보면 다음과 같은 실행 화면이 나온다.

유니티 3D 넉백 - yuniti 3D neogbaeg
캐릭터에게 밟힌 후 사라지는 적 NPC

이와 같은 결과를 받아볼 수 있는데 나만 그런건지 모르겠지만 2가지의 문제점이 아직도 존재한다.

🟡 오브젝트를 비활성화 했음에도 불구화고 비활성화가 안됨 (Log를 찍어보니 DeActive 함수가 호출이 안됨)

🟡 죽을 때 Y축 반전만 일어나야 하는데 좌우로 파르를 떠는 현상 (근데 이거는 버그지만 괜찮은 것 같다.)

(++이 두 부분에 대해서는 추후에 계속 해결해보고 해결이 되면 추가글로 남기도록 하겠다.)

그리고 공격을 했으면 공격을 받는 부분도 구현을 해야 하지 않겠나?

따라서 적 NPC에게 공격을 당했을 때 플레이어의 행동에 대해서도 코딩을 해보자.

방법은 간단하다. (쓰고 보니 전혀 안 간단하다.. 악마놈아😈)

아까 OnColliderOnCollisionEnter2D에 했던 로직에 else문을 만들어서 Enermy 태그를 가진 스프라이트와 if안의 상황이 아닌 모든 상황에서 부딪히게 되면 플레이에가 공격을 당했다고 생각을 하고 그 안에다가 플레이어가 공격을 당했을 시의 행동을 적어주면 된다. 

따라서 플레이어도 공격을 당하면 당하는 방향으로 조금 뒤로 밀려나면서 데미지를 입은 애니메이션이 나오고 색이 약간 투명해지는 방식으로 하겠다. 그리고 이번에는 조금 다른 방식으로 애니메이터를 만져야 한다. 

우선 데미지 입은 애니메이션은 점프의 마지막 모션을 넣어서 만들도록 할 예정이다. 따라서 다음과 같이 설정을 해서 애니메이션을 만들어준다.

유니티 3D 넉백 - yuniti 3D neogbaeg
데미지 애니메이션을 만든다. (자세한 부분은 유튜브 참고)

그리고는 애니메이터를 눌러서 들어가보면 생성한 애니메이션이 새롭게 생긴 것을 볼 수 있고 Idle과 Walk와 Run과 연결하는게 아니라 계속 찬밥 취급을 했던 Any StateExit를 사용할 예정이다.

Any State는 어떤 특정한 상황에만 실행하도록 하는 조건(?)과도 같은거도 Exit와 연결되어 있는 애니메이션은 설정된 시간 만큼 애니메이션을 재생하고 재생을 멈추게 된다. 따라서 연결을 해보면 다음과 같이 연결할 수 있다.

유니티 3D 넉백 - yuniti 3D neogbaeg
맞을 때 잠깐만 재생하면 되기 때문에 다음과 같이 연결한다.

그리고나서 Trigger로 변수를 만들고 Any State와 Damaged를 잇는 Transition에 조건을 추가해준다. (애니메이션 겹치는 부분도 제거해준다.) 그리고 여기서 Layer를 또 살펴보아야 하는데 플레이어 캐릭터와 적 NPC의 레이어를 각각 추가해주는데 다음과 같이 추가해준다.

플레이어 캐릭터 (Player로 설정해두고, PlayerDamaged를 하나 만들어둔다.)

적 NPC 캐릭터 (Enermy를 만들고 설정해둔다.)

플레이어 레이어 설정하는 부분 적 NPC 레이어 설정하는 부분

그리고나서 🟡 Edit > Project Setting > Physics2D > Layer Collision Matrix로 들어간다.

유니티 3D 넉백 - yuniti 3D neogbaeg
Layer Collision Matrix 설정해주는 부분

여기서 Enermy와 PlayerDamaged가 겹치는 부분에만 체크 박스를 해제한다. 해당 부분을 하는 이유는 캐릭터가 적에게 피격 당했을 때 Player라는 레이어 값에서 PlayerDamaged로 바꿔주면서 잠시 동안 무적상태로 만들려고 하기 때문이다.

그리고 Enermy와 Enermy를 체크 해제한 이유는 적 끼리는 충돌하지 않고 겹쳐질 수 있도록 하기 위해서 위와 같이 프로젝트 셋팅을 해준다.

그리고 코드를 적어보면 다음과 같다.

   void OnCollisionEnter2D(Collision2D collision) {
        if (collision.gameObject.tag == "Enermy") {
            //Attack
            if (rigid.velocity.y < 0 && transform.position.y > collision.transform.position.y) {
                OnAttack(collision.transform);
            }  else {
            	//기존 코드에서 해당 부분 추가
                OnDamaged(collision.transform.position);
            }       
        }
    }
    
    void OnDamaged(Vector2 targetPos) {
        //PlayerDamaged = 11 (한 대 맞으면 Layer 변경)
        this.gameObject.layer = 11;

        //피격 시 색 변경
        render.color = new Color(1, 1, 1, 0.4f);

        //맞으면 팅겨 나감
        int dirc = this.transform.position.x - targetPos.x > 0 ? 1 : -1;
        rigid.AddForce(new Vector2(dirc, 1) * 9, ForceMode2D.Impulse);

        //Animation
        animator.SetTrigger("Damaged");

        Invoke("OffDamaged", 2);
    }

    void OffDamaged() {
        //Player = 10;
        this.gameObject.layer = 10;
        render.color = new Color(1, 1, 1, 1);
    }

OnDamaged에서 layer를 11로 변경하는데 11이 바로 PlayerDamaged의 번호이다. 따라서 layer의 값을 변경해주면서 적과 충돌을 잠시 동안 안되게끔 바꾸고 피격 당했을 때 투명하게 색을 바꾸고, 맞으면 팅겨나가는 리액션을 설정해준다.

그리고 animator.SetTrigger를 사용했는데 아까 애니메이터에서 만든 트리거를 호출해서 Damaged라는 애니메이션을 실행시킬 수 있도록 방아쇠를 당기는 것이다. (Damaged는 Trigger 변수 이름을 적어주어야 한다.)

그리고는 마지막으로 무적상태를 해제하기 위한 메서드를 5초 뒤에 불러준다. (10이 Player 레이어 값의 번호이다.)

이렇게 설정하면 다음과 같이 실행이 되면 정상적으로 잘 된 것이다.

유니티 3D 넉백 - yuniti 3D neogbaeg
부딪히고 난 뒤 Enermy와 충돌하지 않는 것을 볼 수 있다.

오늘 프로젝트를 마무리 할라고 했는데, 남은 UI와 Scene 변경까지 적다보면 양이 너무 많아질 것 같다. 또 쓰다보면 생각나는게 많을꺼 같아서 이쯤에서 끊고 다음에 포스팅 하기로 생각했다.

하루 쉬었다고 기억이 안나서 다시 강의도 찾아보고 이전에 작업했던 코딩도 찾아보고 헤매서 조금 오래걸린 포스팅이긴 하지만 다시 되새김질을 하니까 또 공부도 되는 것 같고 시간이 아깝지 않은 그런 포스팅이었다. 따라서 다음에 포스팅을 마지막으로 해당 2D 플랫포머 게임 만드는 포스팅을 마무리 짓고, 그 다음에는 쯔구르 RPG 만드는 유니티를 공부해서 하나 둘 씩 포스팅 해보도록 하겠다.

그럼 오늘 여기서 마무리 짓고

바바~🖐🖐