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