Unity3D学习笔记(二十四)敌人AI,第2版

距离开始写Unity学习笔记已经过去4个月了,回过头来看看写下的笔记,竟然也有个二十多章的积累。可惜国庆回家忘了把练习用的工程文件拷贝到笔记本电脑里,倒是一头栽在Photon Server里研究了好几天。不知道各位同学国庆期间玩的可好,有没有被堵在马路上打网球呢:)

今天准备把之前做的敌人AI部分丰富一下(待命、攻击、死亡、归位、重生),顺便把怪物的模型也一并导进来,加上动画和音效,作为第二版。

▇ 知识点

(GameObjectinstance).SendMessage(),调用当前对象上所有组件上的某个方法;

本章几乎没有新的知识点,只是把以前学过的东西整合起来。看起来代码量有点多,其实还有很多可以精简优化的地方。

▇ 思路


  • 新建一个CreatureSound的脚本,由它负责游戏里生物声音的播放;
  • 新建一个CreatureAnimation的脚本,由它负责游戏里生物动画的播放;
  • 更新已有的Creature脚本,添加Attack和UnderAttack方法,用来控制攻击和被攻击效果;
  • 新建一个EnemyAI的脚本,在其中维护一个EnermyState枚举,并在Update中根据这个成员变量来实现不同状态下的行为;
  • 新建一个空游戏对象,并重命名为Wolf,为它添加以上四个脚本。导入一个WOW中狼的模型,添加到Wolf中作为子对象;

最后一步中的模型可以任意选择,总之完成后记得保存到一个Prefab中,这样就可以快速创建新的怪物了。

