상세 컨텐츠

본문 제목

유니티 공부(8)

카테고리 없음

by MJ_119 2023. 9. 7. 19:16

본문

출처 : 골드메탈 유니티 뱀서라이크 09

 

타격감 있는 몬스터 처치 만들기

 

1. 피격 리액션

 - enemy 스크립트에 애니메이션과 넉백 추가.

 

 - 코루틴 : 생명주기와 비동기처럼 실행되는 함수

 - IEnumerator : 코루틴만의 반환형 인터페이스

 - yield : 코루틴의 반환 키워드

 - yield return을 통해 다양한 쉬는 시간을 지정 
        yield return null; // yield : 코루틴의 반환 키워드, 1 프레임 쉬기
        yield return new WaitForSeconds(2f); // ()안에 시간만큼 쉬기 : 2초 쉬기

 

 - 플레이어 기준의 반대 방향 : 현재 위치 - 플레이어 위치

 -  GetCurrentAnimatorStateInfo : 현재 상태 정보를 가져오는 함수

 -  IsName : 해당 상태의 이름이 지정된 것과 같은지 확인하는 함수
     

void FixedUpdate()
    {
        if (!isLive || anim.GetCurrentAnimatorStateInfo(0).IsName("Hit"))
            return;

        // 플레이어를 바라보도록 몬스터 설정
        Vector2 dirVec = target.position - rigid.position;
        Vector2 nextVec = dirVec.normalized * speed * Time.fixedDeltaTime;
        rigid.MovePosition(rigid.position + nextVec);
        rigid.velocity = Vector2.zero; // 물리속도가 이동에 영향을 주지 않도록 속도 제거
    }

위의 코드 해석 : 

    • isLive라는 변수가 false이거나
    • anim이라는 애니메이터 컴포넌트(아마도 몬스터의 애니메이션을 제어하는 데 사용됩니다)의 현재 애니메이션 상태가 "Hit"인 경우
  • 위의 어느 조건도 충족되지 않으면, 코드의 실행이 계속됩니다. 그렇지 않으면 함수에서 바로 리턴됩니다.

 

 

2. 사망 리액션

- enemy 스크립트

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

public class Enemy : MonoBehaviour
{
    public float speed;
    public float health;
    public float maxHealth;
    public RuntimeAnimatorController[] animCon;
    public Rigidbody2D target;

    bool isLive;

    Rigidbody2D rigid;
    Collider2D coll;
    Animator anim;
    SpriteRenderer spriter;
    WaitForFixedUpdate wait;

    void Awake()
    {
        rigid = GetComponent<Rigidbody2D>();
        coll = GetComponent<Collider2D>();
        anim = GetComponent<Animator>();
        spriter = GetComponent<SpriteRenderer>();
        wait = new WaitForFixedUpdate(); // WaitForFixedUpdate 초기화
    }

    void FixedUpdate()
    {
        // GetCurrentAnimatorStateInfo : 현재 상태 정보를 가져오는 함수
        // IsName : 해당 상태의 이름이 지정된 것과 같은지 확인하는 함수
        if (!isLive || anim.GetCurrentAnimatorStateInfo(0).IsName("Hit")) // 살아 있거나, Hit애니메이션 상태가 아니면 아래 코드 계속 진행
            return;

        // 플레이어를 바라보도록 몬스터 설정
        Vector2 dirVec = target.position - rigid.position;
        Vector2 nextVec = dirVec.normalized * speed * Time.fixedDeltaTime;
        rigid.MovePosition(rigid.position + nextVec);
        rigid.velocity = Vector2.zero; // 물리속도가 이동에 영향을 주지 않도록 속도 제거
    }

    void LateUpdate()
    {
        if (!isLive)
            return;
        // 목표(플레이어)의 X축 값과 자신(몬스터)의 X축 값을 비교하여 작으면 true가 되도록 설정
        // target = 플레이어, rigid = 몬스터
        spriter.flipX = target.position.x < rigid.position.x;
    }



    void OnEnable() // 활성화 될 때 아래 코드 실행
    {
        target = GameManager.instance.player.GetComponent<Rigidbody2D>();
        isLive = true;
        coll.enabled = true;
        rigid.simulated = true;
        spriter.sortingOrder = 2; // 
        anim.SetBool("Dead", false);
        health = maxHealth;


    }

    public void Init(SpawnData data)
    {
        anim.runtimeAnimatorController = animCon[data.spriteType];
        speed = data.speed;
        maxHealth = data.health;
        health = data.health;
    }

    void OnTriggerEnter2D(Collider2D collision)
    {
        if (!collision.CompareTag("Bullet") || !isLive)
            return;

        health -= collision.GetComponent<Bullet>().damage;
        StartCoroutine("KnockBack"); // "KnockBack"과 KnockBack() 둘다 똑같음. 어떤걸 사용해도 무방.
        StartCoroutine(KnockBack());


        if (health > 0)
        {
            // 아직 살아있음, 애니메이션과 넉백 추가
            anim.SetTrigger("Hit"); // 애니메이터 파라미터의 대소문자 맞는지 확인!
        }
        else
        {
            // 몬스터 사망.. Dead 애니메이션
            isLive = false;
            coll.enabled = false;
            rigid.simulated = false;
            spriter.sortingOrder = 1; // 죽고나서 가리면 안되기 때문에 레이어를 내림
            anim.SetBool("Dead", true);
            GameManager.instance.kill++; // 킬수 증가
            GameManager.instance.GetExp(); // 경험치 획득 및 레벨업
        }
    }

    // 코루틴 : 생명주기와 비동기처럼 실행되는 함수
    IEnumerator KnockBack() // IEnumerator : 코루틴만의 반환형 인터페이스
    {
        // yield return을 통해 다양한 쉬는 시간을 지정 
        //yield return null; // yield : 코루틴의 반환 키워드, 1 프레임 쉬기
        //yield return new WaitForSeconds(2f); // ()안에 시간만큼 쉬기 : 2초 쉬기

        yield return wait;
        Vector3 playerPos = GameManager.instance.player.transform.position;
        Vector3 dirVec = transform.position - playerPos; // 플레이어의 반대방향으로 가는것 구하기
                                                         // 플레이어 기준의 반대 방향 : 현재 위치 - 플레이어 위치
        rigid.AddForce(dirVec.normalized * 3, ForceMode2D.Impulse); // 반대방향 쪽으로 힘을 주기. 뒤에 *3은 넉백 크기

    }

    void Dead()
    {
        gameObject.SetActive(false);
    }
}

 

 

 

3. 처치 데이터 얻기

 - gameManager에서 관리

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

public class GameManager : MonoBehaviour
{
    public static GameManager instance;
    [Header("# Game Control")]
    public float gameTime; // 실제 게임 시간
    public float maxGameTime = 20f; // 최대 게임 시간
    [Header("# Player Info")]
    public int level; // 레벨
    public int kill; // 킬수
    public int exp; // 경험치
    public int[] nextExp = {3, 5, 10, 100, 150, 210, 280, 360, 450, 600}; // 현재레벨에 필요한 경험치
    [Header("# Game Object")]
    public PoolManager pool;
    public Player player;
    void Awake()
    {
        instance = this;
    }

    void Update()
    {
        gameTime += Time.deltaTime;

        if (gameTime > maxGameTime)
        {
            gameTime = maxGameTime;
        }
    }

    public void GetExp()
    {
        exp++;
        
        // 레벨업 코드
        if (exp == nextExp[level]) // 현재레벨에 필요한 경험치까지 100%차면 레벨업
        {
            level++; // 레벨업하고 경험치 초기화
            exp = 0;
        }

    }
}