You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

MoveEngine.cs 8.3 kB

3 years ago
3 years ago
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. using System;
  2. using System.Threading;
  3. using Preparation.Interface;
  4. using Preparation.Utility;
  5. using Timothy.FrameRateTask;
  6. namespace GameEngine
  7. {
  8. public class MoveEngine
  9. {
  10. /// <summary>
  11. /// 碰撞结束后要做的事情
  12. /// </summary>
  13. public enum AfterCollision
  14. {
  15. ContinueCheck = 0, // 碰撞后继续检查其他碰撞,暂时没用
  16. MoveMax = 1, // 行走最远距离
  17. Destroyed = 2 // 物体已经毁坏
  18. }
  19. private readonly ITimer gameTimer;
  20. private readonly Action<IMoveable> EndMove;
  21. private readonly CollisionChecker collisionChecker;
  22. private readonly Func<IMoveable, IGameObj, XY, AfterCollision> OnCollision;
  23. /// <summary>
  24. /// Constrctor
  25. /// </summary>
  26. /// <param name="gameMap">游戏地图</param>
  27. /// <param name="OnCollision">发生碰撞时要做的事情,第一个参数为移动的物体,第二个参数为撞到的物体,第三个参数为移动的位移向量,返回值见AfterCollision的定义</param>
  28. /// <param name="EndMove">结束碰撞时要做的事情</param>
  29. public MoveEngine(
  30. IMap gameMap,
  31. Func<IMoveable, IGameObj, XY, AfterCollision> OnCollision,
  32. Action<IMoveable> EndMove
  33. )
  34. {
  35. this.gameTimer = gameMap.Timer;
  36. this.EndMove = EndMove;
  37. this.OnCollision = OnCollision;
  38. this.collisionChecker = new CollisionChecker(gameMap);
  39. }
  40. /// <summary>
  41. /// 在无碰撞的前提下行走最远的距离
  42. /// </summary>
  43. /// <param name="obj">移动物体,默认obj.Rigid为true</param>
  44. /// <param name="moveVec">移动的位移向量</param>
  45. private void MoveMax(IMoveable obj, XY moveVec)
  46. {
  47. /*由于四周是墙,所以人物永远不可能与越界方块碰撞*/
  48. XY nextPos = obj.Position + moveVec;
  49. double maxLen = collisionChecker.FindMax(obj, nextPos, moveVec);
  50. maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.numOfStepPerSecond);
  51. _ = obj.Move(new XY(moveVec.Angle(), maxLen));
  52. }
  53. public void MoveObj(IMoveable obj, int moveTime, double direction)
  54. {
  55. if (obj.IsMoving) // 已经移动的物体不能再移动
  56. return;
  57. new Thread
  58. (
  59. () =>
  60. {
  61. if (!obj.IsAvailable && gameTimer.IsGaming) //不能动就直接return,后面都是能动的情况
  62. return;
  63. lock (obj.MoveLock)
  64. obj.IsMoving = true;
  65. double moveVecLength = 0.0;
  66. double deltaLen = moveVecLength - Math.Sqrt(obj.Move(new XY(direction, moveVecLength))); // 转向,并用deltaLen存储行走的误差
  67. IGameObj? collisionObj = null;
  68. bool isDestroyed = false;
  69. new FrameRateTaskExecutor<int>(
  70. () => gameTimer.IsGaming && obj.CanMove && !obj.IsResetting,
  71. () =>
  72. {
  73. moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond;
  74. // 越界情况处理:如果越界,则与越界方块碰撞
  75. bool flag; // 循环标志
  76. do
  77. {
  78. flag = false;
  79. collisionObj = collisionChecker.CheckCollision(obj, new XY(direction, moveVecLength));
  80. if (collisionObj == null)
  81. break;
  82. switch (OnCollision(obj, collisionObj, new XY(direction, moveVecLength)))
  83. {
  84. case AfterCollision.ContinueCheck:
  85. flag = true;
  86. break;
  87. case AfterCollision.Destroyed:
  88. Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
  89. isDestroyed = true;
  90. return false;
  91. case AfterCollision.MoveMax:
  92. MoveMax(obj, new XY(direction, moveVecLength));
  93. moveVecLength = 0;
  94. break;
  95. }
  96. } while (flag);
  97. deltaLen += moveVecLength - Math.Sqrt(obj.Move(new XY(direction, moveVecLength)));
  98. return true;
  99. },
  100. GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond,
  101. () =>
  102. {
  103. int leftTime = moveTime % (GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond);
  104. bool flag;
  105. do
  106. {
  107. flag = false;
  108. if (!isDestroyed)
  109. {
  110. moveVecLength = deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell;
  111. if ((collisionObj = collisionChecker.CheckCollision(obj, new XY(direction, moveVecLength))) == null)
  112. {
  113. obj.Move(new XY(direction, moveVecLength));
  114. }
  115. else
  116. {
  117. switch (OnCollision(obj, collisionObj, new XY(direction, moveVecLength)))
  118. {
  119. case AfterCollision.ContinueCheck:
  120. flag = true;
  121. break;
  122. case AfterCollision.Destroyed:
  123. Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
  124. isDestroyed = true;
  125. break;
  126. case AfterCollision.MoveMax:
  127. MoveMax(obj, new XY(direction, moveVecLength));
  128. moveVecLength = 0;
  129. break;
  130. }
  131. }
  132. }
  133. } while (flag);
  134. if (leftTime > 0)
  135. {
  136. Thread.Sleep(leftTime); // 多移动的在这里补回来
  137. }
  138. lock (obj.MoveLock)
  139. obj.IsMoving = false; // 结束移动
  140. EndMove(obj);
  141. return 0;
  142. },
  143. maxTotalDuration: moveTime
  144. )
  145. {
  146. AllowTimeExceed = true,
  147. MaxTolerantTimeExceedCount = ulong.MaxValue,
  148. TimeExceedAction = b =>
  149. {
  150. if (b)
  151. Console.WriteLine("Fatal Error: The computer runs so slow that the object cannot finish moving during this time!!!!!!");
  152. #if DEBUG
  153. else
  154. {
  155. Console.WriteLine("Debug info: Object moving time exceed for once.");
  156. }
  157. #endif
  158. }
  159. }.Start();
  160. }
  161. ).Start();
  162. }
  163. }
  164. }