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

3 years ago
3 years ago
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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 XY(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. new FrameRateTaskExecutor<int>(
  88. () => gameTimer.IsGaming && obj.CanMove && !obj.IsResetting && obj.IsMoving,
  89. () =>
  90. {
  91. moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond;
  92. res = new XY(direction, moveVecLength);
  93. // 越界情况处理:如果越界,则与越界方块碰撞
  94. bool flag; // 循环标志
  95. do
  96. {
  97. flag = false;
  98. collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res);
  99. if (collisionObj == null)
  100. break;
  101. switch (OnCollision(obj, collisionObj, res))
  102. {
  103. case AfterCollision.ContinueCheck:
  104. flag = true;
  105. break;
  106. case AfterCollision.Destroyed:
  107. Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
  108. isDestroyed = true;
  109. return false;
  110. case AfterCollision.MoveMax:
  111. MoveMax(obj, res);
  112. moveVecLength = 0;
  113. res = new XY(direction, moveVecLength);
  114. break;
  115. }
  116. } while (flag);
  117. deltaLen += moveVecLength - Math.Sqrt(obj.MovingSetPos(res, GetPlaceType(obj.Position + res)));
  118. return true;
  119. },
  120. GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond,
  121. () =>
  122. {
  123. int leftTime = moveTime % (GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond);
  124. bool flag;
  125. do
  126. {
  127. flag = false;
  128. if (!isDestroyed)
  129. {
  130. moveVecLength = deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell;
  131. res = new XY(direction, moveVecLength);
  132. if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null)
  133. {
  134. obj.MovingSetPos(res, GetPlaceType(obj.Position + res));
  135. }
  136. else
  137. {
  138. switch (OnCollision(obj, collisionObj, res))
  139. {
  140. case AfterCollision.ContinueCheck:
  141. flag = true;
  142. break;
  143. case AfterCollision.Destroyed:
  144. Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
  145. isDestroyed = true;
  146. break;
  147. case AfterCollision.MoveMax:
  148. MoveMax(obj, res);
  149. moveVecLength = 0;
  150. res = new XY(direction, moveVecLength);
  151. break;
  152. }
  153. }
  154. }
  155. } while (flag);
  156. if (leftTime > 0 && obj.IsMoving)
  157. {
  158. Thread.Sleep(leftTime); // 多移动的在这里补回来
  159. }
  160. lock (obj.MoveLock)
  161. obj.IsMoving = false; // 结束移动
  162. EndMove(obj);
  163. return 0;
  164. },
  165. maxTotalDuration: moveTime
  166. )
  167. {
  168. AllowTimeExceed = true,
  169. MaxTolerantTimeExceedCount = ulong.MaxValue,
  170. TimeExceedAction = b =>
  171. {
  172. if (b)
  173. Console.WriteLine("Fatal Error: The computer runs so slow that the object cannot finish moving during this time!!!!!!");
  174. #if DEBUG
  175. else
  176. {
  177. Console.WriteLine("Debug info: Object moving time exceed for once.");
  178. }
  179. #endif
  180. }
  181. }.Start();
  182. }
  183. ).Start();
  184. }
  185. }
  186. }