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 gameMap.GetOutOfBound(nextPos);
return null; return null;
} }
//在列表中检查碰撞
// 在列表中检查碰撞
Func<IEnumerable<IGameObj>, ReaderWriterLockSlim, IGameObj?> CheckCollisionInList = Func<IEnumerable<IGameObj>, ReaderWriterLockSlim, IGameObj?> CheckCollisionInList =
(IEnumerable<IGameObj> lst, ReaderWriterLockSlim listLock) => (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; IGameObj? collisionObj = null;
foreach (var list in lists) foreach (var list in lists)
@@ -62,51 +65,51 @@ namespace GameEngine
/// <param name="obj"></param> /// <param name="obj"></param>
/// <param name="square">矩形的中心坐标</param> /// <param name="square">矩形的中心坐标</param>
/// <returns></returns> /// <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> /// <summary>
/// 寻找最大可能移动距离 /// 寻找最大可能移动距离
@@ -118,10 +121,10 @@ namespace GameEngine
public double FindMax(IMoveable obj, XY nextPos, XY moveVec) public double FindMax(IMoveable obj, XY nextPos, XY moveVec)
{ {
double maxLen = (double)uint.MaxValue; double maxLen = (double)uint.MaxValue;
double tmpMax = maxLen; //暂存最大值
double tmpMax = maxLen; // 暂存最大值


// 先找只考虑墙的最大距离 // 先找只考虑墙的最大距离
//double maxOnlyConsiderWall = FindMaxOnlyConsiderWall(obj, moveVec);
// double maxOnlyConsiderWall = FindMaxOnlyConsiderWall(obj, moveVec);
double maxDistance = maxLen; double maxDistance = maxLen;
foreach (var listWithLock in lists) foreach (var listWithLock in lists)
{ {
@@ -132,47 +135,48 @@ namespace GameEngine
{ {
foreach (IGameObj listObj in lst) foreach (IGameObj listObj in lst)
{ {
//如果再走一步发生碰撞
// 如果再走一步发生碰撞
if (obj.WillCollideWith(listObj, nextPos)) if (obj.WillCollideWith(listObj, nextPos))
{ {
{ {
switch (listObj.Shape) //默认obj为圆形
switch (listObj.Shape) // 默认obj为圆形
{ {
case ShapeType.Circle: case ShapeType.Circle:
{ {
//计算两者之间的距离
// 计算两者之间的距离
double mod = XY.Distance(listObj.Position, obj.Position); double mod = XY.Distance(listObj.Position, obj.Position);
int orgDeltaX = listObj.Position.x - obj.Position.x; int orgDeltaX = listObj.Position.x - obj.Position.x;
int orgDeltaY = listObj.Position.y - obj.Position.y; int orgDeltaY = listObj.Position.y - obj.Position.y;


if (mod < listObj.Radius + obj.Radius) //如果两者已经重叠
if (mod < listObj.Radius + obj.Radius) // 如果两者已经重叠
{ {
tmpMax = 0; tmpMax = 0;
} }
else else
{ {
double tmp = mod - obj.Radius - listObj.Radius; double tmp = mod - obj.Radius - listObj.Radius;
//计算能走的最长距离,好像这么算有一点误差?
// 计算能走的最长距离,好像这么算有一点误差?
tmp = tmp / Math.Cos(Math.Atan2(orgDeltaY, orgDeltaX) - moveVec.angle); tmp = tmp / Math.Cos(Math.Atan2(orgDeltaY, orgDeltaX) - moveVec.angle);
if (tmp < 0 || tmp > uint.MaxValue || tmp == double.NaN) if (tmp < 0 || tmp > uint.MaxValue || tmp == double.NaN)
{ {
tmpMax = uint.MaxValue; tmpMax = uint.MaxValue;
} }
else tmpMax = tmp;
else
tmpMax = tmp;
} }
break; break;
} }
case ShapeType.Square: 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)) if (obj.WillCollideWith(listObj, obj.Position))
tmpMax = 0; tmpMax = 0;
else else
{ {
//二分查找最大可能移动距离
// 二分查找最大可能移动距离
int left = 0, right = (int)moveVec.length; int left = 0, right = (int)moveVec.length;
while (left < right - 1) while (left < right - 1)
{ {
@@ -181,7 +185,8 @@ namespace GameEngine
{ {
right = mid; right = mid;
} }
else left = mid;
else
left = mid;
} }
tmpMax = (uint)left; tmpMax = (uint)left;
} }
@@ -199,11 +204,11 @@ namespace GameEngine
} }
finally finally
{ {
//maxLen = Math.Min(maxOnlyConsiderWall, maxDistance); //最大可能距离的最小值
// maxLen = Math.Min(maxOnlyConsiderWall, maxDistance); //最大可能距离的最小值
listLock.ExitReadLock(); listLock.ExitReadLock();
} }
} }
//return maxLen;
// return maxLen;
return maxDistance; return maxDistance;
} }


@@ -215,9 +220,9 @@ namespace GameEngine
this.gameMap = gameMap; this.gameMap = gameMap;
lists = new Tuple<IEnumerable<IGameObj>, ReaderWriterLockSlim>[gameMap.GameObjDict.Count]; lists = new Tuple<IEnumerable<IGameObj>, ReaderWriterLockSlim>[gameMap.GameObjDict.Count];
int i = 0; 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> /// </summary>
public enum AfterCollision public enum AfterCollision
{ {
ContinueCheck = 0, // 碰撞后继续检查其他碰撞
MoveMax = 1, // 行走最远距离
Destroyed = 2 // 物体已经毁坏
ContinueCheck = 0, // 碰撞后继续检查其他碰撞
MoveMax = 1, // 行走最远距离
Destroyed = 2 // 物体已经毁坏
} }


private readonly ITimer gameTimer; private readonly ITimer gameTimer;
@@ -29,12 +29,11 @@ namespace GameEngine
/// <param name="gameMap">游戏地图</param> /// <param name="gameMap">游戏地图</param>
/// <param name="OnCollision">发生碰撞时要做的事情,第一个参数为移动的物体,第二个参数为撞到的物体,第三个参数为移动的位移向量,返回值见AfterCollision的定义</param> /// <param name="OnCollision">发生碰撞时要做的事情,第一个参数为移动的物体,第二个参数为撞到的物体,第三个参数为移动的位移向量,返回值见AfterCollision的定义</param>
/// <param name="EndMove">结束碰撞时要做的事情</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.gameTimer = gameMap.Timer;
this.EndMove = EndMove; this.EndMove = EndMove;
@@ -59,7 +58,7 @@ namespace GameEngine


public void MoveObj(IMoveable obj, int moveTime, double direction) public void MoveObj(IMoveable obj, int moveTime, double direction)
{ {
if (obj.IsMoving) //已经移动的物体不能再移动
if (obj.IsMoving) // 已经移动的物体不能再移动
return; return;
new Thread new Thread
( (
@@ -67,28 +66,65 @@ namespace GameEngine
{ {
if (!obj.IsAvailable&&gameTimer.IsGaming) //不能动就直接return,后面都是能动的情况 if (!obj.IsAvailable&&gameTimer.IsGaming) //不能动就直接return,后面都是能动的情况
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)) switch (OnCollision(obj, collisionObj, moveVec))
{ {
case AfterCollision.ContinueCheck: case AfterCollision.ContinueCheck:
@@ -97,80 +133,43 @@ namespace GameEngine
case AfterCollision.Destroyed: case AfterCollision.Destroyed:
Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
isDestroyed = true; isDestroyed = true;
return false;
break;
case AfterCollision.MoveMax: case AfterCollision.MoveMax:
MoveMax(obj, moveVec); MoveMax(obj, moveVec);
moveVec.length = 0; moveVec.length = 0;
break; 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 #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 #endif
}
}.Start();
} }
).Start();
}.Start();
} }
).Start();
} }
} }
}

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

@@ -8,7 +8,7 @@ namespace Preparation.Interface
object MoveLock { get; } object MoveLock { get; }
public int MoveSpeed { get; } public int MoveSpeed { get; }
public long Move(XY moveVec); public long Move(XY moveVec);
protected bool IgnoreCollide(IGameObj targetObj); // 忽略碰撞,在具体类中实现
protected bool IgnoreCollide(IGameObj targetObj); // 忽略碰撞,在具体类中实现
public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞 public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞
{ {
if (targetObj == null) if (targetObj == null)


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

@@ -16,7 +16,7 @@ namespace Preparation.Utility
{ {
return "(" + x.ToString() + "," + y.ToString() + ")"; 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); return (v1.x * v2.x) + (v1.y * v2.y);
} }


Loading…
Cancel
Save