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

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