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.1 kB

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