Yeji.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.AI;
  5. /* 动物组件-野鸡 */
  6. public class Yeji : TargetAnimal
  7. {
  8. //动画播放器
  9. Animator animator;
  10. //寻路代理
  11. NavMeshAgent agent;
  12. //血量
  13. [System.NonSerialized] public int hp = 3;
  14. [System.NonSerialized] public TreeAreaRecorder treeAreaRecorder;
  15. [System.NonSerialized] public float flyPlaneHeight = 5;
  16. float currentHeight = 0;
  17. float toFlyHeight = float.NaN;
  18. bool _firstSetFlyHeight = true;
  19. public void SetFlyHeight(float value) {
  20. if (toFlyHeight.Equals(float.NaN)) toFlyHeight = value;
  21. currentHeight = value;
  22. float agentBaseOffset = value - flyPlaneHeight;
  23. this.agent.baseOffset = agentBaseOffset / GetScaleY();//因为agent的baseoffset会受节点的scale影响
  24. if (_firstSetFlyHeight) {
  25. _firstSetFlyHeight = false;
  26. state.flying = currentHeight > GetLandHeight();
  27. }
  28. }
  29. private float GetLandHeight() {
  30. if (stoneRecorder.IsStoneLocker(this)) return stoneRecorder.GetStoneYOffLand();
  31. return 0f;
  32. }
  33. float _scaleY = -1;
  34. private float GetScaleY() {
  35. if (_scaleY < 0) _scaleY = transform.localScale.y;
  36. return _scaleY;
  37. }
  38. void Awake()
  39. {
  40. yejiSet.Add(this);
  41. state = new State();
  42. state.animal = this;
  43. animator = GetComponent<Animator>();
  44. agent = GetComponent<NavMeshAgent>();
  45. this.agent.avoidancePriority = avoidancePriority;
  46. this.onlineHandler.InitOnAwake(this);
  47. }
  48. static int _avoidancePriority = 0;
  49. static int avoidancePriority {
  50. get {
  51. if (_avoidancePriority < 50) {
  52. _avoidancePriority++;
  53. } else {
  54. _avoidancePriority = 1;
  55. }
  56. return _avoidancePriority;
  57. }
  58. }
  59. void OnDestroy() {
  60. yejiSet.Remove(this);
  61. tryReleaseOccupyTree();
  62. ReleasePreHeight(this);
  63. stoneRecorder.TryRelease(this);
  64. }
  65. void Update()
  66. {
  67. this.onlineHandler.Update();
  68. if (this.onlineHandler.isMirror) return;
  69. //寻路过程监测
  70. if (HasCloseToDestination()) {
  71. OnReachDestination();
  72. } else {
  73. OnMovingToDestination();
  74. }
  75. UpdateAutoStrategy();
  76. }
  77. #region 被击触发
  78. public override void OnHit(Arrow arrow, Vector3 hitPoint, string partName)
  79. {
  80. arrow.Head().position = hitPoint + arrow.transform.forward * 0.1f;
  81. arrow.Hit();
  82. if (onlineHandler.isMirror) {
  83. onlineHandler.onHitData = onlineHandler.uid.ToString() + "," + partName;
  84. return;
  85. }
  86. OnHitLogic(arrow, partName);
  87. }
  88. public void OnHitLogic(Arrow arrow, string partName) {
  89. int hurtValue = 0;
  90. if (partName == "Wing") {
  91. hurtValue = 1;
  92. }
  93. else if (partName == "Body") hurtValue = 2;
  94. else if (partName == "Head") hurtValue = 3;
  95. hp -= hurtValue;
  96. GameEventCenter.ins.onTargetAnimalHurt?.Invoke(this, hurtValue);
  97. if (hp <= 0) {
  98. Die(arrow);
  99. } else {
  100. Hurt();
  101. }
  102. }
  103. void Die(Arrow arrow) {
  104. if (state.dead) return;
  105. if (arrow != null) {
  106. arrow.onDoNextShoot += delegate() {
  107. Destroy(this.gameObject);
  108. };
  109. } else {
  110. //需要借住关卡的GameMode来清除
  111. onlineHandler.onDoNextShootWillDestroy = true;
  112. }
  113. this.animator.speed = 1;
  114. state.dead = true;
  115. this.agent.enabled = false;
  116. ReleasePreHeight(this);
  117. onDie?.Invoke(this);
  118. AudioMgr.ins.PlayAnimalEffect("bird_injured", AudioMgr.GetAudioSource(this.gameObject));
  119. this.onlineHandler.injuredID++;
  120. if (currentHeight < 1.5f) {
  121. if (arrow != null) arrow.arrowCameraComp.arrowCameraTemplate.SendMsg(0, null);
  122. }
  123. }
  124. void Hurt() {
  125. CancelStand();
  126. CancelFlyStay();
  127. AudioMgr.ins.PlayAnimalEffect("bird_injured", AudioMgr.GetAudioSource(this.gameObject));
  128. this.onlineHandler.injuredID++;
  129. }
  130. #endregion
  131. #region 寻路导航
  132. bool _moving;
  133. bool moving {
  134. get { return _moving; }
  135. set {
  136. movingTime = 0;
  137. _moving = value;
  138. }
  139. }
  140. float movingTime;
  141. //启动寻路
  142. void SetDestination(Vector3 pos) {
  143. moving = true;
  144. this.agent.destination = pos;
  145. }
  146. //寻路过程
  147. void OnMovingToDestination() {
  148. if (!moving) return;
  149. movingTime += Time.deltaTime;
  150. }
  151. //寻路结束
  152. void OnReachDestination() {
  153. if (!moving) return;
  154. moving = false;
  155. }
  156. //是否已经接近目的地(寻路完成判断)
  157. bool HasCloseToDestination() {
  158. if (!moving) return true;
  159. return movingTime > 0.1 && this.agent.velocity.magnitude < 0.05f;
  160. }
  161. //停止寻路
  162. void StopNavigation() {
  163. if (!moving) return;
  164. this.agent.destination = this.agent.nextPosition;
  165. this.agent.velocity = Vector3.zero;
  166. this.transform.position = this.agent.destination;
  167. OnReachDestination();
  168. }
  169. #endregion
  170. #region 自动逻辑
  171. //自动策略的更新逻辑
  172. void UpdateAutoStrategy() {
  173. if (state.dead) return;
  174. UpdateFlyUpDown();
  175. UpdateBehavior();
  176. }
  177. float canStandTime = 3f;
  178. float canFlyStayTime = 3f;
  179. //能够在前几棵树徘徊
  180. [System.NonSerialized] public int canFlyTreeCount = 2;
  181. const float downSpeed = 2.0f;
  182. //更新逻辑-起飞降落
  183. void UpdateFlyUpDown() {
  184. if (state.up && state.landing) { //起飞阶段
  185. AnimatorStateInfo animatorStateInfo = animator.GetCurrentAnimatorStateInfo(0);
  186. bool isTakeOff = animatorStateInfo.IsName("FlyFromGround");
  187. if (isTakeOff) {
  188. if (animatorStateInfo.normalizedTime >= 1.0) {
  189. state.landing = false;
  190. state.flying = true;
  191. }
  192. }
  193. return;
  194. }
  195. if (state.up && state.flying) { //上升阶段
  196. float surplusHeight = toFlyHeight - currentHeight;
  197. float smoothValue = surplusHeight / toFlyHeight;
  198. float nextFlyHeight = currentHeight + Time.deltaTime * (1f + 2.5f * smoothValue);
  199. this.animator.speed = 1f + 2f * smoothValue;
  200. if (nextFlyHeight >= toFlyHeight) { //上升完成
  201. nextFlyHeight = toFlyHeight;
  202. this.animator.speed = 1;
  203. state.up = false;
  204. }
  205. SetFlyHeight(nextFlyHeight);
  206. return;
  207. }
  208. if (state.down && state.flying) { //下降阶段
  209. float nextH = currentHeight - Time.deltaTime * downSpeed;
  210. float minH = GetLandHeight();
  211. if (nextH <= minH) { //下降完成
  212. nextH = minH;
  213. state.landing = true;
  214. state.flying = false;
  215. StopNavigation();
  216. }
  217. SetFlyHeight(nextH);
  218. return;
  219. }
  220. if (state.down && state.landing) { //着落阶段
  221. AnimatorStateInfo animatorStateInfo = animator.GetCurrentAnimatorStateInfo(0);
  222. bool isLand = animatorStateInfo.IsName("LandOnGround");
  223. if (isLand) {
  224. if (animatorStateInfo.normalizedTime >= 1.0) {
  225. state.down = false;
  226. //进入站立状态
  227. state.standing = true;
  228. canStandTime = 8f + Random.value * 2f;
  229. animator.CrossFade("IdleOnGround2", 0.2f);
  230. }
  231. }
  232. return;
  233. }
  234. }
  235. TreeAreaRecorder.AreaInfo targetAreaInfo;
  236. bool needCheckFlyDown = false;
  237. bool willStayOnTree = false;
  238. void UpdateBehavior() {
  239. if (state.flyStaying) {
  240. canFlyStayTime -= Time.deltaTime;
  241. if (canFlyStayTime <= 0) {
  242. state.flyStaying = false;
  243. }
  244. return;
  245. }
  246. if (state.standing) {
  247. canStandTime -= Time.deltaTime;
  248. if (canStandTime <= 0) {
  249. CancelStand();
  250. }
  251. return;
  252. }
  253. if (state.landing) return;
  254. if (state.flying && !state.down && !state.up) {
  255. if (needCheckFlyDown) {
  256. float downNeedTime = currentHeight / downSpeed;
  257. float keyDistance = this.agent.speed * downNeedTime; //触发下降的水平距离阈值
  258. Vector3 myP = this.transform.position;
  259. Vector3 desP = this.agent.destination;
  260. desP.y = myP.y;
  261. float distance = Vector3.Distance(myP, desP);
  262. if (distance < keyDistance) {
  263. needCheckFlyDown = false;
  264. if (stoneRecorder.IsStoneLocker(this)) {
  265. state.down = true;
  266. } else if (Random.value < 0.5 && tryOccupyTree()) {
  267. // } else if (tryOccupyTree()) {
  268. state.down = true;
  269. } else {
  270. willStayOnTree = true;
  271. }
  272. }
  273. }
  274. }
  275. if (moving) return;
  276. if (willStayOnTree) {
  277. willStayOnTree = false;
  278. state.flyStaying = true;
  279. canFlyStayTime = 9f + Random.value * 1f;
  280. return;
  281. }
  282. AnimatorStateInfo animatorStateInfo = animator.GetCurrentAnimatorStateInfo(0);
  283. if (animatorStateInfo.IsName("Yeji Fly Form Tree") || animatorStateInfo.IsName("Yeji Idle On Tree 01") || animatorStateInfo.IsName("Yeji Land On Tree")) {
  284. //该动作期间还不能适合执行移动,所以先返回
  285. return;
  286. }
  287. //可以飞向石头
  288. bool isOnStone = stoneRecorder.IsOnStone(this);
  289. if (!isOnStone && Random.value < 0.7f && stoneRecorder.TryLock(this)) {
  290. // if (!stoneRecorder.IsOnStone(this) && stoneRecorder.TryLock(this)) {
  291. var newPos1 = stoneRecorder.GetLockPosition(animalsBaseT, flyPlaneHeight);
  292. SetDestination(newPos1);
  293. needCheckFlyDown = true;
  294. return;
  295. }
  296. //飞向别的树
  297. #region
  298. Vector3 myPos = this.transform.position;
  299. TreeAreaRecorder.AreaInfo[] areaInfos = treeAreaRecorder.copyAreaInfos(canFlyTreeCount);
  300. for (int i = 0; i < canFlyTreeCount; i++) {
  301. areaInfos[i].teamCompareValue = areaInfos[i].distanceInHorizontal(myPos);
  302. }
  303. System.Array.Sort(areaInfos, new ComparerAreaInfosByDistance());
  304. int treeFindStartIndex = 1;
  305. if (isOnStone) treeFindStartIndex = 0;
  306. TreeAreaRecorder.AreaInfo areaInfo = areaInfos[Random.Range(treeFindStartIndex, canFlyTreeCount)];
  307. Vector3 newPos = YejiHuntGameMode.CalculateNewPosByTreePos(animalsBaseT, flyPlaneHeight, areaInfo);
  308. SetDestination(newPos);
  309. targetAreaInfo = areaInfo;
  310. needCheckFlyDown = true;
  311. #endregion
  312. }
  313. class ComparerAreaInfosByDistance : IComparer {
  314. public int Compare(object x, object y) {
  315. TreeAreaRecorder.AreaInfo a = (TreeAreaRecorder.AreaInfo)x;
  316. TreeAreaRecorder.AreaInfo b = (TreeAreaRecorder.AreaInfo)y;
  317. if (a.teamCompareValue - b.teamCompareValue > 0) return 1;
  318. if (a.teamCompareValue - b.teamCompareValue < 0) return -1;
  319. else return 0;
  320. }
  321. }
  322. void CancelStand() {
  323. if (!state.standing) return;
  324. state.standing = false;
  325. state.up = true;
  326. animator.CrossFade("FlyFromGround", 0.1f);
  327. tryReleaseOccupyTree();
  328. stoneRecorder.TryRelease(this);
  329. }
  330. void CancelFlyStay() {
  331. if (!state.flyStaying) return;
  332. canFlyStayTime = 0;
  333. }
  334. bool tryOccupyTree() {
  335. if (targetAreaInfo == null) return false;
  336. if (targetAreaInfo.occupy != null) return false;
  337. else {
  338. targetAreaInfo.occupy = this;
  339. return true;
  340. }
  341. }
  342. void tryReleaseOccupyTree() {
  343. if (targetAreaInfo != null && targetAreaInfo.occupy != null && targetAreaInfo.occupy.Equals(this)) {
  344. targetAreaInfo.occupy = null;
  345. }
  346. }
  347. static float[] preFlyHeights;
  348. static object[] preFlyHeightCools;
  349. public static void InitPreHeights() {
  350. preFlyHeights = new float[]{1.3f, 2.3f};
  351. preFlyHeightCools = new object[]{null, null};
  352. }
  353. public static float RandomOnePreHeight(Yeji yeji) {
  354. for (int i = 0; i < preFlyHeightCools.Length; i++) {
  355. if (preFlyHeightCools[i] == null) {
  356. preFlyHeightCools[i] = yeji;
  357. return preFlyHeights[i];
  358. }
  359. }
  360. return 2.5f;
  361. }
  362. public static void ReleasePreHeight(Yeji yeji) {
  363. for (int i = 0; i < preFlyHeightCools.Length; i++) {
  364. if (yeji.Equals(preFlyHeightCools[i])) {
  365. preFlyHeightCools[i] = null;
  366. }
  367. }
  368. }
  369. #endregion
  370. //状态
  371. [System.Serializable]
  372. public class State {
  373. public bool standing = false;
  374. [SerializeField] private bool _flyStaying = false;
  375. public bool flyStaying {
  376. get { return _flyStaying; }
  377. set {
  378. _flyStaying = value;
  379. animal.animator.SetBool("flyStaying", value);
  380. }
  381. }
  382. [SerializeField] private bool _dead;
  383. public bool dead {
  384. get { return _dead; }
  385. set {
  386. _dead = value;
  387. animal.animator.SetBool("dead", value);
  388. }
  389. }
  390. [SerializeField] private bool _down;
  391. public bool down {
  392. get { return _down; }
  393. set {
  394. _down = value;
  395. animal.animator.SetBool("down", value);
  396. }
  397. }
  398. [SerializeField] private bool _landing;
  399. public bool landing {
  400. get { return _landing; }
  401. set {
  402. _landing = value;
  403. animal.animator.SetBool("landing", value);
  404. }
  405. }
  406. [SerializeField] private bool _up;
  407. public bool up {
  408. get { return _up; }
  409. set {
  410. _up = value;
  411. animal.animator.SetBool("up", value);
  412. }
  413. }
  414. [SerializeField] private bool _flying;
  415. public bool flying {
  416. get { return _flying; }
  417. set {
  418. _flying = value;
  419. animal.animator.SetBool("flying", value);
  420. }
  421. }
  422. public Yeji animal;
  423. }
  424. [SerializeField] public State state;
  425. //委托
  426. public System.Action<Yeji> onDie;
  427. #region 联机附加部分
  428. public static HashSet<Yeji> yejiSet = new HashSet<Yeji>();
  429. public OnlineHandler onlineHandler = new OnlineHandler();
  430. public class OnlineHandler {
  431. Yeji animal;
  432. public int uid;
  433. public bool isMirror;
  434. public int mirrorState = 0;
  435. public float animatroSpeed = 1;
  436. public YejiSyncData outputSyncData;
  437. public void InitOnAwake(Yeji animal) {
  438. this.animal = animal;
  439. if (isMirror) {
  440. GameObject.Destroy(this.animal.agent);
  441. this.animal.animator.SetLayerWeight(1, 1);
  442. }
  443. }
  444. public void Update() {
  445. if (isMirror) {
  446. if (inputSyncData == null) return;
  447. if (!hasUpdateBySyncData) {
  448. this.animal.transform.position = syncPosition;
  449. this.animal.transform.rotation = syncRotation;
  450. hasUpdateBySyncData = true;
  451. } else {
  452. this.animal.transform.position = Vector3.Lerp(this.animal.transform.position, syncPosition, Time.deltaTime * 15);
  453. this.animal.transform.rotation = Quaternion.Lerp(this.animal.transform.rotation, syncRotation, Time.deltaTime * 15);
  454. }
  455. this.animal.animator.SetInteger("mirrorState", mirrorState);
  456. this.animal.animator.speed = animatroSpeed;
  457. if (inputSyncData.ii != injuredID) {
  458. injuredID = inputSyncData.ii;
  459. AudioMgr.ins.PlayAnimalEffect("bird_injured", AudioMgr.GetAudioSource(this.animal.gameObject));
  460. }
  461. return;
  462. }
  463. if (GlobalData.pkMatchType == PKMatchType.OnlinePK) {
  464. if (outputSyncData == null) outputSyncData = new YejiSyncData();
  465. int curStateHash = this.animal.animator.GetCurrentAnimatorStateInfo(0).fullPathHash;
  466. mirrorState = System.Array.IndexOf(stateHashList, curStateHash);
  467. animatroSpeed = this.animal.animator.speed;
  468. outputSyncData.SetData(this.animal);
  469. }
  470. }
  471. private YejiSyncData _inputSyncData;
  472. public YejiSyncData inputSyncData {
  473. get {
  474. return _inputSyncData;
  475. }
  476. set {
  477. _inputSyncData = value;
  478. uid = value.id;
  479. syncRotation.x = value.rx;
  480. syncRotation.y = value.ry;
  481. syncRotation.z = value.rz;
  482. syncRotation.w = value.rw;
  483. syncPosition.x = value.px;
  484. syncPosition.y = value.py;
  485. syncPosition.z = value.pz;
  486. mirrorState = value.ms;
  487. animatroSpeed = value.asp;
  488. }
  489. }
  490. private Quaternion syncRotation;
  491. private Vector3 syncPosition;
  492. private bool hasUpdateBySyncData = false;
  493. public bool isInvalid = false; //外部运算用的
  494. public string onHitData = null;
  495. public bool onDoNextShootWillDestroy = false;
  496. public int injuredID = 0;
  497. private int[] stateHashList = {
  498. Animator.StringToHash("Base Layer.Fly"),
  499. Animator.StringToHash("Base Layer.FlyUp"),
  500. Animator.StringToHash("Base Layer.FlyFromGround"),
  501. Animator.StringToHash("Base Layer.FlyDown"),
  502. Animator.StringToHash("Base Layer.LandOnGround"),
  503. Animator.StringToHash("Base Layer.IdleOnGround2"),
  504. Animator.StringToHash("Base Layer.Yeji Idle On Tree 01"),
  505. Animator.StringToHash("Base Layer.Yeji Land On Tree"),
  506. Animator.StringToHash("Base Layer.Yeji Fly Form Tree"),
  507. Animator.StringToHash("Base Layer.DeadOnSky"),
  508. Animator.StringToHash("Base Layer.DeadOnGround")
  509. };
  510. }
  511. #endregion
  512. public override int GetOnlineID() {
  513. return onlineHandler.uid;
  514. }
  515. static StoneRecorder stoneRecorder = new StoneRecorder();
  516. }
  517. public class StoneRecorder {
  518. private object locker;
  519. public bool TryLock(object locker) {
  520. if (this.locker == null) {
  521. this.locker = locker;
  522. return true;
  523. }
  524. return false;
  525. }
  526. public bool TryRelease(object locker) {
  527. if (this.locker == locker) {
  528. this.locker = null;
  529. return true;
  530. }
  531. return false;
  532. }
  533. public bool IsStoneLocker(object locker) {
  534. return this.locker == locker;
  535. }
  536. float stoneX = 158.981f;
  537. float stoneZ = 140.848f;
  538. public float GetStoneYOffLand() {
  539. return 0.35f;
  540. }
  541. public Vector3 GetLockPosition(Transform animalsBaseT, float flyPlaneHeight) {
  542. return new Vector3(stoneX, animalsBaseT.position.y + flyPlaneHeight, stoneZ);
  543. }
  544. public bool IsOnStone(MonoBehaviour animal) {
  545. Vector3 pos = animal.transform.position;
  546. float distance = Mathf.Sqrt(Mathf.Pow(stoneX - pos.x, 2) + Mathf.Pow(stoneZ - pos.z, 2));
  547. return distance < 0.6f;
  548. }
  549. }