diff --git a/logic/GameEngine/CollisionChecker.cs b/logic/GameEngine/CollisionChecker.cs index 93e0074..08ca0f7 100644 --- a/logic/GameEngine/CollisionChecker.cs +++ b/logic/GameEngine/CollisionChecker.cs @@ -24,26 +24,29 @@ namespace GameEngine return gameMap.GetOutOfBound(nextPos); return null; } - //在列表中检查碰撞 + // 在列表中检查碰撞 Func, ReaderWriterLockSlim, IGameObj?> CheckCollisionInList = (IEnumerable lst, ReaderWriterLockSlim listLock) => + { + IGameObj? collisionObj = null; + listLock.EnterReadLock(); + try { - IGameObj? collisionObj = null; - listLock.EnterReadLock(); - try + foreach (var listObj in lst) { - foreach (var listObj in lst) + if (obj.WillCollideWith(listObj, nextPos)) { - if (obj.WillCollideWith(listObj, nextPos)) - { - collisionObj = listObj; - break; - } + collisionObj = listObj; + break; } } - finally { listLock.ExitReadLock(); } - return collisionObj; - }; + } + finally + { + listLock.ExitReadLock(); + } + return collisionObj; + }; IGameObj? collisionObj = null; foreach (var list in lists) @@ -62,51 +65,51 @@ namespace GameEngine /// /// 矩形的中心坐标 /// - //private double MaxMoveToSquare(IMoveable obj, IGameObj square) + // private double MaxMoveToSquare(IMoveable obj, IGameObj square) //{ - // double tmpMax; - // double angle = Math.Atan2(square.Position.y - obj.Position.y, square.Position.x - obj.Position.x); - // if (obj.WillCollideWith(square, obj.Position)) - // tmpMax = 0; - // else tmpMax = - // Math.Abs(XYPosition.Distance(obj.Position, square.Position) - obj.Radius - - // (square.Radius / Math.Min(Math.Abs(Math.Cos(angle)), Math.Abs(Math.Sin(angle))))); - // return tmpMax; - //} + // double tmpMax; + // double angle = Math.Atan2(square.Position.y - obj.Position.y, square.Position.x - obj.Position.x); + // if (obj.WillCollideWith(square, obj.Position)) + // tmpMax = 0; + // else tmpMax = + // Math.Abs(XYPosition.Distance(obj.Position, square.Position) - obj.Radius - + // (square.Radius / Math.Min(Math.Abs(Math.Cos(angle)), Math.Abs(Math.Sin(angle))))); + // return tmpMax; + // } - //private double FindMaxOnlyConsiderWall(IMoveable obj, Vector moveVec) + // private double FindMaxOnlyConsiderWall(IMoveable obj, Vector moveVec) //{ - // var desination = moveVec; - // double maxOnlyConsiderWall = moveVec.length; - // if (desination.length > 0) //如果length足够长,还是有可能穿墙的 - // { - // XYPosition nextXY = Vector.Vector2XY(desination) + obj.Position + new XYPosition((int)(obj.Radius * Math.Cos(moveVec.angle)), (int)(obj.Radius * Math.Sin(moveVec.angle))); - // if (gameMap.IsWall(nextXY)) //对下一步的位置进行检查,但这里只是考虑移动物体的宽度,只是考虑下一步能达到的最远位置 - // { - // maxOnlyConsiderWall = MaxMoveToSquare(obj, gameMap.GetCell(nextXY)); - // } - // else //考虑物体宽度 - // { - // double dist = 0; - // XYPosition nextXYConsiderWidth; - // nextXYConsiderWidth = nextXY + new XYPosition((int)(obj.Radius * Math.Cos(moveVec.angle + Math.PI / 4)), (int)(obj.Radius * Math.Sin(moveVec.angle + Math.PI / 4))); - // if (gameMap.IsWall(nextXYConsiderWidth)) //对下一步的位置进行检查,但这里只是考虑移动物体的宽度,只是考虑下一步能达到的最远位置 - // { - // dist = MaxMoveToSquare(obj, gameMap.GetCell(nextXYConsiderWidth)); - // if (dist < maxOnlyConsiderWall) - // maxOnlyConsiderWall = dist; - // } - // nextXYConsiderWidth = nextXY + new XYPosition((int)(obj.Radius * Math.Cos(moveVec.angle - Math.PI / 4)), (int)(obj.Radius * Math.Sin(moveVec.angle - Math.PI / 4))); - // if (gameMap.IsWall(nextXYConsiderWidth)) //对下一步的位置进行检查,但这里只是考虑移动物体的宽度,只是考虑下一步能达到的最远位置 - // { - // dist = MaxMoveToSquare(obj, gameMap.GetCell(nextXYConsiderWidth)); - // if (dist < maxOnlyConsiderWall) - // maxOnlyConsiderWall = dist; - // } - // } - // } - // return maxOnlyConsiderWall; - //} + // var desination = moveVec; + // double maxOnlyConsiderWall = moveVec.length; + // if (desination.length > 0) //如果length足够长,还是有可能穿墙的 + // { + // XYPosition nextXY = Vector.Vector2XY(desination) + obj.Position + new XYPosition((int)(obj.Radius * Math.Cos(moveVec.angle)), (int)(obj.Radius * Math.Sin(moveVec.angle))); + // if (gameMap.IsWall(nextXY)) //对下一步的位置进行检查,但这里只是考虑移动物体的宽度,只是考虑下一步能达到的最远位置 + // { + // maxOnlyConsiderWall = MaxMoveToSquare(obj, gameMap.GetCell(nextXY)); + // } + // else //考虑物体宽度 + // { + // double dist = 0; + // XYPosition nextXYConsiderWidth; + // nextXYConsiderWidth = nextXY + new XYPosition((int)(obj.Radius * Math.Cos(moveVec.angle + Math.PI / 4)), (int)(obj.Radius * Math.Sin(moveVec.angle + Math.PI / 4))); + // if (gameMap.IsWall(nextXYConsiderWidth)) //对下一步的位置进行检查,但这里只是考虑移动物体的宽度,只是考虑下一步能达到的最远位置 + // { + // dist = MaxMoveToSquare(obj, gameMap.GetCell(nextXYConsiderWidth)); + // if (dist < maxOnlyConsiderWall) + // maxOnlyConsiderWall = dist; + // } + // nextXYConsiderWidth = nextXY + new XYPosition((int)(obj.Radius * Math.Cos(moveVec.angle - Math.PI / 4)), (int)(obj.Radius * Math.Sin(moveVec.angle - Math.PI / 4))); + // if (gameMap.IsWall(nextXYConsiderWidth)) //对下一步的位置进行检查,但这里只是考虑移动物体的宽度,只是考虑下一步能达到的最远位置 + // { + // dist = MaxMoveToSquare(obj, gameMap.GetCell(nextXYConsiderWidth)); + // if (dist < maxOnlyConsiderWall) + // maxOnlyConsiderWall = dist; + // } + // } + // } + // return maxOnlyConsiderWall; + // } /// /// 寻找最大可能移动距离 @@ -118,10 +121,10 @@ namespace GameEngine public double FindMax(IMoveable obj, XY nextPos, XY moveVec) { double maxLen = (double)uint.MaxValue; - double tmpMax = maxLen; //暂存最大值 + double tmpMax = maxLen; // 暂存最大值 // 先找只考虑墙的最大距离 - //double maxOnlyConsiderWall = FindMaxOnlyConsiderWall(obj, moveVec); + // double maxOnlyConsiderWall = FindMaxOnlyConsiderWall(obj, moveVec); double maxDistance = maxLen; foreach (var listWithLock in lists) { @@ -132,47 +135,48 @@ namespace GameEngine { foreach (IGameObj listObj in lst) { - //如果再走一步发生碰撞 + // 如果再走一步发生碰撞 if (obj.WillCollideWith(listObj, nextPos)) { { - switch (listObj.Shape) //默认obj为圆形 + switch (listObj.Shape) // 默认obj为圆形 { case ShapeType.Circle: { - //计算两者之间的距离 + // 计算两者之间的距离 double mod = XY.Distance(listObj.Position, obj.Position); int orgDeltaX = listObj.Position.x - obj.Position.x; int orgDeltaY = listObj.Position.y - obj.Position.y; - if (mod < listObj.Radius + obj.Radius) //如果两者已经重叠 + if (mod < listObj.Radius + obj.Radius) // 如果两者已经重叠 { tmpMax = 0; } else { double tmp = mod - obj.Radius - listObj.Radius; - //计算能走的最长距离,好像这么算有一点误差? + // 计算能走的最长距离,好像这么算有一点误差? tmp = tmp / Math.Cos(Math.Atan2(orgDeltaY, orgDeltaX) - moveVec.angle); if (tmp < 0 || tmp > uint.MaxValue || tmp == double.NaN) { tmpMax = uint.MaxValue; } - else tmpMax = tmp; + else + tmpMax = tmp; } break; } case ShapeType.Square: { - //if (obj.WillCollideWith(listObj, obj.Position)) - // tmpMax = 0; - //else tmpMax = MaxMoveToSquare(obj, listObj); - //break; + // if (obj.WillCollideWith(listObj, obj.Position)) + // tmpMax = 0; + // else tmpMax = MaxMoveToSquare(obj, listObj); + // break; if (obj.WillCollideWith(listObj, obj.Position)) tmpMax = 0; else { - //二分查找最大可能移动距离 + // 二分查找最大可能移动距离 int left = 0, right = (int)moveVec.length; while (left < right - 1) { @@ -181,7 +185,8 @@ namespace GameEngine { right = mid; } - else left = mid; + else + left = mid; } tmpMax = (uint)left; } @@ -199,11 +204,11 @@ namespace GameEngine } finally { - //maxLen = Math.Min(maxOnlyConsiderWall, maxDistance); //最大可能距离的最小值 + // maxLen = Math.Min(maxOnlyConsiderWall, maxDistance); //最大可能距离的最小值 listLock.ExitReadLock(); } } - //return maxLen; + // return maxLen; return maxDistance; } @@ -215,9 +220,9 @@ namespace GameEngine this.gameMap = gameMap; lists = new Tuple, ReaderWriterLockSlim>[gameMap.GameObjDict.Count]; int i = 0; - foreach(var keyValuePair in gameMap.GameObjDict) + foreach (var keyValuePair in gameMap.GameObjDict) { - lists[i++] = new Tuple, ReaderWriterLockSlim>(keyValuePair.Value as IList, gameMap.GameObjLockDict[keyValuePair.Key]); + lists[i++] = new Tuple, ReaderWriterLockSlim>(keyValuePair.Value as IList, gameMap.GameObjLockDict[keyValuePair.Key]); } } } diff --git a/logic/GameEngine/MoveEngine.cs b/logic/GameEngine/MoveEngine.cs index 0424397..867dae3 100644 --- a/logic/GameEngine/MoveEngine.cs +++ b/logic/GameEngine/MoveEngine.cs @@ -14,9 +14,9 @@ namespace GameEngine /// public enum AfterCollision { - ContinueCheck = 0, // 碰撞后继续检查其他碰撞 - MoveMax = 1, // 行走最远距离 - Destroyed = 2 // 物体已经毁坏 + ContinueCheck = 0, // 碰撞后继续检查其他碰撞 + MoveMax = 1, // 行走最远距离 + Destroyed = 2 // 物体已经毁坏 } private readonly ITimer gameTimer; @@ -29,12 +29,11 @@ namespace GameEngine /// 游戏地图 /// 发生碰撞时要做的事情,第一个参数为移动的物体,第二个参数为撞到的物体,第三个参数为移动的位移向量,返回值见AfterCollision的定义 /// 结束碰撞时要做的事情 - public MoveEngine - ( - IMap gameMap, - Func OnCollision, - Action EndMove - ) + public MoveEngine( + IMap gameMap, + Func OnCollision, + Action EndMove + ) { this.gameTimer = gameMap.Timer; this.EndMove = EndMove; @@ -59,7 +58,7 @@ namespace GameEngine public void MoveObj(IMoveable obj, int moveTime, double direction) { - if (obj.IsMoving) //已经移动的物体不能再移动 + if (obj.IsMoving) // 已经移动的物体不能再移动 return; new Thread ( @@ -67,28 +66,65 @@ namespace GameEngine { if (!obj.IsAvailable&&gameTimer.IsGaming) //不能动就直接return,后面都是能动的情况 return; - lock (obj.MoveLock) - obj.IsMoving = true; + 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, - () => + 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)) { - moveVec.length = obj.MoveSpeed / GameData.numOfStepPerSecond; + 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); - //越界情况处理:如果越界,则与越界方块碰撞 - bool flag; //循环标志 - do - { - flag = false; - collisionObj = collisionChecker.CheckCollision(obj, moveVec); - if (collisionObj == null) break; + 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: @@ -97,80 +133,43 @@ namespace GameEngine case AfterCollision.Destroyed: Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); isDestroyed = true; - return false; + break; 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 - ) + } + } while (flag); + if (leftTime > 0) { - 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!!!!!!"); + 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."); - } + else + { + Console.WriteLine("Debug info: Object moving time exceed for once."); + } #endif - } - }.Start(); } - ).Start(); + }.Start(); } + ).Start(); } } +} diff --git a/logic/Preparation/Interface/IMoveable.cs b/logic/Preparation/Interface/IMoveable.cs index e72e14e..ff23718 100644 --- a/logic/Preparation/Interface/IMoveable.cs +++ b/logic/Preparation/Interface/IMoveable.cs @@ -8,7 +8,7 @@ namespace Preparation.Interface object MoveLock { get; } public int MoveSpeed { get; } public long Move(XY moveVec); - protected bool IgnoreCollide(IGameObj targetObj); // 忽略碰撞,在具体类中实现 + protected bool IgnoreCollide(IGameObj targetObj); // 忽略碰撞,在具体类中实现 public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞 { if (targetObj == null) diff --git a/logic/Preparation/Utility/XY.cs b/logic/Preparation/Utility/XY.cs index f7536a0..ff28482 100644 --- a/logic/Preparation/Utility/XY.cs +++ b/logic/Preparation/Utility/XY.cs @@ -16,7 +16,7 @@ namespace Preparation.Utility { return "(" + x.ToString() + "," + y.ToString() + ")"; } - public static int operator *(XY v1, XY v2) + public static int operator*(XY v1, XY v2) { return (v1.x * v2.x) + (v1.y * v2.y); }