using System;
using System.Threading;
using Preparation.Interface;
using Preparation.Utility;
using Timothy.FrameRateTask;
namespace GameEngine
{
public class MoveEngine
{
///
/// 碰撞结束后要做的事情
///
public enum AfterCollision
{
ContinueCheck = 0, // 碰撞后继续检查其他碰撞,暂时没用
MoveMax = 1, // 行走最远距离
Destroyed = 2 // 物体已经毁坏
}
private readonly ITimer gameTimer;
private readonly Action EndMove;
public readonly uint[,] ProtoGameMap;
public PlaceType GetPlaceType(XY Position)
{
try
{
return (PlaceType)ProtoGameMap[Position.x / GameData.numOfPosGridPerCell, Position.y / GameData.numOfPosGridPerCell];
}
catch
{
return PlaceType.Null;
}
}
public IGameObj? CheckCollision(IMoveable obj, XY Pos)
{
return collisionChecker.CheckCollision(obj, Pos);
}
private readonly CollisionChecker collisionChecker;
private readonly Func OnCollision;
///
/// Constrctor
///
/// 游戏地图
/// 发生碰撞时要做的事情,第一个参数为移动的物体,第二个参数为撞到的物体,第三个参数为移动的位移向量,返回值见AfterCollision的定义
/// 结束碰撞时要做的事情
public MoveEngine(
IMap gameMap,
Func OnCollision,
Action EndMove
)
{
this.ProtoGameMap = gameMap.ProtoGameMap;
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 + moveVec;
double maxLen = collisionChecker.FindMax(obj, nextPos, moveVec);
maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.numOfStepPerSecond);
obj.MovingSetPos(new XY(moveVec, maxLen));
}
public void MoveObj(IMoveable obj, int moveTime, double direction, long threadNum)
{
if (!gameTimer.IsGaming) return;
lock (obj.ActionLock)
{
if (!obj.IsAvailableForMove) return;
obj.IsMoving = true;
}
new Thread
(
() =>
{
double moveVecLength = 0.0;
XY res = new(direction, moveVecLength);
double deltaLen = 0; // 转向,并用deltaLen存储行走的误差
IGameObj? collisionObj = null;
bool isDestroyed = false;
bool flag; // 循环标志
do
{
flag = false;
collisionObj = collisionChecker.CheckCollision(obj, obj.Position);
if (collisionObj == null)
break;
switch (OnCollision(obj, collisionObj, res))
{
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:
break;
}
} while (flag);
if (!isDestroyed)
{
new FrameRateTaskExecutor(
() => gameTimer.IsGaming && obj.CanMove && !obj.IsRemoved && obj.IsMoving,
() =>
{
moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond;
res = new XY(direction, moveVecLength);
//对人特殊处理
if (threadNum > 0 && obj.StateNum != threadNum) return false;
// 越界情况处理:如果越界,则与越界方块碰撞
bool flag; // 循环标志
do
{
flag = false;
collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res);
if (collisionObj == null)
break;
switch (OnCollision(obj, collisionObj, res))
{
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:
if (threadNum == 0 || obj.StateNum == threadNum)
MoveMax(obj, res);
moveVecLength = 0;
res = new XY(direction, moveVecLength);
break;
}
} while (flag);
if (threadNum == 0 || obj.StateNum == threadNum)
deltaLen += moveVecLength - Math.Sqrt(obj.MovingSetPos(res));
return true;
},
GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond,
() =>
{
int leftTime = moveTime % (GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond);
bool flag;
do
{
flag = false;
if (!isDestroyed)
{
moveVecLength = deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell;
res = new XY(direction, moveVecLength);
if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null)
{
if (threadNum == 0 || obj.StateNum == threadNum)
obj.MovingSetPos(res);
}
else
{
switch (OnCollision(obj, collisionObj, res))
{
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:
if (threadNum == 0 || obj.StateNum == threadNum)
MoveMax(obj, res);
moveVecLength = 0;
res = new XY(direction, moveVecLength);
break;
}
}
}
} while (flag);
if (leftTime > 0 && obj.IsMoving)
{
Thread.Sleep(leftTime); // 多移动的在这里补回来
}
lock (obj.ActionLock)
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();
}
}
}