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 9.0 kB

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