Unity C# 操控行为类
备注:因为这篇博客是根据《游戏人工智能编程案例精粹》写的,而这本书是由c++写的。所以在这篇文章里只是有ai的逻辑,但并没有很好地用在Unity身上。后来我发现了一本书,也是根据《游戏人工智能编程案例精粹》这本书写的,而且专门是为了Unity写的。因此下面的代码理解一下就好,实际应用的话可以参考那本书的第一章。那本书就是《Unity 5.x Game AI Programming Cookbook(PACKT,2016)》 网上有pdf版下载。—— 2017/06/16
在游戏中,操控一个对象的行为是很必要的操作。今天来将之前学的《游戏人工智能编程案例精粹》改成C#版供大家参考参考。
我就直接放代码和项目了,想了解为甚么这么写可以直接看这本书或者看代码注释然后自己画图理解,最好看书吧~书就是最好的学习笔记。
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 5 public enum MoverState 6 { 7 Null, 8 Seek, 9 Flee, 10 Arrive, 11 Pursuit, 12 Evade, 13 Wander, 14 Interpose, 15 Hiding, 16 OffsetPursuit 17 } 18 19 public class MovingEntity : MonoBehaviour 20 { 21 /// <summary> 22 /// 当前智能体的速度 23 /// </summary> 24 [HideInInspector] 25 public Vector3 m_vVelocity; 26 27 /// <summary> 28 /// 当前智能体的位置信息 29 /// </summary> 30 [HideInInspector] 31 public Transform selfTransform; 32 33 /// <summary> 34 /// 当前智能体所达到的最大速率 35 /// </summary> 36 public float m_fMaxSpeed = 1f; 37 38 /// <summary> 39 /// 徘徊圈半径 40 /// </summary> 41 public float m_fWanderRadius = 10f; 42 /// <summary> 43 /// 徘徊圈凸到智能体前面的距离 44 /// </summary> 45 //public float m_fWanderDistance = 0f; //可不用 46 /// <summary> 47 /// 徘徊圈上的目标位置跳动阈值 48 /// </summary> 49 public float m_fWanderJitter = 50f; 50 /// <summary> 51 /// 徘徊目标 52 /// </summary> 53 public Vector3 m_vWanderTarger = Vector3.zero; 54 /// <summary> 55 /// 徘徊目标时间间隔 56 /// </summary> 57 private float wanderTime = 1.5f; 58 /// <summary> 59 /// 徘徊计时器 60 /// </summary> 61 private float timer; 62 63 /// <summary> 64 /// 当前智能体的行为状态 65 /// </summary> 66 public MoverState state; 67 68 /// <summary> 69 /// 当前与智能体互动的目标 70 /// </summary> 71 public MovingEntity target; 72 public MovingEntity targetB; 73 public MovingEntity leader; 74 public Vector3 offsetPos; 75 76 /// <summary> 77 /// 当前智能体的操控行为类 78 /// </summary> 79 SteeringBehavior m_pSteering; 80 81 /// <summary> 82 /// 用来测试得到智能体的隐藏点 83 /// </summary> 84 Vector3 posTarget; 85 86 private void Awake() 87 { 88 m_pSteering = new SteeringBehavior(this); 89 selfTransform = this.transform; 90 timer = wanderTime; 91 posTarget = selfTransform.position; 92 } 93 94 private void Update() 95 { 96 switch (state) 97 { 98 case MoverState.Null: 99 break; 100 case MoverState.Seek: 101 Vector3 seekVel = m_pSteering.Seek(target.selfTransform.position); 102 selfTransform.Translate(seekVel * Time.deltaTime); 103 // 图形调试:画出智能体移动的方向 104 Debug.DrawRay(selfTransform.position, seekVel * 10f, Color.green); 105 break; 106 case MoverState.Flee: 107 Vector3 fleeVel = m_pSteering.Flee(target.selfTransform.position); 108 selfTransform.Translate(fleeVel * Time.deltaTime); 109 // 图形调试:画出智能体移动的方向 110 Debug.DrawRay(selfTransform.position, fleeVel * 10f, Color.green); 111 break; 112 case MoverState.Arrive: 113 Vector3 arriveVel = m_pSteering.Arrive(target.selfTransform.position, 1f, 1f); 114 selfTransform.Translate(arriveVel * Time.deltaTime); 115 // 图形调试:画出智能体移动的方向 116 Debug.DrawRay(selfTransform.position, arriveVel * 10f, Color.green); 117 break; 118 case MoverState.Pursuit: 119 Vector3 pursuiteVel = m_pSteering.Pursuit(target); 120 selfTransform.Translate(pursuiteVel * Time.deltaTime); 121 // 图形调试:画出智能体移动的方向 122 Debug.DrawRay(selfTransform.position, pursuiteVel * 10f, Color.green); 123 break; 124 case MoverState.Evade: 125 Vector3 evadeVel = m_pSteering.Evade(target); 126 selfTransform.Translate(evadeVel * Time.deltaTime); 127 // 图形调试:画出智能体移动的方向 128 Debug.DrawRay(selfTransform.position, evadeVel * 10f, Color.green); 129 break; 130 case MoverState.Wander: 131 timer += Time.deltaTime; 132 if (timer >= wanderTime) 133 { 134 m_pSteering.Wander(); 135 timer = 0f; 136 } 137 m_vWanderTarger.y = selfTransform.position.y; 138 Vector3 wanderVel = m_pSteering.Seek(m_vWanderTarger); 139 selfTransform.Translate(wanderVel * Time.deltaTime); 140 // 图形调试:画出智能体移动的方向 141 Debug.DrawRay(selfTransform.position, wanderVel * 10f, Color.green); 142 // 图形调试:画出智能体徘徊范围 143 Debug.DrawLine(selfTransform.position, m_vWanderTarger, Color.red, 1000f); 144 break; 145 case MoverState.Interpose: 146 Vector3 interposeVel = m_pSteering.Interpose(target, targetB); 147 selfTransform.Translate(interposeVel * Time.deltaTime); 148 // 图形调试:画出智能体移动的方向 149 Debug.DrawRay(selfTransform.position, interposeVel * 10f, Color.green); 150 break; 151 case MoverState.Hiding: 152 Vector3 hidingPos = m_pSteering.GetHidingPosition(target.selfTransform.position, 3f, posTarget); 153 Vector3 arriveVel1 = m_pSteering.Arrive(hidingPos, 1f, 1f); 154 selfTransform.Translate(arriveVel1 * Time.deltaTime); 155 // 图形调试:画出智能体移动的方向 156 Debug.DrawRay(selfTransform.position, arriveVel1 * 10f, Color.green); 157 break; 158 case MoverState.OffsetPursuit: 159 Vector3 offsetPursuitVel = m_pSteering.OffsetPursuit(leader,offsetPos); 160 selfTransform.Translate(offsetPursuitVel * Time.deltaTime); 161 // 图形调试:画出智能体移动的方向 162 Debug.DrawRay(selfTransform.position, offsetPursuitVel * 10f, Color.green); 163 break; 164 } 165 } 166 167 private void OnDrawGizmos() 168 { 169 //Gizmos.DrawLine(Vector3.zero,target.position); 170 //Gizmos.DrawWireSphere(Vector3.zero, m_fWanderRadius); 171 } 172 }
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 5 public class SteeringBehavior 6 { 7 /// <summary> 8 /// 操控行为类的操控对象 9 /// </summary> 10 private MovingEntity mover; 11 12 /// <summary> 13 /// SteeringBehavior构造函数,初始化操控行为类 14 /// </summary> 15 /// <param name="mover"></param> 16 public SteeringBehavior(MovingEntity mover) 17 { 18 this.mover = mover; 19 } 20 21 /// <summary> 22 /// 返回靠近目标位置的速度 23 /// </summary> 24 /// <param name="targetPos"></param> 25 /// <returns></returns> 26 public Vector3 Seek(Vector3 targetPos) 27 { 28 // 对靠近目标位置的理想速度 29 Vector3 desiredVel = mover.m_fMaxSpeed * Vector3.Normalize(targetPos - mover.selfTransform.position); 30 // 理想速度-当前速度=结果速度,这样行动方向是收受到当前的速度影响,比较合理 31 return desiredVel - mover.m_vVelocity; 32 } 33 34 /// <summary> 35 /// 返回远离目标位置的速度 36 /// </summary> 37 /// <param name="targetPos"></param> 38 /// <returns></returns> 39 public Vector3 Flee(Vector3 targetPos) 40 { 41 // 对远离目标位置的理想速度 42 Vector3 desiredVel = mover.m_fMaxSpeed * Vector3.Normalize(mover.selfTransform.position - targetPos); 43 // 理想速度-当前速度=结果速度,这样行动方向是收受到当前的速度影响,比较合理 44 return desiredVel - mover.m_vVelocity; 45 } 46 47 /// <summary> 48 /// 根据智能体与目标位置的距离,返回到达目标距离的速度 49 /// 若到达了则速速为Vector3.zero 50 /// </summary> 51 /// <param name="targetPos">目标位置</param> 52 /// <param name="stopDistance">据目标多少米停止移动</param> 53 /// <param name="tweaker">用于调整即将到达目标位置时速率大小,tweaker越大则即将到达时运动速率越小,但有最大速度限制</param> 54 /// <returns></returns> 55 public Vector3 Arrive(Vector3 targetPos, float stopDistance, float tweaker) 56 { 57 // 智能体到目标位置的预期速度 58 Vector3 toTarget = targetPos - mover.selfTransform.position; 59 60 // 智能体与目标位置的距离 61 float dist = toTarget.magnitude; 62 63 // 判读是否到达停止距离 64 if (dist > stopDistance) 65 { 66 // 与目标点距离越近则运动速率越小,tweaker越大则运动速率越小 67 float speed = dist / tweaker; 68 // 确保不超过最大速率 69 speed = Mathf.Min(speed, mover.m_fMaxSpeed); 70 // 类似Seek 71 Vector3 desiredVel = toTarget / dist * speed; 72 return desiredVel - mover.m_vVelocity; 73 } 74 75 return Vector3.zero; 76 } 77 78 /// <summary> 79 /// 返回智能体追逐目标的速度 80 /// </summary> 81 /// <param name="evader">被智能体追逐的逃避者</param> 82 /// <returns></returns> 83 public Vector3 Pursuit(MovingEntity evader) 84 { 85 // 智能体到逃避者的预期速度 86 Vector3 toEvader = evader.selfTransform.position - mover.selfTransform.position; 87 88 // 智能体与逃避者前方向的点积运算 89 float relativeHeading = Vector3.Dot(evader.selfTransform.forward,mover.selfTransform.forward); 90 91 //这里视角判定逃避者是否在智能体前面视角范围36度左右的 92 if (relativeHeading < -0.95 //acos(0.95) = 18degs 93 && Vector3.Dot(toEvader, mover.selfTransform.forward) > 0) //智能体朝向与预期速度之间的夹角 大于0为锐角 所以逃避者肯定不会在智能体朝向的后面 94 { 95 //确定逃避者在智能体前面36°视角范围,直接Seek 96 return Seek(evader.selfTransform.position); 97 } 98 99 //预估相遇时间 相遇时间是和两者距离成正比,和追逐者速率和逃避者速率成反比 100 float LookAheadTime = toEvader.magnitude / (mover.m_fMaxSpeed + evader.m_fMaxSpeed); 101 102 //靠近相遇地点(逃避者的被预测位置) 103 return Seek(evader.selfTransform.position + evader.m_vVelocity * LookAheadTime); 104 } 105 106 /// <summary> 107 /// 返回智能体逃避追逐者的速度 108 /// </summary> 109 /// <param name="pursuer">追逐智能体的追逐者</param> 110 /// <returns></returns> 111 public Vector3 Evade(MovingEntity pursuer) 112 { 113 /*不需要检查是否面向*/ 114 115 Vector3 toPursuer = pursuer.selfTransform.position - mover.selfTransform.position; 116 117 //预估相遇时间 118 float LookAheadTime = toPursuer.magnitude / (mover.m_fMaxSpeed + pursuer.m_fMaxSpeed); 119 //逃离相遇地点 120 return Flee(pursuer.selfTransform.position + pursuer.m_vVelocity * LookAheadTime); 121 } 122 123 /// <summary> 124 /// 返回在徘徊圈位置的速度 125 /// </summary> 126 /// <returns></returns> 127 public void Wander() 128 { 129 // 给目标点增加随机位移 130 mover.m_vWanderTarger += Random.insideUnitSphere * mover.m_fWanderJitter; 131 132 // 目标点回归到圆上 133 mover.m_vWanderTarger.Normalize(); 134 mover.m_vWanderTarger *= mover.m_fWanderRadius; 135 } 136 137 /// <summary> 138 /// 返回插入两个智能体之间的到达速度 139 /// </summary> 140 /// <param name="agentA"></param> 141 /// <param name="agentB"></param> 142 /// <returns></returns> 143 public Vector3 Interpose(MovingEntity agentA, MovingEntity agentB) 144 { 145 // 插入的两个智能体之间的位置的中点 146 Vector3 MidPoint = (agentA.selfTransform.position + agentB.selfTransform.position) / 2.0f; 147 148 // 计算当前智能体到达MidPoint的时间 149 float TimeToReachMinPoint = Vector3.Distance(mover.selfTransform.position, MidPoint) / mover.m_fMaxSpeed; 150 151 // 假设AB两个智能体在时间T继续前行 152 Vector3 APos = agentA.selfTransform.position + agentA.m_vVelocity * TimeToReachMinPoint; 153 Vector3 BPos = agentB.selfTransform.position + agentB.m_vVelocity * TimeToReachMinPoint; 154 155 // 重新计算中点 156 MidPoint = (APos + BPos) / 2.0f; 157 158 return Arrive(MidPoint, 0f, 1f); 159 160 } 161 162 /// <summary> 163 /// 得到目标位置到障碍物之间的隐藏点 164 /// </summary> 165 /// <param name="posOb"></param> 166 /// <param name="radiusOb"></param> 167 /// <param name="posTarget"></param> 168 /// <returns></returns> 169 public Vector3 GetHidingPosition(Vector3 posOb, float radiusOb, Vector3 posTarget) 170 { 171 // 计算从目标到物体的朝向 172 Vector3 toOb = Vector3.Normalize(posOb - posTarget); 173 174 // 确定大小并加到障碍物的位置,得到隐藏点 175 return (toOb * radiusOb) + posOb; 176 } 177 178 /// <summary> 179 /// 偏移追逐 180 /// </summary> 181 /// <param name="leader">追逐的领头者</param> 182 /// <param name="offset">与领头者之间的偏移(间隔)</param> 183 /// <returns></returns> 184 public Vector3 OffsetPursuit(MovingEntity leader, Vector3 offset) 185 { 186 //在世界空间中计算偏移的位置 187 Vector3 worldOffsetPos = leader.selfTransform.TransformPoint(offset); 188 Vector3 toOffset = worldOffsetPos - mover.selfTransform.position; 189 190 //预期时间 与领头者和追逐者的距离成正比,与速度之和成反比 191 float lookAheadTime = toOffset.magnitude / (mover.m_fMaxSpeed + leader.m_vVelocity.magnitude); 192 193 //偏移的预测位置 194 return Arrive(worldOffsetPos + leader.m_vVelocity * lookAheadTime, 0f, 1f); 195 } 196 }
测试项目地址:https://git.oschina.net/TMoon-Net/SteeringBehavior