▇ EnermyAI.cs


  1. using UnityEngine;
  2. using System.Collections;

  3. [RequireComponent(typeof(CharacterController))]
  4. [RequireComponent(typeof(Creature))]
  5. [RequireComponent(typeof(CreatureAnimation))]
  6. public class EnemyAI : MonoBehaviour
  7. {
  8. /// <summary>
  9. /// 敌人状态
  10. /// </summary>
  11. public enum EnermyState
  12. {
  13. /// <summary>
  14. /// 重生
  15. /// </summary>
  16. Respawn,
  17. /// <summary>
  18. /// 待命
  19. /// </summary>
  20. Idle,
  21. /// <summary>
  22. /// 攻击
  23. /// </summary>
  24. Attack,
  25. /// <summary>
  26. /// 逃跑
  27. /// </summary>
  28. RunAway,
  29. /// <summary>
  30. /// 死亡
  31. /// </summary>
  32. Dead,
  33. /// <summary>
  34. /// 归位
  35. /// </summary>
  36. Homing,
  37. }

  38. /// <summary>
  39. /// 移动速度
  40. /// </summary>
  41. public float MoveSpeed = 4;

  42. /// <summary>
  43. /// 旋转速度
  44. /// </summary>
  45. public float RotateSpeed = 10;

  46. /// <summary>
  47. /// 攻击速度
  48. /// </summary>
  49. public float AttackSpeed = 1.0f;

  50. /// <summary>
  51. /// 攻击范围
  52. /// </summary>
  53. public float AttackRange = 2.0f;

  54. /// <summary>
  55. /// 视野距离
  56. /// </summary>
  57. public float SightRange = 10.0f;

  58. /// <summary>
  59. /// 重生时间
  60. /// </summary>
  61. public float RespawnTime = 10.0f;

  62. /// <summary>
  63. /// 状态
  64. /// </summary>
  65. public EnermyState State = EnermyState.Idle;

  66. private float mAttackCooldown;
  67. private float mRespawnTimer;
  68. private Vector3 mHomePosition;

  69. private Transform mTargetTransform;
  70. private CharacterController mController;
  71. private Creature mCreature;
  72. private CreatureAnimation mCreatureAnimation;

  73. void Awake()
  74. {
  75. //初始化
  76. mTargetTransform = GameManager.Instance.Player.transform;
  77. mController = GetComponent<CharacterController>();
  78. mHomePosition = transform.position;
  79. mCreature = GetComponent<Creature>();
  80. mCreatureAnimation = GetComponent<CreatureAnimation>();
  81. }

  82. void Update()
  83. {
  84. //攻击冷却计时
  85. mAttackCooldown -= Time.deltaTime;
  86. if (mAttackCooldown <= 0)
  87. mAttackCooldown = 0;

  88. //状态机
  89. switch (State)
  90. {
  91. case EnermyState.Respawn:
  92. respawn();
  93. break;
  94. case EnermyState.Idle:
  95. idle();
  96. break;
  97. case EnermyState.Attack:
  98. attack();
  99. break;
  100. case EnermyState.RunAway:
  101. break;
  102. case EnermyState.Dead:
  103. dead();
  104. break;
  105. case EnermyState.Homing:
  106. homing();
  107. break;
  108. default:
  109. break;
  110. }

  111. }

  112. private void respawn()
  113. {
  114. //重生
  115. transform.position = mHomePosition;
  116. mCreature.Health = mCreature.MaxHealth;
  117. State = EnermyState.Idle;
  118. }
  119. private void idle()
  120. {
  121. //获取和玩家之间的距离
  122. var distance = Vector3.Distance(transform.position, mTargetTransform.position);
  123. if (distance <= SightRange)
  124. {
  125. //若在警觉范围内就主动攻击
  126. State = EnermyState.Attack;
  127. }
  128. mCreatureAnimation.PlayIdle();
  129. }
  130. private void attack()
  131. {
  132. //获取和玩家之间的距离
  133. var distance = Vector3.Distance(transform.position, mTargetTransform.position);
  134. if (distance <= AttackRange)
  135. {
  136. //在攻击范围内:面朝玩家
  137. var lookRotation = Quaternion.LookRotation(mTargetTransform.position - transform.position);
  138. transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, RotateSpeed * Time.deltaTime);

  139. //攻击已冷却:攻击
  140. if (mAttackCooldown == 0)
  141. {
  142. mCreature.Attack(mTargetTransform.GetComponent<Creature>());
  143. mAttackCooldown = AttackSpeed;
  144. }
  145. else
  146. {
  147. mCreatureAnimation.PlayAttackReady();
  148. }
  149. }
  150. else if (distance < SightRange)
  151. {
  152. //在视野范围内:冲向玩家
  153. runTo(mTargetTransform.position);
  154. }
  155. else
  156. {
  157. //超出距离:归位
  158. State = EnermyState.Homing;
  159. }
  160. }
  161. private void dead()
  162. {
  163. //死亡
  164. if (mRespawnTimer == 0)
  165. {
  166. SendMessage("OnDead");
  167. mCreatureAnimation.PlayDead();
  168. }
  169. mRespawnTimer += Time.deltaTime;
  170. if (mRespawnTimer > RespawnTime)
  171. {
  172. //死亡一定时间后重生
  173. State = EnermyState.Respawn;
  174. }
  175. }
  176. private void homing()
  177. {
  178. //归位
  179. var distance = Vector3.Distance(mHomePosition, transform.position);
  180. if (distance > 1)
  181. {
  182. runTo(mHomePosition);
  183. }
  184. else
  185. {
  186. State = EnermyState.Idle;
  187. }
  188. }
  189. private void runTo(Vector3 iPosition)
  190. {
  191. //移动至指定位置
  192. mCreatureAnimation.PlayRun();
  193. var lookRotation = Quaternion.LookRotation(iPosition - transform.position);
  194. transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, RotateSpeed * Time.deltaTime);
  195. mController.SimpleMove(lookRotation * Vector3.forward * MoveSpeed);
  196. }
  197. }
复制代码

