출처 : 골드메탈 유니티 뱀서라이크 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; // 물리속도가 이동에 영향을 주지 않도록 속도 제거
}
위의 코드 해석 :
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;
}
}
}