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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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));
  69. }
  70. public void MoveObj(IMoveable obj, int moveTime, double direction, long threadNum)
  71. {
  72. if (!gameTimer.IsGaming) return;
  73. lock (obj.ActionLock)
  74. {
  75. if (!obj.IsAvailableForMove) return;
  76. obj.IsMoving = true;
  77. }
  78. new Thread
  79. (
  80. () =>
  81. {
  82. double moveVecLength = 0.0;
  83. XY res = new(direction, moveVecLength);
  84. double deltaLen = 0; // 转向,并用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.IsRemoved && obj.IsMoving,
  111. () =>
  112. {
  113. moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond;
  114. res = new XY(direction, moveVecLength);
  115. //对人特殊处理
  116. if (threadNum > 0 && obj.StateNum != threadNum) return false;
  117. // 越界情况处理:如果越界,则与越界方块碰撞
  118. bool flag; // 循环标志
  119. do
  120. {
  121. flag = false;
  122. collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res);
  123. if (collisionObj == null)
  124. break;
  125. switch (OnCollision(obj, collisionObj, res))
  126. {
  127. case AfterCollision.ContinueCheck:
  128. flag = true;
  129. break;
  130. case AfterCollision.Destroyed:
  131. Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
  132. isDestroyed = true;
  133. return false;
  134. case AfterCollision.MoveMax:
  135. if (threadNum == 0 || obj.StateNum == threadNum)
  136. MoveMax(obj, res);
  137. moveVecLength = 0;
  138. res = new XY(direction, moveVecLength);
  139. break;
  140. }
  141. } while (flag);
  142. if (threadNum == 0 || obj.StateNum == threadNum)
  143. deltaLen += moveVecLength - Math.Sqrt(obj.MovingSetPos(res));
  144. return true;
  145. },
  146. GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond,
  147. () =>
  148. {
  149. int leftTime = moveTime % (GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond);
  150. bool flag;
  151. do
  152. {
  153. flag = false;
  154. if (!isDestroyed)
  155. {
  156. moveVecLength = deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell;
  157. res = new XY(direction, moveVecLength);
  158. if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null)
  159. {
  160. if (threadNum == 0 || obj.StateNum == threadNum)
  161. obj.MovingSetPos(res);
  162. }
  163. else
  164. {
  165. switch (OnCollision(obj, collisionObj, res))
  166. {
  167. case AfterCollision.ContinueCheck:
  168. flag = true;
  169. break;
  170. case AfterCollision.Destroyed:
  171. Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
  172. isDestroyed = true;
  173. break;
  174. case AfterCollision.MoveMax:
  175. if (threadNum == 0 || obj.StateNum == threadNum)
  176. MoveMax(obj, res);
  177. moveVecLength = 0;
  178. res = new XY(direction, moveVecLength);
  179. break;
  180. }
  181. }
  182. }
  183. } while (flag);
  184. if (leftTime > 0 && obj.IsMoving)
  185. {
  186. Thread.Sleep(leftTime); // 多移动的在这里补回来
  187. }
  188. lock (obj.ActionLock)
  189. obj.IsMoving = false; // 结束移动
  190. EndMove(obj);
  191. return 0;
  192. },
  193. maxTotalDuration: moveTime
  194. )
  195. {
  196. AllowTimeExceed = true,
  197. MaxTolerantTimeExceedCount = ulong.MaxValue,
  198. TimeExceedAction = b =>
  199. {
  200. if (b)
  201. Console.WriteLine("Fatal Error: The computer runs so slow that the object cannot finish moving during this time!!!!!!");
  202. #if DEBUG
  203. else
  204. {
  205. Console.WriteLine("Debug info: Object moving time exceed for once.");
  206. }
  207. #endif
  208. }
  209. }.Start();
  210. }
  211. }
  212. ).Start();
  213. }
  214. }
  215. }