▇ Creature.cs

  1. using UnityEngine;
  2. using System.Collections;

  3. /// <summary>
  4. /// 生物组件
  5. /// </summary>
  6. public class Creature : MonoBehaviour
  7. {
  8. /// <summary>
  9. /// 名称
  10. /// </summary>
  11. public string Name;

  12. /// <summary>
  13. /// 等级
  14. /// </summary>
  15. public int Level;

  16. /// <summary>
  17. /// 攻击力
  18. /// </summary>
  19. public int AttackPower;

  20. /// <summary>
  21. /// 护甲
  22. /// </summary>
  23. public int Armor;

  24. /// <summary>
  25. /// 生命
  26. /// </summary>
  27. public float Health;

  28. /// <summary>
  29. /// 最大生命值
  30. /// </summary>
  31. public float MaxHealth;

  32. /// <summary>
  33. /// 能量
  34. /// </summary>
  35. public float Power;

  36. /// <summary>
  37. /// 最大能量值
  38. /// </summary>
  39. public float MaxPower;

  40. /// <summary>
  41. /// 经验值
  42. /// </summary>
  43. public float Exp;

  44. /// <summary>
  45. /// 最大经验值
  46. /// </summary>
  47. public float MaxExp;

  48. /// <summary>
  49. /// 目标生物
  50. /// </summary>
  51. public Creature Target
  52. {
  53. get
  54. {
  55. return mTarget;
  56. }
  57. set
  58. {
  59. mTarget = value;
  60. }
  61. }

  62. private Creature mTarget;

  63. void Start()
  64. {

  65. }

  66. void Update()
  67. {

  68. }

  69. /// <summary>
  70. /// 攻击目标
  71. /// </summary>
  72. /// <param name="iTarget">目标</param>
  73. public void Attack(Creature iTarget)
  74. {
  75. SendMessage("OnAttack");
  76. iTarget.UnderAttack(this);
  77. }

  78. /// <summary>
  79. /// 受到攻击
  80. /// </summary>
  81. /// <param name="iSource">攻击来源</param>
  82. protected void UnderAttack(Creature iSource)
  83. {
  84. var damage = iSource.AttackPower - iSource.Armor;
  85. if (damage <= 0)
  86. damage = 1;
  87. Health -= damage;
  88. Debug.Log(string.Format("{0}->{1} damage={2}", iSource, this, damage));
  89. SendMessage("OnWounded");
  90. if (Health <= 0)
  91. {
  92. Health = 0;
  93. SendMessage("OnDead");
  94. }
  95. }
  96. }
复制代码

