功能:生成敌人按指定路线移动,受伤减血死亡,到达终点攻击玩家,播放各种行为动画。
需求分析:
敌人马达 EnemyMotor,移动 旋转 寻路功能。
敌人状态EnemyStatusInfo,血量 受伤 死亡。
敌人动画EnemyAnimation,整合动画"名称" 播放功能。
敌人AI EnemyAI 用来判断状态 执行敌人行为。
创建根线路,添加多条配有路点的路线坐标。
路线WayLine,包含路点坐标Vector3[] 是否可用bool。
敌人生成器EnemySpawn,放在跟线路中 生成敌人。
生成器启动 获取所有路线的路点。
生成敌人时 随机选择可使用路线。
SpawnSystem,管理生成器 提供激活下一个生成器。
SpawnTigger,检测接触玩家。
敌人马达EnemyMotor↓
///
/// 敌人马达,负责敌人运动功能
///
public class EnemyMotor : MonoBehaviour
{
//生成敌人时传递路线引用
public WayLine wayline;
///
/// 移动速度
///
public float moveSpeed = 5;
///
/// 向前移动
///
public void MovementForward()
{
transform.Translate(0, 0, moveSpeed * Time.deltaTime);
}
///
/// 朝向目标点的旋转
///
/// 目标位置
public void LookRotation(Vector3 targetPos)
{
//暂时…… 一帧旋转至目标方位
transform.LookAt(targetPos);
}
private int currentIndex;
///
/// 寻路
///
public bool Pathfinding()
{
//如果索引超过最大值 则 返回false ,表示寻路结束
if (currentIndex >= wayline.Points.Length) return false;
LookRotation(wayline.Points[currentIndex]);
MovementForward();
if (Vector3.Distance(transform.position, wayline.Points[currentIndex]) <= 0.1)
currentIndex++;
return true;//返回true 表示 可以继续寻路
}
}
敌人状态EnemyStatusInfo↓
///
/// 敌人状态信息类
///
public class EnemyStatusInfo : MonoBehaviour
{
///
/// 当前血量
///
public float currentHP;
///
/// 最大血量
///
public float maxHP;
public void Damage(float amount)
{
//如果敌人已经死亡 则退出(防止虐尸)
if (currentHP <= 0) return;
currentHP -= amount;
if (currentHP <= 0)
Death();
}
///
/// 死亡延迟时间
///
public float deathDelay =10;
//敌人生成器引用 敌人创建时由生成器传递
public EnemySpawn spawn;
///
/// 死亡
///
public void Death()
{
//销毁当前游戏物体
Destroy(gameObject, deathDelay);
//播放动画
var anim = GetComponent();
anim.Play(anim.deathName);
//修改状态
GetComponent().state = EnemyAI.State.Death;
//修改路线状态
GetComponent().wayline.IsUsable = true;
//需要再生成一个敌人
spawn.GenerateEnemy();
}
}
敌人动画EnemyAnimation↓
///
/// 敌人动画类
///
public class EnemyAnimation : MonoBehaviour
{
///
/// 跑步动画
///
public string runName = "run";
///
/// 攻击动画
///
public string atkName = "shooting";
///
/// 死亡动画
///
public string deathName = "death";
///
/// 闲置动画
///
public string idleName = "idleWgun";
private Animation anim;
//public AnimationAction action;
private void Awake()
{
//查找动画组件
anim = GetComponentInChildren();
//action=new AnimationAction();
}
///
/// 播放指定名称的人物动画
///
/// 动画名称
public void Play(string name)
{
anim.CrossFade(name);
}
///
/// 指定动画片段是否正在播放
///
/// 动画片段名称
///
public bool IsPlaying(string name)
{
return anim.IsPlaying(name);
}
}
播放行为类AnimationAction↓
public class AnimationAction
{
//附加在敌人模型上的动画组件引用
private Animation anim;
//创建动画行为类
public AnimationAction(Animation anim)
{
this.anim=anim;
}
//播放动画
public void Play(string animName)
{
anim.CrossFade(animName);
}
}
敌人AI EnemyAI↓
///
/// 人工智能
///
[RequireComponent(typeof(EnemyAnimation),typeof(EnemyMotor),typeof(EnemyStatusInfo))]
public class EnemyAI : MonoBehaviour
{
///
/// 敌人状态
///
public enum State
{
///
/// 攻击状态
///
Attack,
///
/// 死亡状态
///
Death,
///
/// 寻路状态
///
Pathfinding
}
private EnemyAnimation animAction;
private EnemyMotor motor;
private Gun gun;
private GunAnimation gunAnim;
private void Start()
{
animAction = GetComponent();
motor = GetComponent();
gun = GetComponent();
gunAnim = GetComponent();
}
///
/// 敌人状态
///
public State state = State.Pathfinding;
private void Update()
{
switch (state)
{
case State.Pathfinding:
Pathfinding();
break;
case State.Attack:
Attack();
break;
}
}
private float atkTime;
///
/// 攻击间隔
///
public float atkInterval = 3;
///
/// 攻击延迟时间
///
public float delay = 0.3f;
private void Attack()
{
motor.LookRotation(PlayerStatusInfo.Instance.transform.position);
//限制攻击频率
//播放攻动画
if (atkTime <= Time.time && !gunAnim.IsPlaying(gunAnim.updateAnimName))
{
animAction.Play(animAction.atkName);
//希望动画播放到某一时刻再执行攻击行为
//建议使用动画事件
Invoke("Shoot", delay);
atkTime = Time.time + atkInterval;
}
if (!animAction.IsPlaying(animAction.atkName) && !gunAnim.IsPlaying(gunAnim.updateAnimName))
{
//如果攻击动画没有播放 再 播放闲置动画
animAction.Play(animAction.idleName);
}
}
private void Pathfinding()
{
//播放跑步动画
animAction.Play(animAction.runName);
//调用马达寻路功能 如果到达终点,修改状态为 state 攻击
if (!motor.Pathfinding()) state = State.Attack;
}
private void Shoot()
{
if (!gun.Firing(PlayerStatusInfo.Instance.headTF.position - gun.firePoint.position))
{
//如果发射子弹失败
gun.UpdateAmmo();
}
}
}
线路WayLine↓
///
/// 路线类
///
public class WayLine
{
///
/// 所有路点坐标
///
public Vector3[] Points { get; set; }
///
/// 是否可用
///
public bool IsUsable { get; set; }
//默认 IsUsable 为 false
// Points 为 null
public WayLine(int pointCount)
{
this.Points = new Vector3[pointCount];
this.IsUsable = true;
}
}
敌人生成器EnemySpawn↓
///
/// 敌人生成器
///
public class EnemySpawn : MonoBehaviour
{
///
/// 开始时需要创建的敌人数量
///
public int startCount = 2;
private void Start()
{
CalculateWayLines();
for (int i = 0; i < startCount; i++)
{
GenerateEnemy();
}
}
//计算路线
private WayLine[] lines;
private void CalculateWayLines()
{
//WayLine 路线 与 子物体 对应
//lines[0].Points 该路线所有路点坐标 与 子物体的子物体.Position对应
lines = new WayLine[transform.childCount];
//创建路线
for (int i = 0; i < lines.Length; i++)//0 1 2
{
Transform waylineTF = transform.GetChild(i);
lines[i] = new WayLine(waylineTF.childCount);
for (int pointIndex = 0; pointIndex < waylineTF.childCount; pointIndex++)
{
//获取每个路点坐标
lines[i].Points[pointIndex] = waylineTF.GetChild(pointIndex).position;
}
}
}
//当前产生的敌人数量
private int spawnedCount;
///
/// 可以产生敌人的最大值
///
public int maxCount;
///
/// 产生敌人的最大延迟时间
///
public int maxDelay = 10;
///
/// 敌人类型
///
public GameObject[] enemyTypes;
///
/// 生成一个敌人(一开始根据startCount生成敌人,敌人死亡时生成敌人)
///
public void GenerateEnemy()
{
if (spawnedCount < maxCount)
{
spawnedCount++;
// 随机延迟时间
float delay = Random.Range(0, maxDelay);
Invoke("CreateEnemy", delay);
}
else
{
print("over");
//生成任务结束……
//如果所有敌人死亡,再启用下一个生成器
if (IsEnemyAllDeath())
{
print("death");
GetComponentInParent().ActivateNextSpawn();
}
}
}
private bool IsEnemyAllDeath()
{
//便利所有路线
foreach (var item in lines)
{
if (!item.IsUsable)
return false;
}
return true;
}
private void CreateEnemy()
{
//查找所有可用路线
var usableWaylines = SelectUsableWayLines();
//随机选择一条
WayLine wayLine = usableWaylines[Random.Range(0, usableWaylines.Length)];
//创建一个敌人
//GameObject enemyGO = Instantiate(敌人预制件, 第一个路点, Quaternion.identity) as GameObject;
int enemyTypeIndex = Random.Range(0, enemyTypes.Length);
GameObject enemyGO = Instantiate(enemyTypes[enemyTypeIndex], wayLine.Points[0], Quaternion.identity) as GameObject;
//传递信息
enemyGO.GetComponent().wayline = wayLine;
wayLine.IsUsable = false;//该路线不可用
//传递当前生成器对象引用,便于敌人死亡时调用当前对象的生成敌人方法
//[建议使用委托代替]
enemyGO.GetComponent().spawn = this;
}
private WayLine[] SelectUsableWayLines()
{
List result = new List(lines.Length);
foreach (var item in lines)
{
if (item.IsUsable) result.Add(item);
}
return result.ToArray();
}
}
生成器系统SpawnSystem↓
///
/// 生成器系统
///
public class SpawnSystem : MonoBehaviour
{
private GameObject[] spawnList;
private void Start()
{
spawnList = new GameObject[transform.childCount];
for (int i = 0; i < spawnList.Length; i++)
{
spawnList[i] = transform.GetChild(i).gameObject;
}
ActivateNextSpawn();
}
public int currentIndex=-1;
public void ActivateNextSpawn()
{
if (currentIndex != -1)
spawnList[currentIndex].SetActive(false);
if (currentIndex < spawnList.Length - 1)
{
spawnList[++currentIndex].SetActive(true);
}
else
{
//游戏结束
Debug.Log("游戏结束喽");
}
}
}
SpawnTrigger
///
///
///
public class SpawnTrigger : MonoBehaviour
{
public GameObject targetSpawn;
private void OnTriggerEnter(Collider other)
{
//if (other.tag == "Player")
if(other.CompareTag("Player"))
{
targetSpawn.SetActive(true);
gameObject.SetActive(false);
}
}
}