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

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