Yeji.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.AI;
  5. public class Yeji : TargetAnimal
  6. {
  7. //动画播放器
  8. AnimationPlayer ap;
  9. //寻路代理
  10. NavMeshAgent agent;
  11. //血量
  12. [System.NonSerialized] public int hp = 3;
  13. [System.NonSerialized] public TreeAreaRecorder treeAreaRecorder;
  14. [System.NonSerialized] public float flyPlaneHeight = 5;
  15. float currentHeight = 0;
  16. public void SetFlyHeight(float value) {
  17. currentHeight = value;
  18. float agentBaseOffset = value - flyPlaneHeight;
  19. this.agent.baseOffset = agentBaseOffset / 2;//因为agent的baseoffset会受节点的scale影响
  20. }
  21. void Awake()
  22. {
  23. ap = GetComponent<AnimationPlayer>();
  24. agent = GetComponent<NavMeshAgent>();
  25. this.agent.avoidancePriority = avoidancePriority;
  26. }
  27. static int _avoidancePriority = 0;
  28. static int avoidancePriority {
  29. get {
  30. if (_avoidancePriority < 50) {
  31. _avoidancePriority++;
  32. } else {
  33. _avoidancePriority = 1;
  34. }
  35. return _avoidancePriority;
  36. }
  37. }
  38. void Start()
  39. {
  40. //初始化自动策略
  41. InitAutoStrategy();
  42. }
  43. void Update()
  44. {
  45. //寻路过程监测
  46. if (HasCloseToDestination()) {
  47. OnReachDestination();
  48. } else {
  49. OnMovingToDestination();
  50. }
  51. UpdateAction();
  52. UpdateActionTime();
  53. UpdateAutoStrategy();
  54. }
  55. public void Disappear() {
  56. Destroy(this.gameObject);
  57. }
  58. public override void OnHit(Arrow arrow, Vector3 hitPoint, string partName)
  59. {
  60. arrow.Head().position = hitPoint + arrow.transform.forward * 0.1f;
  61. arrow.Hit();
  62. if (partName == "Wing") {
  63. hp -= 1;
  64. this.agent.speed /= 2f;
  65. }
  66. else if (partName == "Body") hp -= 2;
  67. else if (partName == "Head") hp -= 3;
  68. if (hp <= 0) {
  69. Die(arrow);
  70. } else {
  71. Hurt();
  72. }
  73. }
  74. void Die(Arrow arrow) {
  75. if (dead) return;
  76. arrow.onDoNextShoot += delegate() {
  77. Disappear();
  78. };
  79. dead = true;
  80. this.agent.enabled = false;
  81. PuaseAutoStrategy();
  82. onDie?.Invoke(this);
  83. AudioMgr.ins.PlayAnimalEffect("bird_die", AudioMgr.GetAudioSource(this.gameObject));
  84. AudioMgr.ins.PlayCheer(true);
  85. if (currentHeight < 1.5f) {
  86. arrow.arrowCameraComp.arrowCameraTemplate.SendMsg(0, null);
  87. }
  88. }
  89. void Hurt() {
  90. CancelStand();
  91. AudioMgr.ins.PlayAnimalEffect("bird_injured", AudioMgr.GetAudioSource(this.gameObject));
  92. }
  93. //站立状态
  94. bool standing = false;
  95. //是否已经死亡
  96. [System.NonSerialized] public bool dead = false;
  97. //是否处于移动状态
  98. bool _moving = false;
  99. bool moving {
  100. get {
  101. return _moving;
  102. }
  103. set {
  104. if (_moving != value) {
  105. movingTime = 0;
  106. stayingTime = 0;
  107. }
  108. _moving = value;
  109. }
  110. }
  111. //实时记录“单次移动的持续时间”
  112. float movingTime = 0;
  113. //实时停留时间
  114. float stayingTime = 0;
  115. //启动寻路
  116. void SetDestination(Vector3 pos) {
  117. moving = true;
  118. this.agent.destination = pos;
  119. }
  120. //寻路过程
  121. void OnMovingToDestination() {
  122. if (!moving) return;
  123. }
  124. //寻路结束
  125. void OnReachDestination() {
  126. if (!moving) return;
  127. moving = false;
  128. }
  129. //是否已经接近目的地(寻路完成判断)
  130. bool HasCloseToDestination() {
  131. if (!moving) return true;
  132. if (movingTime > 0.1) {
  133. if (this.agent.velocity.magnitude < 0.05f) {
  134. return true;
  135. }
  136. }
  137. return false;
  138. }
  139. //更新动作
  140. void UpdateAction() {
  141. if (dead) {
  142. if (!isAniDie()) playAniDie();
  143. } else if (standing) {
  144. if (!isAniStand()) playAniStand();
  145. } else {
  146. if (!isAniFly()) playAniFly();
  147. }
  148. }
  149. //记录动作时间
  150. void UpdateActionTime() {
  151. if (moving) {
  152. movingTime += Time.deltaTime;
  153. } else {
  154. stayingTime += Time.deltaTime;
  155. }
  156. }
  157. bool autoStrategy = true;
  158. //自动策略的更新逻辑
  159. void UpdateAutoStrategy() {
  160. if (!autoStrategy) return;
  161. RandomMove();
  162. }
  163. void InitAutoStrategy() {
  164. autoStrategy = true;
  165. }
  166. void PuaseAutoStrategy() {
  167. autoStrategy = false;
  168. }
  169. //逃跑远离猎人
  170. void RunAwayFromHunter()
  171. {
  172. Vector3 backVec = GetPointerHunterToMe();
  173. SetDestination(transform.position + backVec.normalized * 5);
  174. }
  175. float toFlyHeight = 1.5f;
  176. float canStandTime = 3f;
  177. bool toUp = false;
  178. bool toLand = false;
  179. //能够在前几棵树徘徊
  180. [System.NonSerialized] public int canFlyTreeCount = 4;
  181. //将会绕树转
  182. bool willMoveRoundTree = false;
  183. Queue<Vector3> roundTreePostions = new Queue<Vector3>();
  184. //随机移动
  185. void RandomMove() {
  186. if (dead) return;
  187. if (moving) {
  188. if (toUp) {
  189. float nextFlyHeight = currentHeight + Time.deltaTime * 1f;
  190. if (nextFlyHeight > toFlyHeight) {
  191. nextFlyHeight = toFlyHeight;
  192. toUp = false;
  193. }
  194. SetFlyHeight(nextFlyHeight);
  195. return;
  196. }
  197. if (toLand) {
  198. float nextH = currentHeight - Time.deltaTime * 1.5f;
  199. if (nextH < 0) {
  200. nextH = 0;
  201. toLand = false;
  202. //进入站立状态
  203. standing = true;
  204. canStandTime = 2f + Random.value * 3f;
  205. Stay();
  206. }
  207. SetFlyHeight(nextH);
  208. return;
  209. }
  210. return;
  211. }
  212. if (standing) {
  213. canStandTime -= Time.deltaTime;
  214. if (canStandTime <= 0) {
  215. CancelStand();
  216. }
  217. return;
  218. }
  219. if (willMoveRoundTree && roundTreePostions.Count > 0) {
  220. Vector3 pos = roundTreePostions.Dequeue();
  221. SetDestination(pos);
  222. return;
  223. } else {
  224. willMoveRoundTree = false;
  225. }
  226. bool isOnLand = currentHeight < 0.001f;
  227. if (!isOnLand) {
  228. if (Random.value < 0.5) {
  229. toLand = true;
  230. return;
  231. }
  232. }
  233. //规划下一次行动路线
  234. Vector3 myPos = this.transform.position;
  235. TreeAreaRecorder.AreaInfo[] areaInfos = treeAreaRecorder.copyAreaInfos(canFlyTreeCount);
  236. for (int i = 0; i < canFlyTreeCount; i++) {
  237. areaInfos[i].teamCompareValue = areaInfos[i].distanceInHorizontal(myPos);
  238. }
  239. System.Array.Sort(areaInfos, new ComparerAreaInfosByDistance());
  240. //如果离这棵树比较近,有几率使其绕着树转
  241. TreeAreaRecorder.AreaInfo firstAreaInfo = areaInfos[0];
  242. bool isNearTree4th = areaInfos.Length == 4 && firstAreaInfo == treeAreaRecorder.getValidAreaInfo(3);
  243. if (!isNearTree4th && firstAreaInfo.teamCompareValue < firstAreaInfo.radius + 1.5f && Random.value < 0.5) {
  244. willMoveRoundTree = true;
  245. Vector3 treePos = areaInfos[0].transform.position;
  246. treePos.y = myPos.y;
  247. Vector3 pointer = myPos - treePos;
  248. roundTreePostions.Clear();
  249. int direction = Random.value >= 0.5 ? 1 : -1;
  250. for (int i = 1; i <= 6; i++) {
  251. roundTreePostions.Enqueue(treePos + Quaternion.AngleAxis(i * 60 * direction, Vector3.up) * pointer);
  252. }
  253. RandomMove();
  254. return;
  255. }
  256. TreeAreaRecorder.AreaInfo areaInfo = areaInfos[Random.Range(1, canFlyTreeCount)];
  257. Vector3 newPos = YejiHuntGameMode.CalculateNewPosByTreePos(animalsBaseT, flyPlaneHeight, areaInfo);
  258. SetDestination(newPos);
  259. }
  260. class ComparerAreaInfosByDistance : IComparer {
  261. public int Compare(object x, object y) {
  262. TreeAreaRecorder.AreaInfo a = (TreeAreaRecorder.AreaInfo)x;
  263. TreeAreaRecorder.AreaInfo b = (TreeAreaRecorder.AreaInfo)y;
  264. if (a.teamCompareValue - b.teamCompareValue > 0) return 1;
  265. if (a.teamCompareValue - b.teamCompareValue < 0) return -1;
  266. else return 0;
  267. }
  268. }
  269. //停留
  270. void Stay() {
  271. if (moving) {
  272. this.agent.destination = this.agent.nextPosition;
  273. OnReachDestination();
  274. }
  275. }
  276. //取消站在地上
  277. void CancelStand() {
  278. if (standing) {
  279. standing = false;
  280. toUp = true;
  281. toFlyHeight = 1.5f + Random.value * 2.5f;
  282. }
  283. }
  284. //当前动画索引
  285. int curAnimIndex = -1;
  286. void playAniDie() {
  287. ap.play(curAnimIndex = 1, WrapMode.Once);
  288. }
  289. bool isAniDie() {
  290. return curAnimIndex == 1;
  291. }
  292. void playAniFly() {
  293. ap.play(curAnimIndex = 0, WrapMode.Loop);
  294. }
  295. bool isAniFly() {
  296. return curAnimIndex == 0;
  297. }
  298. void playAniStand() {
  299. ap.play(curAnimIndex = 2, WrapMode.Loop);
  300. }
  301. bool isAniStand() {
  302. return curAnimIndex == 2;
  303. }
  304. //委托
  305. public System.Action<Yeji> onDie;
  306. }