动作管理
用来管理单位的动作。
我将动作分别攻击、防御、奔跑、发呆、死亡这5个基本动作,一般来说给你的模型的动作也就这几项。
我们今天就写个来统一管理,并进行通用化。
Animation和Animator
Unity的话,动画就分为这二个。
一个是状态机、一个是动画帧数。
接着我们来对这二个进行操作即可。
Animation这个的话,我就用Trigger来进行控制。
Animator这个的话,直接用Play即可。
using UnityEngine;
using System.Collections;
public class BehaviorOfAni : MonoBehaviour {
Animation animation;
Animator animator;
public string nowAni = "";
int nowPriority = -1; // 当前动作优先级
float nowAniTime = 0; // 当前动作时间
float time = 0; // 当前动作计时
[SerializeField]
string []attackAniName = {"Attack1", "Attack2", "Attack3"}; // 攻击名字
[SerializeField]
float[] attackAniTime = {1.2f, 1.5f, 1.2f}; // 攻击时间
public int attackPriority = 3; // 攻击优先级
[SerializeField]
string[] defendAniName = {"Defend"}; // 防御名字
[SerializeField]
float[] defendAniTime = {1.0f}; // 防御时间
public int defendPriority = 1; // 防御优先级
[SerializeField]
string runAniName = "Run"; // 奔跑名字
[SerializeField]
float runAniTime = 1.0f; // 奔跑时间
public int runPriority = 2; // 奔跑优先级
[SerializeField]
string idleAniName = "Idle"; // 发呆名字
[SerializeField]
float idleAniTime = 1.0f; // 发呆时间
public int idlePriority = 0; // 发呆优先级
[SerializeField]
string deathAniName = "Death"; // 死亡名字
[SerializeField]
float deathAniTime = 2.5f; // 死亡时间
public int deathPriority = 10; // 死亡优先级
// Use this for initialization
[SerializeField]
public int nowTime = 0;
void Start () {
animation = GetComponent<Animation>();
animator = GetComponent<Animator>();
if (animation == null && animator == null) {
Debug.LogError("未指定animation和animator中的任意一个");
}
}
public float beDef() {
if (this.nowPriority < defendPriority) {
if (this.nowAni.Equals("Defent")) {
nowTime = 0;
} else {
nowTime++;
if (nowTime >= defendAniName.Length) {
nowTime = 0;
}
}
if (animation) {
animation.Play(defendAniName[nowTime]);
} else {
animator.SetBool(defendAniName[nowTime], true);
}
this.nowPriority = defendPriority;
this.nowAniTime = defendAniTime[nowTime];
time = 0;
nowAni = "Defend";
return nowAniTime;
}
return 0;
}
public float beAttack() {
if (this.nowPriority < attackPriority) {
if (this.nowAni.Equals("Attack")) {
nowTime = 0;
} else {
nowTime++;
if (nowTime >= attackAniName.Length) {
nowTime = 0;
}
}
if (animation) {
animation.Play(attackAniName[nowTime]);
} else {
animator.SetBool(attackAniName[nowTime], true);
}
this.nowPriority = attackPriority;
this.nowAniTime = attackAniTime[nowTime];
time = 0;
nowAni = "Attack";
return nowAniTime;
}
return 0;
}
public float beIdle() {
if (this.nowPriority < idlePriority) {
if (animation) {
animation.Play(idleAniName);
} else {
animator.SetBool(idleAniName, true);
}
this.nowPriority = idlePriority;
this.nowAniTime = idleAniTime;
time = 0;
nowAni = "Idle";
return nowAniTime;
}
return 0;
}
public float beMustIdle() {
if (animation) {
animation.Play(idleAniName);
} else {
animator.SetBool(idleAniName, true);
}
this.nowPriority = idlePriority;
this.nowAniTime = idleAniTime;
time = 0;
nowAni = "Idle";
return nowAniTime;
}
public float beRun() {
if (this.nowPriority < runPriority) {
if (animation) {
animation.Play(runAniName);
} else {
animator.SetBool(runAniName, true);
}
this.nowPriority = runPriority;
this.nowAniTime = runAniTime;
time = 0;
nowAni = "Run";
return nowAniTime;
}
return 0;
}
public float beDeath() {
if (this.nowPriority < deathPriority) {
if (animation) {
animation.Play(deathAniName);
} else {
animator.SetBool(deathAniName, true);
}
this.nowPriority = deathPriority;
this.nowAniTime = deathAniTime;
time = 0;
nowAni = "Death";
return nowAniTime;
}
return 0;
}
void FixedUpdate() {
if (nowPriority != 0) {
time += Time.fixedDeltaTime;
if (time >= nowAniTime) {
time = 0;
nowPriority = -999;
beMustIdle();
nowAni = "";
}
}
}
}
加上执行时间和优先级,即可实现动作管理,用起来也非常的方便。
测试
void Start () {
StartCoroutine(fun());
}
IEnumerator fun() {
yield return new WaitForSeconds(1.0f);
float time = behaviorOfAni.beAttack();
TextControl.getIns().set3dText("正在执行攻击-" + time, behaviorOfAni.gameObject, Vector3.up * 2, time * 0.75f);
yield return new WaitForSeconds(time * 1.25f);
time = behaviorOfAni.beRun();
TextControl.getIns().set3dText("正在执行跑步-" + time, behaviorOfAni.gameObject, Vector3.up * 4, time * 0.75f);
yield return new WaitForSeconds(time * 1.25f);
time = behaviorOfAni.beIdle();
TextControl.getIns().set3dText("正在执行呆立-" + time, behaviorOfAni.gameObject, Vector3.up * 6, time * 0.75f);
yield return new WaitForSeconds(time * 1.25f);
time = behaviorOfAni.beDeath();
TextControl.getIns().set3dText("正在执行死亡-" + time, behaviorOfAni.gameObject, Vector3.up * 8, time * 0.75f);
yield return new WaitForSeconds(time * 1.25f);
behaviorOfAni.gameObject.AddComponent<AutoDeath>().maxTime = time;
}
自动寻路攻击
接着就是配套的自动寻路攻击。
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(NavMeshAgent))]
public class BehaviorOfMove : MonoBehaviour {
NavMeshAgent agent;
float stoppingDistance;
// Use this for initialization
void Start() {
init();
stoppingDistance = agent.stoppingDistance;
}
protected void init() {
agent = GetComponent<NavMeshAgent>();
}
public void moveTo(Vector3 pos) {
if (!isFinish(pos)) {
agent.destination = pos;
}
}
public bool isFinish(Vector3 pos) {
if (Vector3.Distance(this.transform.position, pos) <= stoppingDistance) {
return true;
} else {
return false;
}
}
}
具体的话,根据怪物的不同,我们需要继承下面这个类,进行逻辑设计。
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(BehaviorOfMove))]
public class BehaviorAutoAttack : MonoBehaviour {
protected BehaviorOfMove move;
protectedGameObject des; // 攻击目标
[SerializeField, TooltipAttribute("刷新周期(次/秒)")]
protected int defTime = 10; // 周期
protected float time; // 记录周期
protected float maxTime; // 记录周期
protected virtual void Start() {
init();
}
protected virtual void init() {
move = this.GetComponent<BehaviorOfMove>();
maxTime = 1.0f / defTime;
}
public void setAttack(GameObject att) {
des = att;
}
protected virtual void attack() {
}
protected virtual void run() {
}
protected virtual void idle() {
}
protected virtual void doThing() {
time += Time.fixedDeltaTime;
if (time >= maxTime) {
time = 0;
if (des) {
move.moveTo(des.transform.position);
if (move.isFinish(des.transform.position)) {
this.transform.LookAt(des.transform.position);
attack();
} else {
run();
}
}
}
}
}
以这个弓箭手为例(一般的远程也通用,近战也一样- -就是射程掌握好即可)
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(BehaviorOfAni))]
public class Archer : BehaviorAutoAttack {
BehaviorOfAni ani;
[SerializeField]
float firingRange = 1.5f; // 射程
protected override void Start () {
init();
}
protected override void init() {
base.init();
ani = this.GetComponent<BehaviorOfAni>();
this.move.setStoppingDistance(firingRange);
}
protected override void attack() {
ani.beAttack();
}
protected override void run() {
ani.beRun();
}
protected override void idle() {
ani.beIdle();
}
void FixedUpdate () {
doThing();
}
}
测试
可以看到弓箭手自动去攻击另外一个弓箭手