본문 바로가기

유니티/Hunting게임[2D]

2D 미니게임 만들기 연습 7 ] 추가 설정 사항(파괴 애니메이션, 버그 수정)

지난 포스팅에서는 스테이지 관리와 최대 총알 개수를 설정하였으며, 총알 개수를 나타 내 주는 UI를 세팅하였다.

 

이제는 타겟이 파괴될 때 애니메이션 효과를 추가 해 주도록 하자.

 

Aseprite를 통하여 파괴되는 애니메이션을 추가 해 주도록 하자

 

파괴되는 도트 애니메이션 제작

이것도 지난 포스팅에서 Sheet를 추출 한 것 처럼 Sprite Sheet의 형태로 추출 해 준다.

 

Destroy

파괴되는 모습은 위와 같다.

 

유니티에 Sheet를 추가한 뒤, Slice를 해 준다. 

 

그리고 애니메이션을 추가 해 주면 다음과 같다.

애니메이션 화면

그러면 위 사진과 같이 애니메이션 화면이 나오게 될 것이다. 

 

애니메이션을 해당 타겟에 추가 해 주고, 자연스러운 모습이 나오게 프레임을 삭제 해 준다.

 

그리고 애니메이터를 열어 아래 사진과 같이 추가 해 준 다음, Trigger를 하나 만들어서 터지는 모션으로의 이동 조건을 Trigger로 설정 해 준다.

 

Animator 내부

그리고 마우스로 타겟을 클릭 했을 때 발동되는 코드에 Trigger 발동을 넣어주면 된다.

 

Target.cs 수정

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Target : MonoBehaviour
{
    bool hit = false; // 맞았는지 여부를 나타냄
    GameObject Manager;
    Animator Target1_ani;

    // Start is called before the first frame update
    void Start()
    {
        Manager = GameObject.Find("Manager");
        if (this.tag == "Score1")
        {
            Target1_ani = GetComponent<Animator>();
        }
    }

    // Update is called once per frame
    void Update()
    {
        if(hit == true)
        {
            if(this.tag == "Score1") // Tag Check
            {
                Target1_ani.SetTrigger("Destroy_Target1");

                Manager.GetComponent<SoundManager>().PlayScore1();
                Manager.GetComponent<StageManager>().MinusTarget1(); // 잡아야 하는 타겟의 수를 줄인다.
                Manager.GetComponent<ScoreManager>().SetOne();
                Destroy(gameObject);

            }
            else if(this.tag == "Minus1")
            {
                Manager.GetComponent<SoundManager>().PlayMinus1();
                Manager.GetComponent<ScoreManager>().MinusOne();

                Destroy(gameObject);
               
            }

        }
        
    }

    public void beHit()
    {
        hit = true;
    }
    

}

 

 


예상치 못한 문제

 

그런데 여기서 문제가 발생한다.

 

바로, 터지는 애니메이션이 출력되지 않는 것이다.

 

조금만 생각 해 보면 당연히 발생할 수 있는 부분이었다. 생각이 짧아서 그 부분을 처음에 잡아 내지 못한 것이다.

 

현재 상태

애니메이션을 설정 하는 사진에서 터지는 장면은 5프레임 동안 출력되게 된다.

 

그렇지만 코드 내에서는 출력이 되기 전에 Destroy(gameObject); 가 실행되게 된다.

 

따라서 애니메이션이 출력 될 때 까지 시간을 끌어 주었다가 Destroy가 실행되게 해 주어야 한다.


딜레이를 주었다가 시작하는 방법에는 두 가지가 존재한다.

 

1. Destroy(gameObject)를 별개의 함수로 만들어서 Invoke를 통해 딜레이를 주는 방법

 

2. 애니메이션 파일에서 프레임 이벤트를 부여하는 방법


1. Invoke를 이용한 딜레이 부여

 

Invoke는 특정 함수를 설정 한 딜레이 후에 실행되게 하는 역할을 한다.

 

아래 코드와 같이 Target.cs 코드를 수정 해 보자

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Target : MonoBehaviour
{
    bool hit = false; // 맞았는지 여부를 나타냄
    GameObject Manager;
    Animator Target1_ani;

    // Start is called before the first frame update
    void Start()
    {
        Manager = GameObject.Find("Manager");
        if (this.tag == "Score1")
        {
            Target1_ani = GetComponent<Animator>();
        }
    }

    // Update is called once per frame
    void Update()
    {
        if(hit == true)
        {
            if(this.tag == "Score1") // Tag Check
            {
                Target1_ani.SetTrigger("Destroy_Target1");

                Manager.GetComponent<SoundManager>().PlayScore1();
                Manager.GetComponent<StageManager>().MinusTarget1(); // 잡아야 하는 타겟의 수를 줄인다.
                Manager.GetComponent<ScoreManager>().SetOne();

                this.tag = "DeleteState"; // 애니메이션이 나오는 동안 사라지는 상태로 태그를 바꾼다.

                Invoke("Target1_Delete", 0.5f);

            }
            else if(this.tag == "Minus1")
            {
                Manager.GetComponent<SoundManager>().PlayMinus1();
                Manager.GetComponent<ScoreManager>().MinusOne();

                Destroy(gameObject);
               
            }

            // Delete();

        }
    }

    public void beHit()
    {
        hit = true;
    }
    
    void Target1_Delete()
    {
        Destroy(gameObject); // 터치시 삭제
    }
    

}

