write: 2021-12-01 / modified: 25/09/2024
Скрипт атакующего спутника/союзника/компаньона для 2D игры на Unity.
Спутник летает за игроком "_followPoint.position"
и добавляет в лист ScansClosestEnemys()
ближайших врагов в радиусе "detectRange"
попутно выбирая активной целью самого близкого к игроку врага FindTarget()
, как только текущая цель оказывается в зоне агро "agroRange"
делает рывок к ней на "distance"
, и наносит урон. После чего сбрасывает текущую цель летит к игроку, ждет и выбирает новую ближайшую цель.
Так как у спутника больше нет в игре применений было решено зафигачить все свои хотелки в один скрипт. Скорее всего это не самая лучшая реализация, но это работает черт побери.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class SummonDemon: MonoBehaviour
{
public float detectRange = 3;
public float agroRange = 3;
public float distance = 1;z
private float Speed = 3;
public int Damage = 1;
private CircleCollider2D _collider;
private Transform _followPoint;
public GameObject TargetEnemy;
public bool TargetLocked;
private bool LeaveState;
public List<GameObject> Enemys = new List<GameObject>();
private Color editorCircleColor = Color.green;
private State state;
private enum State { Normal, ChaseTarget, Attacking }
private void Start() {
_followPoint = GameObject.FindGameObjectWithTag("SummonFollowPoint").transform;
if (transform.parent.parent != null)
transform.parent = transform.parent.parent;
Collider2D _collider = GetComponentInChildren<CircleCollider2D>();
}
private void Update() {
Enemys.RemoveAll(item => item == null);
if(TargetEnemy != null && TargetLocked)
Debug.DrawLine(transform.position, TargetEnemy.transform.position, Color.yellow);
switch (state) {
default:
case State.Normal:
if (TargetEnemy == null && Enemys.Count != 0 && !TargetLocked) {
FindTarget();
}
transform.position = Vector2.Lerp(transform.position, _followPoint.position, Time.deltaTime*Speed);
if (TargetEnemy != null && Vector3.Distance(transform.position, TargetEnemy.transform.position) < agroRange)
if (!TargetLocked && LeaveState || TargetLocked && !LeaveState) {
StartCoroutine(Wait());
}
break;
case State.ChaseTarget:
transform.position = Vector2.Lerp(transform.position, TargetEnemy.transform.position, Time.deltaTime*Speed);
if (Vector3.Distance(transform.position, TargetEnemy.transform.position) > distance) {
transform.Translate(new Vector3(Speed * Time.deltaTime, 0, 0));
}
float attackRange = 0.5f;
if (TargetEnemy != null &&
Vector3.Distance(transform.position, TargetEnemy.transform.position) < attackRange) {
state = State.Attacking;
}
break;
case State.Attacking:
Debug.Log("ATTACK!");
TargetEnemy.GetComponent<IDamagable>().TakeDamage(Damage);
TargetEnemy = null;
state = State.Normal;
break;
}
}
IEnumerator Wait() {
state = State.ChaseTarget;
LeaveState = true;
yield return new WaitForSeconds(5);
TargetLocked = false;
LeaveState = false;
}
private void OnTriggerEnter2D(Collider2D col) {
Collider2D[] ScansEnemies = Physics2D.OverlapCircleAll(_followPoint.position, detectRange);
foreach (Collider2D enemy in ScansEnemies) {
if (enemy.gameObject.tag == "Enemy" && !Enemys.Contains(enemy.gameObject)) {
Enemys.Add(enemy.gameObject);
print(enemy.name + " added to list");
}
}
}
public void FindTarget() {
Debug.Log("Targeting!");
float lowestDist = Mathf.Infinity;
for(int i=0; i<Enemys.Count; i++) {
float dist = Vector3.Distance(Enemys[i].transform.position, _followPoint.position);
if (dist<lowestDist) {
lowestDist = dist;
TargetEnemy = Enemys[i];
TargetLocked = true;
print(TargetEnemy.name + " new target locked");
}
}
}
void OnDrawGizmosSelected() {
UnityEditor.Handles.color = editorCircleColor;
UnityEditor.Handles.DrawWireDisc(this.transform.position, this.transform.forward, detectRange);
}
}
}
Демон был отрисован в двух кадрах размером 32x32 за 10 минут на сайте pixilart.com