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

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