Destroy를 함수로 만들어 놓은 다음, Invoke를 이용하여 애니메이션이 끝나는 0.5f 초 뒤에 Destroy 함수가 실행되게 만들어 준다.

 

그런데 여기서 또 문제가 생기게 된다.

 

애니메이션이 끝나기를 기다리면 생기는 문제

애니메이션이 진행되는 동안에도 클릭을 하게 되면 그 사이에 계속 타겟이 눌릴 수 있는 문제가 생기게 된다.

 

따라서 코드에도 볼 수 있듯이, 새로운 tag를 만들어 주어 삭제되는 중이라는 것을 표현 해 주어야 한다.


이 것은 2번 방법에도 적용된다.

 

 

2. 애니메이션 프레임 이벤트 이용

 

터지는 애니메이션에 들어 가 준다.

 

애니메이션 프레임 이벤트 추가

그리고 위 사진과 같이 키 프레임에서 마우스 오른쪽 버튼을 누르고 Add Animation Event를 눌러 준다.

 

이벤트 대상 및 실행 함수 설정

inspector 부분에 보면 위와 같이 뜰 것인데 여기서 Target 속에 제거되는 함수가 있기에 대상 오브젝트를 Target Prefab으로 설정 해 준다.

 


 

버그  - 총알이 0개가 되는 동시에 마지막 대상을 맞췄을 경우 무조건 GameOver가 나오는 현상

위 현상은 게임 오버의 조건이 총알 개수가 0개인 시점에서 타겟의 수가 1개 이상일 때이다.

(남은 총알들로 다 맞추지 못했기 때문이다.)

 

버그 원인

이것은 위 사진과 같이 총알의 감소 타이밍과 타겟 수의 감소 타이밍, 그리고 게임 오버 판단 타이밍이 맞지 않아서 생긴 것으로 추정된다.

 

버그 솔루션

따라서 총알을 소비하며, 남은 타겟 수가 감소하는 것들을 같은 함수로 묶어서 같은 타이밍에 넣어주게 되면 게임 오버 판단이 뒤로 가게 된다.

 

MousePointer.cs 변경 사항

void Update()
    {
        mousePos = Input.mousePosition;
        mousePos = UnityEngine.Camera.main.ScreenToWorldPoint(mousePos);
        mousePos.z = -1;
        pointerRed.transform.position = mousePos;
        Ray2D ray = new Ray2D(mousePos, Vector2.zero); // 원점 ~ 포인터로 발사되는 레이저

        if (Input.GetMouseButtonDown(0))
        {
            //Debug.Log(mousePos);

            float distance = Mathf.Infinity; // Ray 내에서 감지할 최대 거리

            // RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction, distance); // 다 잡음 
            RaycastHit2D hitDrawer = Physics2D.Raycast(ray.origin, ray.direction, distance, 1 << LayerMask.NameToLayer("Touchable")); // 1 << LayerMask.NameToLayer("Touchable") 대신 2048을 써도 됨
            
            if(bullet.GetBulletCount() > 0)
            {
                
                if (hitDrawer) // 맞았을 때는 Stage에서 총알 차감!
                {
                    Debug.Log("터치!");
                    hitDrawer.collider.gameObject.GetComponent<Target>().beHit();
                }
                else
                {
                    bullet.discountBullet(); // 안맞았을 때 총알 차감
                }


            }
            else
            {
                Debug.Log("남은 총알이 없습니다!");
            }


        }

    }

여기서 핵심은 타겟에 총알이 맞았을 때에만 총알이 줄어드는 것을 묶는 것이다.

 

총알이 맞지 않았을 때는 바로 총알이 줄어들게 해 준다.

 

Target.cs에서 hit이 활성화 되게 하면 StageManager.cs 의 MinusTarget1()을 실행시킨다.

 

여기에서 타겟의 개수가 감소하는데 여기에서 타겟을 맞췄을 때 총알의 수를 제거 해 준다.

 

StageManager.cs 중 타겟 감소 함수

public void MinusTarget1()
    {
        remainTarget1--;
        bullet.discountBullet(); // 맞췄을 때 총알 감소
    }

이렇게 해 주게 되면 소위 말해 러브샷이 가능 해 지게 된다.

 


 

일단 독수리 게임 모방 구현은 여기까지 하도록 하겠다.

 

자세한 코드 및 내용은 github에 올려 놓았다.

 

https://github.com/kkyoulza/Unity/blob/main/2D/HuntingEagle.md