▇ CreatureAnimation.cs


  1. using UnityEngine;
  2. using System.Collections;

  3. public class CreatureAnimation : MonoBehaviour
  4. {
  5. #region AnimationNameSetting

  6. /// <summary>
  7. /// 待命动画名称
  8. /// </summary>
  9. public string IdleAnimationName = "Stand";

  10. /// <summary>
  11. /// 行走动画名称
  12. /// </summary>
  13. public string WalkAnimationName = "Walk";

  14. /// <summary>
  15. /// 左转动画名称
  16. /// </summary>
  17. public string ShuffleLeftAnimationName = "ShuffleLeft";

  18. /// <summary>
  19. /// 右转动画名称
  20. /// </summary>
  21. public string ShuffleRightAnimationName = "ShuffleRight";

  22. /// <summary>
  23. /// 后退动画名称
  24. /// </summary>
  25. public string WalkBackwardAnimationName = "Walkbackwards";

  26. /// <summary>
  27. /// 跑步动画名称
  28. /// </summary>
  29. public string RunAnimationName = "Run";

  30. /// <summary>
  31. /// 跳跃动画名称
  32. /// </summary>
  33. public string JumpStartAnimationName = "JumpStart";

  34. /// <summary>
  35. /// 跳跃结束继续跑动动画名称
  36. /// </summary>
  37. public string JumpLandRunAnimationName = "JumpLandRun";

  38. /// <summary>
  39. /// 跳跃结束动画名称
  40. /// </summary>
  41. public string JumpEndAnimationName = "JumpEnd";

  42. /// <summary>
  43. /// 游泳待命动画名称
  44. /// </summary>
  45. public string SwimIdleAnimationName = "SwimIdle";

  46. /// <summary>
  47. /// 向左游泳动画名称
  48. /// </summary>
  49. public string SwimLeftAnimationName = "SwimLeft";

  50. /// <summary>
  51. /// 向右游泳动画名称
  52. /// </summary>
  53. public string SwimRightAnimationName = "SwimRight";

  54. /// <summary>
  55. /// 向前游泳动画名称
  56. /// </summary>
  57. public string SwimForwardAnimationName = "Swim";

  58. /// <summary>
  59. /// 后退游泳动画名称
  60. /// </summary>
  61. public string SwimBackwardAnimationName = "SwimBackward";

  62. /// <summary>
  63. /// 下落动画名称
  64. /// </summary>
  65. public string FallAnimationName = "Fall";

  66. /// <summary>
  67. /// 攻击动画名称
  68. /// </summary>
  69. public string AttackUnarmedAnimationName = "AttackUnarmed";

  70. /// <summary>
  71. /// 准备攻击动画名称
  72. /// </summary>
  73. public string ReadyUnarmedAnimationName = "ReadyUnarmed";

  74. /// <summary>
  75. /// 死亡动画名称
  76. /// </summary>
  77. public string DeadAnimationName = "Death";

  78. #endregion

  79. /// <summary>
  80. /// 获取或者设置动画的对象
  81. /// </summary>
  82. public Animation AnimationTarget;

  83. /// <summary>
  84. /// 播放待命状态动画
  85. /// </summary>
  86. public void PlayIdle()
  87. {
  88. playAnimation(IdleAnimationName);
  89. }

  90. /// <summary>
  91. /// 播放左转动画
  92. /// </summary>
  93. public void PlayShuffleLeft()
  94. {
  95. playAnimation(ShuffleLeftAnimationName);
  96. }

  97. /// <summary>
  98. /// 播放右转动画
  99. /// </summary>
  100. public void PlayShuffleRight()
  101. {
  102. playAnimation(ShuffleRightAnimationName);
  103. }

  104. /// <summary>
  105. /// 播放行走动画
  106. /// </summary>
  107. public void PlayWalk()
  108. {
  109. playAnimation(WalkAnimationName);
  110. }

  111. /// <summary>
  112. /// 播放后退动画
  113. /// </summary>
  114. public void PlayWalkBackward()
  115. {
  116. playAnimation(WalkAnimationName, 0.5f);
  117. }

  118. /// <summary>
  119. /// 播放向前游泳的动画
  120. /// </summary>
  121. public void PlaySwimForward()
  122. {
  123. playAnimation(SwimForwardAnimationName);
  124. }

  125. /// <summary>
  126. /// 播放向后游泳的动画
  127. /// </summary>
  128. public void PlaySwimBackward()
  129. {
  130. playAnimation(SwimBackwardAnimationName);
  131. }

  132. /// <summary>
  133. /// 播放游泳待机的动画
  134. /// </summary>
  135. public void PlaySwimIdle()
  136. {
  137. playAnimation(SwimIdleAnimationName);
  138. }

  139. /// <summary>
  140. /// 播放跑步动画
  141. /// </summary>
  142. public void PlayRun()
  143. {
  144. playAnimation(RunAnimationName);
  145. }

  146. /// <summary>
  147. /// 播放跳跃动画
  148. /// </summary>
  149. public void PlayJumpStart()
  150. {
  151. playAnimation(JumpStartAnimationName, 1.0f, WrapMode.ClampForever);
  152. }

  153. /// <summary>
  154. /// 播放跳跃动画
  155. /// </summary>
  156. public void PlayJump()
  157. {
  158. playAnimation(FallAnimationName);
  159. }

  160. /// <summary>
  161. /// 播放跳跃结束后继续奔跑的动画
  162. /// </summary>
  163. public void PlayJumpLandRun()
  164. {
  165. playAnimation(JumpLandRunAnimationName);
  166. }

  167. /// <summary>
  168. /// 播放跳跃结束动画
  169. /// </summary>
  170. public void PlayJumpEnd()
  171. {
  172. playAnimation(JumpEndAnimationName, 1.0f, WrapMode.ClampForever);
  173. }

  174. /// <summary>
  175. /// 播放下落动画
  176. /// </summary>
  177. public void PlayFall()
  178. {
  179. playAnimation(FallAnimationName);
  180. }

  181. /// <summary>
  182. /// 播放攻击等待中的动画
  183. /// </summary>
  184. public void PlayAttackReady()
  185. {
  186. if (!isPlaying(AttackUnarmedAnimationName))
  187. playAnimation(IdleAnimationName);
  188. }

  189. /// <summary>
  190. /// 播放死亡动画
  191. /// </summary>
  192. public void PlayDead()
  193. {
  194. playAnimation(DeadAnimationName, 1.0f, WrapMode.ClampForever);
  195. }

  196. /// <summary>
  197. /// 获取一个值,表示是否正在播放指定动画
  198. /// </summary>
  199. /// <param name="iAnimationName">动画名称</param>
  200. /// <returns></returns>
  201. public bool IsPlaying(string iAnimationName)
  202. {
  203. return isPlaying(iAnimationName);
  204. }

  205. /// <summary>
  206. /// 当生物攻击时调用此方法
  207. /// </summary>
  208. public void OnAttack()
  209. {
  210. playAnimation(AttackUnarmedAnimationName);
  211. }

  212. private void playAnimation(string iAnimationName, float iSpeed = 1.0f, WrapMode iWarpMode = WrapMode.Loop)
  213. {
  214. //播放指定动画(必须存在且未在播放状态)
  215. if (AnimationTarget == null)
  216. throw new MissingReferenceException("AnimationTarget");
  217. if (string.IsNullOrEmpty(iAnimationName) || AnimationTarget[iAnimationName] == null)
  218. throw new UnityException(string.Format("The animation state '{0}' could not be played because it couldn't be found!", iAnimationName));

  219. var isPlaying = AnimationTarget.IsPlaying(iAnimationName);
  220. if (!isPlaying)
  221. {
  222. AnimationTarget.wrapMode = iWarpMode;
  223. AnimationTarget[iAnimationName].speed = iSpeed;
  224. AnimationTarget.CrossFade(iAnimationName);
  225. }
  226. }
  227. private bool isPlaying(string iAnimationName)
  228. {
  229. //获取一个值,表示某个动画是否正在播放
  230. if (string.IsNullOrEmpty(iAnimationName))
  231. return false;
  232. var isPlaying = AnimationTarget.IsPlaying(iAnimationName);
  233. return isPlaying;
  234. }
  235. }
