注意:在阅读本文前建议观看对应的视频教程,本文主要作为那些对视频教程仍有疑问的学习者的参考资料。且本文的部分内容有些过时(如仍使用旧版Unity的动画系统),故直接阅读本文对Unity的学习没有太大意义
视频教程:https://www.bilibili.com/video/BV12s411g7gU?p=136
目录
敌人模块
需求分析
EnemyMotor 敌人马达类
EnemyStatusInfo 敌人状态信息类
EnemyAnimation 敌人动画类
敌人生成器
需求分析
EnemySpawn 敌人生成器类
Path 路线类
敌人模块
1.敌人沿指定路线运动
2.受击后减血死亡
3.到达终点攻击玩家
4.运动播放跑步动画,攻击播放攻击动画,攻击间隔播放闲置动画,死亡播放死亡动画
需求分析
EnemyMotor 敌人马达类:提供移动、旋转、寻路功能
EnemyStatusInfo 敌人状态信息类:定义血量,提供受伤、死亡的功能
EnemyAnimation 敌人动画类:定义各种动画名称,提供播放动画的功能
ps:由于没有相应的游戏素材,故本文使用print(“播放XX动画”)来代替场景中动画的部分,其次本文建议创建几个对应的空动画以满足运行条件
关于动画的创建及在新版中使用旧版动画系统的注意事项,请阅读Unity脚本(三)中关于Animation的内容
EnemyAI 敌人AI类:通过判断状态,执行寻路或者攻击
ps:以上组件均应挂载至敌人的预制体上,但在实际开发中不建议直接将组件构造至模型上,应为其创建一个空物体作为其父级并将组件挂载至该父级上,如下图
EnemyMotor 敌人马达类
1.定义向前移动的方法
2.定义朝向目标点旋转的方法
3.定义寻路的方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//敌人马达,提供移动、旋转、寻路功能
public class EnemyMotor : MonoBehaviour
{
public Path path;
//当前路径点的索引
private int currentPointIndex = 0;
public float moveSpeed;
private void Update()
{
PathFinding();
}
//向前移动
public void MovementForward()
{
transform.Translate(0, 0, moveSpeed * Time.deltaTime);
}
//注视选转
public void LookRotation(Vector3 target)
{
this.transform.LookAt(target);
}
//寻路,沿路线移动
public bool PathFinding()
{
//return true;继续寻路
//return false;到达终点,无需寻路
//如果到达目标点
//更新目标点(向下一个点移动)
if (path == null || currentPointIndex >= path.wayPoints.Length) return false;
//朝向目标点
LookRotation(path.wayPoints[currentPointIndex]);
//向前移动
MovementForward();
//如果达到目标(当前位置接近于目标点)
if (Vector3.Distance(transform.position, path.wayPoints[currentPointIndex]) < 0.1f)
{
currentPointIndex++;
}
return true;
}
}
EnemyStatusInfo 敌人状态信息类
1.定义变量:当前生命值
2.定义方法:受伤,死亡
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//敌人状态信息类,定义敌人信息,提供受伤死亡功能
public class EnemyStatusInfo : MonoBehaviour
{
public float HP = 200;
public float maxHP = 200;
//死亡延迟销毁时间
public float deathDelay = 5;
public EnemySpawn spawn;
//受伤
public void Damage(float damage)
{
//扣血
HP -= damage;
//血量为0时,调用死亡方法
if (HP <= 0)
Death();
}
//死亡
public void Death()
{
//播放死亡动画
var anim = GetComponent<EnemyAnimation>();
anim.action.Play(anim.deathAnimName);
print("播放死亡动画");
//销毁当前物体
Destroy(gameObject, deathDelay);
//设置路线状态
GetComponent<EnemyMotor>().path.isUsable = true;
//产生下一个物体
spawn.GenerateEnemy();
}
//测试方法,鼠标点击敌人,对其造成50点伤害
private void OnMouseDown() {
Damage(50);
}
}
ps:请务必在敌人的预制体上挂载碰撞组件(Collider),否则OnMouseDown()无法生效
EnemyAnimation 敌人动画类
1.定义各种动画名称的变量,如跑步、闲置、攻击
2.定义AnimationAction类,提供有关动画的行为
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyAnimation : MonoBehaviour
{
//跑步动画名称
public string runAnimName;
//攻击动画名称
public string attackAnimName;
//闲置动画名称
public string idleAnimaName;
//死亡动画名称
public string deathAnimName;
//行为类
public AnimationAction action;
private void Awake()
{
//action=new AnimationAction(?);
action=new AnimationAction(GetComponent<Animation>());
}
}
AnimationAction 动画行为类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//动画行为类,提供有关动画的行为
public class AnimationAction
{
//附加在敌人模型上的动画组件引用
private Animation anim;
//创建动画行为类
public AnimationAction(Animation anim)
{
this.anim=anim;
}
//播放动画
public void Play(string animName)
{
anim.CrossFade(animName);
}
public bool IsPlaying(string animName)
{
return anim.IsPlaying(animName);
}
}
敌人生成器
1.开始时生成指定数量的敌人
2.为每人随机选择一条可以使用的路线
3.敌人类型、产生的延迟时间随机
4.当敌人死亡后,再产生下一个敌人,直到生成数量到达上限为止
需求分析
1.创建根路线,并添加多条配有路点的路线
2.EnemySpawn 敌人生成器类:附加到根路线中,提供生成敌人的功能
3.Path 路线类:包括的属性有,路点坐标Vector3[] wayPoints,是否可用bool IsUseable
4.当生成器启用后,计算所有所有可以路线路线
5.当生成敌人时,随机选择一条可以使用的路线
EnemySpawn 敌人生成器类
定义变量:
GameObject[] enemyTypes:用于记录敌人预制件
int startCount:用于记录开始时需要创建的敌人数量
int spawnedCount:用于记录已经产生的敌人数量
int maxCount:用于记录产生敌人数量的上限
int maxDelay:用于记录产生敌人的最大延迟时间
定义方法:
CalculateWayLines():用于计算所有路线及坐标
GenerateEnemy():用于生成一个敌人。
using System.Net.Mail;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemySpawn : MonoBehaviour
{
//需要创建的敌人预制件
public GameObject[] enmeyType;
//需要创建敌人的最大数目
public int maxCount = 5;
//初始创建的敌人数目
public int startCount = 2;
//已经创建的敌人数目
private int spawnCount;
private Path[] paths;
public int maxDelay = 10;
//生成一个敌人
public void GenerateEnemy()
{
spawnCount++;
//如果生成数量已达到上限
if(spawnCount>=maxCount) return;
//延迟时间随机
Invoke("GreateEnemy", Random.Range(0, maxDelay));
}
private void GreateEnemy()
{
//选择所有可以使用的路线
Path[] usablePaths = SelectUsablePath();
//随机选择一条可以使用的路线
Path path = usablePaths[Random.Range(0, usablePaths.Length)];
int randomIndex = Random.Range(0, enmeyType.Length);
//Instantiate(敌人预制件,位置,旋转角度);
GameObject go = Instantiate(enmeyType[randomIndex], path.wayPoints[0], Quaternion.identity) as GameObject;
//配置信息
EnemyMotor motor = go.GetComponent<EnemyMotor>();
motor.path = path;
path.isUsable = false;
//传递生成器引用
go.GetComponent<EnemyStatusInfo>().spawn=this;
}
private void Start()
{
CalculateWayLines();
GenerateEnemy();
}
private void CalculateWayLines()
{
paths = new Path[this.transform.childCount];
for (int i = 0; i < paths.Length; i++)
{
//每一条路线
//路线变换组件的引用
Transform path = this.transform.GetChild(i);
//创建路线对象
paths[i] = new Path(path.childCount);
for (int pointIndex = 0; pointIndex < path.childCount; pointIndex++)
{
paths[i].wayPoints[pointIndex] = path.GetChild(pointIndex).position;
}
}
}
//闲置可以使用的路线
private Path[] SelectUsablePath()
{
List<Path> res = new List<Path>(paths.Length);
//遍历所有路线
foreach (var path in paths)
{
//如果可以使用,添加到res中
if (path.isUsable) res.Add(path);
}
return res.ToArray();
}
}
Path 路线类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Path
{
public Vector3[] wayPoints { get; set; }
public bool isUsable{ get; set; }
//构造函数
public Path(int wayPointCount){
wayPoints=new Vector3[wayPointCount];
isUsable=true;
}
}