몬스터 AI구현
근접 몬스터와 원거리 몬스터 두종류
근접 몬스터
- 근접몬스터는 랜덤한 방향으로 이동하다
- 플레이어가 시야에 들어오면 플레이어를 쫒아온다 (플레이어가 사망할 때 까지)
- 플레이어가 공격범위에 들어오면 공격한다
원거리 몬스터
- 근접몬스터는 랜덤한 방향으로 이동하다
- 플레이어가 시야에 들어오면 플레이어를 공격 사정거리 까지만 쫒아온다 (플레이어가 사망할 때 까지)
- 플레이어가 공격범위에 들어오면 공격한다
- 공격이 쿨타임 중이고 이동 딜레이가 끝났다면 랜덤한 방향으로 이동한다(회피)
- 플레이어가 사정거리에서 멀어지면 다시 플레이어를 쫒는다
추가할 부분
- 몬스터 애니메이션
- 공격시 플레이어에게 데미지 전달
- 피격시 hp감소
- 프로토타입이 끝나면 길찾기 알고리즘도 추가
오브젝트 풀링 개선방향
- 원거리 몬스터와 플레이어의 탄환을 보관할 오브젝트 배치
- 코드도
코드
근거리 몬스터
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TestMeleeMonster : MonoBehaviour
{
//Editor에서 사용하는 변수
public float sight;
public float attackRange;
public float damage;
public float speed;
public float moveDelta;
public float moveSpan;
public float attackDelta;
public float attackDelay;
public GameObject[] targets;
public GameObject target;
private void Init()
{
this.sight = 5;
this.attackRange = 0.5f;
this.damage = 1;
this.speed = 3;
this.moveSpan = 1;
this.attackDelay = 2;
}
void Start()
{
this.Init();
}
void Update()
{
//공격 쿨타임
this.attackDelta += Time.deltaTime;
//타겟 서칭
if (this.targets.Length == 0)
{
this.SearchTarget();
}
if (this.targets.Length > 0 && this.target == null)
{
for (int i = 0; i < this.targets.Length; i++)
{
float distance = Vector3.Distance(this.targets[i].transform.position, this.transform.position);
if (distance < this.sight)
{
this.target = this.targets[i];
}
}
//타겟을 찾았다면 ! 애니메이션(Idel)
//if (this.target != null)
//{
// //서치애니메이션 실행
//}
}
//이동
if(!this.moveRunning) //moveRunning이 false라면
{
this.moveDelta += Time.deltaTime;
}
if (this.target == null)
{
if (this.moveDelta > this.moveSpan)
{
this.moveDelta = 0;
if(this.moveRoutine != null)
{
StopCoroutine(this.moveRoutine);
}
this.moveRoutine = StartCoroutine(this.Move());
}
}
else
{
Vector2 dir = (this.target.transform.position - this.transform.position).normalized;
this.transform.Translate(dir * this.speed * Time.deltaTime);
float distance = Vector3.Distance(this.target.transform.position, this.transform.position);
if (distance < this.attackRange)
{
if(this.attackDelta > this.attackDelay)
{
//공격
this.attackDelta = 0;
Debug.Log("Attack");
}
}
}
}
private void SearchTarget()
{
this.targets = GameObject.FindGameObjectsWithTag("Player");
}
private Coroutine moveRoutine;
public bool moveRunning;
private IEnumerator Move()
{
this.moveRunning = true;
//Vector2 dir = Vector2.zero - new Vector2(Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f));
Vector2 dir = new Vector2(Random.Range(-1, 1), Random.Range(-1, 1)).normalized;
float delta = 0;
while(2 > delta)
{
this.transform.Translate(dir * 0.5f * Time.deltaTime);
delta += Time.deltaTime;
yield return null;
}
this.moveRunning = false;
}
}
원거리 몬스터
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class TestRangedMonster : MonoBehaviour
{
//Editor에서 사용하는 변수
public float sight;
public float attackRange;
public float damage;
public float speed;
public float moveDelta;
public float moveSpan;
public float attackDelta;
public float attackDelay;
public GameObject[] targets;
public GameObject target;
public UnityAction<Vector3> attackAction;
private void Init()
{
this.sight = 5;
this.attackRange = 3.5f;
this.damage = 1;
this.speed = 2;
this.moveSpan = 1;
this.attackDelay = 2;
}
void Start()
{
this.Init();
}
void Update()
{
//공격 쿨타임
this.attackDelta += Time.deltaTime;
//타겟 서칭
if (this.targets.Length == 0)
{
this.SearchTarget();
}
if (this.targets.Length > 0 && this.target == null)
{
for (int i = 0; i < this.targets.Length; i++)
{
float distance = Vector3.Distance(this.targets[i].transform.position, this.transform.position);
if (distance < this.sight)
{
this.target = this.targets[i];
}
}
//타겟을 찾았다면 ! 애니메이션(Idel)
//if (this.target != null)
//{
// //서치애니메이션 실행
//}
}
//이동
if (!this.moveRunning) //moveRunning이 false라면
{
this.moveDelta += Time.deltaTime;
}
if (this.target == null)
{
if (this.moveDelta > this.moveSpan)
{
this.moveDelta = 0;
if (this.moveRoutine != null)
{
StopCoroutine(this.moveRoutine);
}
this.moveRoutine = StartCoroutine(this.Move());
}
}
else
{
float distance = Vector3.Distance(this.target.transform.position, this.transform.position);
if (distance < this.attackRange)
{
if (this.attackDelta > this.attackDelay)
{
//공격
this.attackDelta = 0;
this.attackAction(this.target.transform.position);
Debug.Log("Attack");
}
else
{
//쿨타임 중이면 이동
if (this.moveDelta > this.moveSpan)
{
this.moveDelta = 0;
if (this.moveRoutine != null)
{
StopCoroutine(this.moveRoutine);
}
this.moveRoutine = StartCoroutine(this.Move());
}
}
}
else
{
if (this.attackDelta > this.attackDelay)
{
Vector2 dir = (this.target.transform.position - this.transform.position).normalized;
this.transform.Translate(dir * this.speed * Time.deltaTime);
}
}
}
}
private void SearchTarget()
{
this.targets = GameObject.FindGameObjectsWithTag("Player");
}
private Coroutine moveRoutine;
public bool moveRunning;
private IEnumerator Move()
{
this.moveRunning = true;
//Vector2 dir = Vector2.zero - new Vector2(Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f));
Vector2 dir = new Vector2(Random.Range(-1, 1), Random.Range(-1, 1)).normalized;
float delta = 0;
while (2 > delta)
{
this.transform.Translate(dir * 1.0f * Time.deltaTime);
delta += Time.deltaTime;
yield return null;
}
this.moveRunning = false;
}
}
적 불릿 제너레이터
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TestEnemyBulletGenerator : MonoBehaviour
{
[SerializeField]
private GameObject bulletPrefab;
[SerializeField]
private TestRangedMonster testRangedMonster;
//오브젝트 풀링
//public GameObject bullet;
public int bulletPoolCount = 5;
public Queue<GameObject> bulletPool = new Queue<GameObject>();
void Start()
{
this.testRangedMonster = this.GetComponent<TestRangedMonster>();
this.testRangedMonster.attackAction = (targetPos) => {
this.Attack(targetPos);
};
for (int i = 0; i < bulletPoolCount; i++)
{
CreateBulletPool();
}
}
private void Attack(Vector3 targetPos)
{
GameObject go = this.DequeueBullet();
go.transform.position = this.transform.position;
float bulletAngle = GetAngle(this.transform.position, targetPos);
go.transform.eulerAngles = new Vector3(0, 0, bulletAngle - 90); //총알 방향이 Vector2.up이라서 -90
}
private void CreateBulletPool()
{
GameObject temp = Instantiate(bulletPrefab);
temp.GetComponent<TestEnemyBullet>().Init(this);
temp.SetActive(false);
bulletPool.Enqueue(temp);
}
public GameObject DequeueBullet()
{
if (this.bulletPool.Count <= 0)
{
CreateBulletPool();
}
GameObject dequeueObject = this.bulletPool.Dequeue();
//dequeueObject.GetComponent<TestEnemyBullet>().CleanUp();
dequeueObject.SetActive(true);
return dequeueObject;
}
public void EnqueueBullet(GameObject enqueueObject)
{
enqueueObject.SetActive(false);
this.bulletPool.Enqueue(enqueueObject);
}
public static float GetAngle(Vector2 vStart, Vector2 vEnd)
{
Vector2 v = vEnd - vStart;
return Mathf.Atan2(v.y, v.x) * Mathf.Rad2Deg;
}
}
적 불릿
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TestEnemyBullet : MonoBehaviour
{
[SerializeField]
private float bulletSpeed;
private TestEnemyBulletGenerator testEnemyBulletGenerator;
public void Init(TestEnemyBulletGenerator testEnemyBulletGenerator)
{
this.testEnemyBulletGenerator = testEnemyBulletGenerator;
}
void Update()
{
this.transform.Translate(Vector2.up * this.bulletSpeed * Time.deltaTime);
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "Wall")
{
//Destroy(this.gameObject);
this.testEnemyBulletGenerator.EnqueueBullet(this.gameObject);
}
if (collision.tag == "Player")
{
//Destroy(this.gameObject);
this.testEnemyBulletGenerator.EnqueueBullet(this.gameObject);
}
}
//public void CleanUp()
//{
//}
}'[ProjectT] > 개발일지' 카테고리의 다른 글
| [21.12.07] 프로토타입 영상자료 (0) | 2022.03.28 |
|---|---|
| [21.11.30] 몬스터 랜덤한 방향으로 이동 (0) | 2021.11.30 |
| [21.11.24] 조이스틱 공격_02 (0) | 2021.11.24 |
| [21.11.23] 조이스틱 공격_01 (0) | 2021.11.24 |
| [21.11.23] 조이스틱 이동 (0) | 2021.11.24 |