using System; using System.Threading; using Preparation.Interface; using Preparation.Utility; using Timothy.FrameRateTask; using Preparation.GameData; namespace GameEngine { public class MoveEngine { /// /// 碰撞结束后要做的事情 /// public enum AfterCollision { ContinueCheck = 0, // 碰撞后继续检查其他碰撞 MoveMax = 1, // 行走最远距离 Destroyed = 2 // 物体已经毁坏 } private readonly ITimer gameTimer; private readonly Action EndMove; private readonly CollisionChecker collisionChecker; private readonly Func OnCollision; /// /// Constrctor /// /// 游戏地图 /// 发生碰撞时要做的事情,第一个参数为移动的物体,第二个参数为撞到的物体,第三个参数为移动的位移向量,返回值见AfterCollision的定义 /// 结束碰撞时要做的事情 public MoveEngine( IMap gameMap, Func OnCollision, Action EndMove ) { this.gameTimer = gameMap.Timer; this.EndMove = EndMove; this.OnCollision = OnCollision; this.collisionChecker = new CollisionChecker(gameMap); } /// /// 在无碰撞的前提下行走最远的距离 /// /// 移动物体,默认obj.Rigid为true /// 移动的位移向量 private void MoveMax(IMoveable obj, XY moveVec) { /*由于四周是墙,所以人物永远不可能与越界方块碰撞*/ XY nextPos = obj.Position + XY.VectorToXY(moveVec); double maxLen = collisionChecker.FindMax(obj, nextPos, moveVec); maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.numOfStepPerSecond); _ = obj.Move(new Vector(moveVec.angle, maxLen)); } public void MoveObj(IMoveable obj, int moveTime, double direction) { if (obj.IsMoving) // 已经移动的物体不能再移动 return; new Thread ( ()=> { if (!obj.IsAvailable&&gameTimer.IsGaming) //不能动就直接return,后面都是能动的情况 return; lock (obj.MoveLock) obj.IsMoving = true; XY moveVec = new(direction, 0.0); double deltaLen = moveVec.length - Math.Sqrt(obj.Move(moveVec)); // 转向,并用deltaLen存储行走的误差 IGameObj? collisionObj = null; bool isDestroyed = false; new FrameRateTaskExecutor( () => gameTimer.IsGaming && obj.CanMove && !obj.IsResetting, () => { moveVec.length = obj.MoveSpeed / GameData.numOfStepPerSecond; // 越界情况处理:如果越界,则与越界方块碰撞 bool flag; // 循环标志 do { flag = false; collisionObj = collisionChecker.CheckCollision(obj, moveVec); if (collisionObj == null) break; switch (OnCollision(obj, collisionObj, moveVec)) { case AfterCollision.ContinueCheck: flag = true; break; case AfterCollision.Destroyed: Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); isDestroyed = true; return false; case AfterCollision.MoveMax: MoveMax(obj, moveVec); moveVec.length = 0; break; } } while (flag); deltaLen += moveVec.length - Math.Sqrt(obj.Move(moveVec)); return true; }, GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond, () => { int leftTime = moveTime % (GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond); bool flag; do { flag = false; if (!isDestroyed) { moveVec.length = deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell; if ((collisionObj = collisionChecker.CheckCollision(obj, moveVec)) == null) { obj.Move(moveVec); } else { switch (OnCollision(obj, collisionObj, moveVec)) { case AfterCollision.ContinueCheck: flag = true; break; case AfterCollision.Destroyed: Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); isDestroyed = true; break; case AfterCollision.MoveMax: MoveMax(obj, moveVec); moveVec.length = 0; break; } } } } while (flag); if (leftTime > 0) { Thread.Sleep(leftTime); // 多移动的在这里补回来 } lock (obj.MoveLock) obj.IsMoving = false; // 结束移动 EndMove(obj); return 0; }, maxTotalDuration: moveTime ) { AllowTimeExceed = true, MaxTolerantTimeExceedCount = ulong.MaxValue, TimeExceedAction = b => { if (b) Console.WriteLine("Fatal Error: The computer runs so slow that the object cannot finish moving during this time!!!!!!"); #if DEBUG else { Console.WriteLine("Debug info: Object moving time exceed for once."); } #endif } }.Start(); } ).Start(); } } }