Browse Source

style: 🎨

tags/0.1.0
shangfengh 3 years ago
parent
commit
28a5ca9df1
4 changed files with 170 additions and 166 deletions
  1. +78
    -73
      logic/GameEngine/CollisionChecker.cs
  2. +90
    -91
      logic/GameEngine/MoveEngine.cs
  3. +1
    -1
      logic/Preparation/Interface/IMoveable.cs
  4. +1
    -1
      logic/Preparation/Utility/XY.cs

+ 78
- 73
logic/GameEngine/CollisionChecker.cs View File

@@ -24,26 +24,29 @@ namespace GameEngine
return gameMap.GetOutOfBound(nextPos);
return null;
}
//在列表中检查碰撞
// 在列表中检查碰撞
Func<IEnumerable<IGameObj>, ReaderWriterLockSlim, IGameObj?> CheckCollisionInList =
(IEnumerable<IGameObj> 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
/// <param name="obj"></param>
/// <param name="square">矩形的中心坐标</param>
/// <returns></returns>
//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;
// }

/// <summary>
/// 寻找最大可能移动距离
@@ -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<IEnumerable<IGameObj>, ReaderWriterLockSlim>[gameMap.GameObjDict.Count];
int i = 0;
foreach(var keyValuePair in gameMap.GameObjDict)
foreach (var keyValuePair in gameMap.GameObjDict)
{
lists[i++] = new Tuple<IEnumerable<IGameObj>, ReaderWriterLockSlim>(keyValuePair.Value as IList<IGameObj>, gameMap.GameObjLockDict[keyValuePair.Key]);
lists[i++] = new Tuple<IEnumerable<IGameObj>, ReaderWriterLockSlim>(keyValuePair.Value as IList<IGameObj>, gameMap.GameObjLockDict[keyValuePair.Key]);
}
}
}


+ 90
- 91
logic/GameEngine/MoveEngine.cs View File

@@ -14,9 +14,9 @@ namespace GameEngine
/// </summary>
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
/// <param name="gameMap">游戏地图</param>
/// <param name="OnCollision">发生碰撞时要做的事情,第一个参数为移动的物体,第二个参数为撞到的物体,第三个参数为移动的位移向量,返回值见AfterCollision的定义</param>
/// <param name="EndMove">结束碰撞时要做的事情</param>
public MoveEngine
(
IMap gameMap,
Func<IMoveable, IGameObj, XY, AfterCollision> OnCollision,
Action<IMoveable> EndMove
)
public MoveEngine(
IMap gameMap,
Func<IMoveable, IGameObj, XY, AfterCollision> OnCollision,
Action<IMoveable> 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<int>
(
() => 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<int>(
() => 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();
}
}
}

+ 1
- 1
logic/Preparation/Interface/IMoveable.cs View File

@@ -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)


+ 1
- 1
logic/Preparation/Utility/XY.cs View File

@@ -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);
}


Loading…
Cancel
Save