复制代码

▇ CreatureSound.cs


  1. using UnityEngine;
  2. using System.Collections;

  3. [RequireComponent(typeof(AudioSource))]
  4. public class CreatureSound : MonoBehaviour
  5. {
  6. /// <summary>
  7. /// 攻击音效
  8. /// </summary>
  9. public AudioClip AttackClip;

  10. /// <summary>
  11. /// 受伤音效
  12. /// </summary>
  13. public AudioClip WoundedClip;

  14. /// <summary>
  15. /// 发现敌人音效
  16. /// </summary>
  17. public AudioClip ArrgoClip;

  18. /// <summary>
  19. /// 死亡音效
  20. /// </summary>
  21. public AudioClip DeadClip;

  22. private AudioSource mAudioSource;

  23. void Start()
  24. {
  25. mAudioSource = GetComponent<AudioSource>();
  26. mAudioSource.rolloffMode = AudioRolloffMode.Linear;
  27. }

  28. void Update()
  29. {

  30. }

  31. /// <summary>
  32. /// 当生物受到攻击时调用此方法
  33. /// </summary>
  34. public void OnAttack()
  35. {
  36. playSound(AttackClip);
  37. }

  38. /// <summary>
  39. /// 当生物发现目标并进入攻击状态时调用此方法
  40. /// </summary>
  41. public void OnArrgo()
  42. {
  43. playSound(ArrgoClip);
  44. }

  45. /// <summary>
  46. /// 当生物受伤时调用此方法
  47. /// </summary>
  48. public void OnWounded()
  49. {
  50. playSound(WoundedClip);
  51. }

  52. /// <summary>
  53. /// 当生物死亡时调用此方法
  54. /// </summary>
  55. public void OnDead()
  56. {
  57. playSound(DeadClip);
  58. }

  59. private void playSound(AudioClip iAudioClip)
  60. {
  61. //播放指定声音
  62. if (iAudioClip == null)
  63. return;
  64. mAudioSource.clip = iAudioClip;
  65. mAudioSource.Play();
  66. }
  67. }
复制代码

▇ 实现效果

 
2013-4-25 11:02:10 上传
下载附件  (419.99 KB)


狼来啦!!!:P

你可能感兴趣的:(Unity3D学习笔记(二十四)敌人AI,第2版)