背景:我有一款游戏,我一直主要测试(低规格)笔记本电脑。它运行正常。在xbox上进行测试时,有一种方法在调用时会严重影响性能/ fps。在PC上你不会注意到任何减速/跳过帧。
我在xbox上进行过分析,平均而言,我每次获得GC约1-2次,在游戏运行时需要20-40ms。
当我的慢速方法运行时,我注意到GC速率或持续时间没有变化。
接下来,我尝试在PC上进行性能分析,以确定方法中花费的时间最多。事实证明它正在进行List<T>.Contains()
,所以我创建了自己的类,内部有List<T>
和HashSet<T>
,因此我可以在HashSet<T>
内部使用Contains()
}。
我现在已经达到了这一点,在没有改变算法的情况下,我无法真正想到要调整的其他内容,并且我认为算法就像它将要获得的一样简单。< / p>
我知道我无法对xbox上的方法时间/百分比进行分析,所以我对下一步尝试的内容感到有些不知所措。
我已经包含了下面的代码,用于模拟基于图块的系统中的流水。它每个水砖运行一次,试图将其向下移动,可能通过其他水砖(一般来说)。
问题:我想知道我是否做了明显错误(即会严重影响xbox性能)。调用Func
的速度慢吗?我用Point
对象在哪里拳击?等
大量代码的应用!磁贴本身来自对象池以最小化GC。 InternalThink()
方法导致了所有问题。
public abstract class TileFlowing : TileMovable
{
private FastList<Point> TeleportSwapLocations = new FastList<Point>();
private FastList<Point> PossibleMoveLocations = new FastList<Point>();
private FastQueue<Point> PositionsToCheck = new FastQueue<Point>();
private FastList<Point> PositionsChecked = new FastList<Point>();
private static Comparison<Point> _PossibleMoveComparer;
public bool Static = false;
protected abstract Func<Point, int> PossibleMoveLocationOrdering { get; }
protected abstract Func<Point, Point, bool, bool> MoveSidewaysFunc { get; }
protected abstract int MaxUnitsWithin { get; }
protected abstract int Weight { get; }
public int UnitsWithin;
public int AvailableToFlowThrough = 0;
protected virtual bool RecurseTilesUp { get { return true; } }
protected virtual bool RecurseTilesDown { get { return true; } }
protected virtual bool RecurseTilesLeft { get { return true; } }
protected virtual bool RecurseTilesRight { get { return true; } }
public TileFlowing()
: base()
{
}
public override void LoadContent(Components.TileGridManagement.GameGrid Owner)
{
base.LoadContent(Owner);
_PossibleMoveComparer = (Point P1, Point P2) =>
{
int Result = PossibleMoveLocationOrdering(P1) -
PossibleMoveLocationOrdering(P2);
if (Result == 0)
Result = (IsSameType(P1) ? (_Owner[P1] as TileFlowing).UnitsWithin : 0) -
(IsSameType(P2) ? (_Owner[P2] as TileFlowing).UnitsWithin : 0);
return Result;
};
}
public override void ResetProperties()
{
base.ResetProperties();
Static = false;
UnitsWithin = MaxUnitsWithin;
AvailableToFlowThrough = 0;
}
public override void CopyProperties(Tile SourceTile)
{
base.CopyProperties(SourceTile);
Static = (SourceTile as TileFlowing).Static;
UnitsWithin = (SourceTile as TileFlowing).UnitsWithin;
AvailableToFlowThrough = (SourceTile as TileFlowing).AvailableToFlowThrough;
}
public override void Think()
{
base.Think();
InternalThink(false, false);
}
public override void FactoryThink()
{
base.FactoryThink();
InternalThink(true, false);
}
public void FlowThink(bool CalledFromFactoryThink)
{
InternalThink(CalledFromFactoryThink, true);
}
private bool IsSameType(Point Position)
{
return IsSameType(Position.X, Position.Y);
}
private bool IsSameType(int X, int Y)
{
return _Owner[X, Y] != null && _Owner[X, Y].GetType() == GetType();
}
private bool IsDifferentFlowingTile(Point Position)
{
return IsDifferentFlowingTile(Position.X, Position.Y);
}
private bool IsDifferentFlowingTile(int X, int Y)
{
return !IsSameType(X, Y) && _Owner[X, Y] is TileFlowing;
}
protected void CheckPosition(Point PositionToCheck, Point TilePosition, bool CalledFromFactoryThink, bool CalledFromFlowThink,
ref FastList<Point> PossibleMoveLocations, ref FastList<Point> TeleportSwapLocations, ref FastQueue<Point> PositionsToCheck,
Func<Point, Point, bool, bool> ClearCheckFunc)
{
if (IsSameType(PositionToCheck))
{
if (!PositionsToCheck.Contains(PositionToCheck))
PositionsToCheck.Enqueue(PositionToCheck);
}
else if (_Owner[PositionToCheck] is TileFlowing && (ClearCheckFunc == null || ClearCheckFunc(PositionToCheck, TilePosition, CalledFromFactoryThink)))
{
// If we weigh more than the other tile, or we're called from the factory think (are under pressure)
if ((_Owner[PositionToCheck] as TileFlowing).Weight < Weight || CalledFromFactoryThink)
{
if (!(_Owner[PositionToCheck] as TileFlowing).Static || !CalledFromFlowThink)
PossibleMoveLocations.Add(PositionToCheck);
}
}
else if (_Owner.IsClear(PositionToCheck) && (ClearCheckFunc == null || ClearCheckFunc(PositionToCheck, TilePosition, CalledFromFactoryThink)))
{
PossibleMoveLocations.Add(PositionToCheck);
}
}
private int PossibleMoveLocationsComparer(Point P1, Point P2)
{
return (PossibleMoveLocationOrdering(P1) - PossibleMoveLocationOrdering(P2)) * 1000 +
((IsSameType(P1) ? (_Owner[P1] as TileFlowing).UnitsWithin : 0) - (IsSameType(P2) ? (_Owner[P2] as TileFlowing).UnitsWithin : 0)) * 100;
}
protected void InternalThink(bool CalledFromFactoryThink, bool CalledFromFlowThink)
{
AvailableToFlowThrough = 0;
TeleportSwapLocations.Clear();
PossibleMoveLocations.Clear();
PositionsToCheck.Clear();
PositionsChecked.Clear();
PositionsToCheck.Enqueue(Position);
while (PositionsToCheck.Count != 0)
{
Point PositionToCheck = PositionsToCheck.Dequeue();
if (!PositionsChecked.Contains(PositionToCheck) &&
((_Owner[PositionToCheck] as TileFlowing).AvailableToFlowThrough < MaxUnitsWithin || CalledFromFactoryThink))
{
if (((_Owner[PositionToCheck] as TileFlowing).Static && !CalledFromFactoryThink))
continue;
if (PositionToCheck != Position)
{
(_Owner[PositionToCheck] as TileFlowing).AvailableToFlowThrough++;
}
PositionsChecked.Add(PositionToCheck);
if ((_Owner[PositionToCheck] as TileFlowing).UnitsWithin < MaxUnitsWithin && PositionToCheck != Position)
{
PossibleMoveLocations.Add(PositionToCheck);
if (CalledFromFactoryThink && (_Owner[PositionToCheck] as TileFlowing).UnitsWithin + UnitsWithin <= MaxUnitsWithin)
continue;
}
// Check below
Point PosBelow = new Point(PositionToCheck.X + TileDirection.Down.X, PositionToCheck.Y + TileDirection.Down.Y);
CheckPosition(PosBelow, Position, CalledFromFactoryThink, CalledFromFlowThink, ref PossibleMoveLocations, ref TeleportSwapLocations, ref PositionsToCheck, null);
// Check one horizontal direction
Point RandHDir = Randomiser.GetHDirection();
Point RandHPos = new Point(RandHDir.X + PositionToCheck.X, RandHDir.Y + PositionToCheck.Y);
CheckPosition(RandHPos, Position, CalledFromFactoryThink, CalledFromFlowThink, ref PossibleMoveLocations, ref TeleportSwapLocations, ref PositionsToCheck, MoveSidewaysFunc);
// Check the other horizontal direction
Point OtherHDir = new Point(-RandHDir.X, RandHDir.Y);
Point OtherHPos = new Point(OtherHDir.X + PositionToCheck.X, OtherHDir.Y + PositionToCheck.Y);
CheckPosition(OtherHPos, Position, CalledFromFactoryThink, CalledFromFlowThink, ref PossibleMoveLocations, ref TeleportSwapLocations, ref PositionsToCheck, MoveSidewaysFunc);
// Check above if appropriate
Point AbovePos = new Point(PositionToCheck.X + TileDirection.Up.X, PositionToCheck.Y + TileDirection.Up.Y);
if (TileDirection.Below(AbovePos, Position) || CalledFromFactoryThink)
{
CheckPosition(AbovePos, Position, CalledFromFactoryThink, CalledFromFlowThink, ref PossibleMoveLocations, ref TeleportSwapLocations, ref PositionsToCheck, null);
}
}
}
PossibleMoveLocations.Sort(_PossibleMoveComparer);
bool Moved = false;
if (PossibleMoveLocations.Count != 0)
{
if (CalledFromFactoryThink)
{
while (UnitsWithin != 0 && PossibleMoveLocations.Count != 0)
{
int OldUnitsWithin = UnitsWithin;
Moved = IterateTeleport(CalledFromFactoryThink, ref PossibleMoveLocations, (P) => !IsDifferentFlowingTile(P), Moved);
if (UnitsWithin == OldUnitsWithin)
{
Moved = IterateTeleport(CalledFromFactoryThink, ref PossibleMoveLocations, (P) => IsDifferentFlowingTile(P), Moved);
}
PossibleMoveLocations.RemoveAll(P => IsSameType(P) && (_Owner[P] as TileFlowing).UnitsWithin == MaxUnitsWithin);
}
}
else
{
Moved = Moved || Teleport(PossibleMoveLocations[0]);
}
// If we did move and not because we were forced to then mark all mercury tiles above and left or right as not static.
if (!CalledFromFactoryThink)
{
_Owner.RecurseTiles(Position,
(P) => RecurseTilesUp && IsSameType(P.X + TileDirection.Up.X, P.Y + TileDirection.Up.Y),
(P) => RecurseTilesDown && IsSameType(P.X + TileDirection.Down.X, P.Y + TileDirection.Down.Y),
(P) => RecurseTilesLeft && IsSameType(P.X + TileDirection.Left.X, P.Y + TileDirection.Left.Y),
(P) => RecurseTilesRight && IsSameType(P.X + TileDirection.Right.X, P.Y + TileDirection.Right.Y),
(P) =>
{
if (IsSameType(P))
(_Owner[P] as TileFlowing).Static = false;
});
}
}
else
{
// Mark this tile as static if we didn't move, no water moved through this tile and we have all the units we can take.
Static = (AvailableToFlowThrough == 0) && (UnitsWithin == MaxUnitsWithin); // TODO: 9 Fix flowing tiles becoming static and getting stuck
}
if (!Moved)
{
// If we haven't moved
if (TeleportSwapLocations.Count != 0)
{
Moved = TeleportSwap(TeleportSwapLocations[0]);
}
if(!Moved)
{
// If we didn't move, undo checked tiles
foreach (var CheckedPosition in PositionsChecked)
{
(_Owner[CheckedPosition] as TileFlowing).AvailableToFlowThrough--;
}
}
}
}
private bool IterateTeleport(bool CalledFromFactoryThink, ref FastList<Point> SortedPossibleMoveLocations, Func<Point, bool> PossibleMoveLocationsFilter, bool Moved)
{
foreach (var PossibleMoveLocation in SortedPossibleMoveLocations)
{
if (PossibleMoveLocationsFilter(PossibleMoveLocation))
{
if (IsDifferentFlowingTile(PossibleMoveLocation))
{
bool OldStatic = Static;
Static = true;
(_Owner[PossibleMoveLocation] as TileFlowing).FlowThink(CalledFromFactoryThink);
Static = OldStatic;
}
bool TeleportResult = Teleport(PossibleMoveLocation);
Moved = Moved || TeleportResult;
if (TeleportResult)
break;
}
}
return Moved;
}
protected bool TeleportSwap(Point NewPosition)
{
TileFlowing OurNewTile = (TileFlowing)Tile.GetNewTileFromStore(this);
OurNewTile.CopyProperties(this);
OurNewTile.Position = NewPosition;
TileFlowing ReplacedTile = (TileFlowing)Tile.GetNewTileFromStore(_Owner[NewPosition]);
ReplacedTile.CopyProperties(_Owner[NewPosition]);
ReplacedTile.Position = Position;
_Owner.ClearTile(NewPosition);
_Owner.AddTileToGrid(OurNewTile);
_Owner.ClearTile(Position);
_Owner.AddTileToGrid(ReplacedTile);
UnitsWithin = 0;
return true;
}
protected bool Teleport(Point NewPosition)
{
if (IsDifferentFlowingTile(NewPosition))
{
return TeleportSwap(NewPosition);
}
else
{
TileFlowing NewTile;
bool RemovedAllUnits = false;
int NewPositionUnits = IsSameType(NewPosition) ? (_Owner[NewPosition] as TileFlowing).UnitsWithin : 0;
int UnitsToRemove = Math.Min(UnitsWithin,
Math.Max(1,
Math.Min(Math.Abs(UnitsWithin - NewPositionUnits) / 2,
MaxUnitsWithin - NewPositionUnits)));
UnitsWithin -= UnitsToRemove;
if (IsSameType(NewPosition))
{
(_Owner[NewPosition] as TileFlowing).UnitsWithin += UnitsToRemove;
}
else
{
NewTile = (TileFlowing)Tile.GetNewTileFromStore(this);
NewTile.Position = NewPosition;
NewTile.UnitsWithin = UnitsToRemove;
_Owner.AddTileToGrid(NewTile);
}
if (UnitsWithin == 0)
{
_Owner.ClearTile(Position);
RemovedAllUnits = true;
}
return RemovedAllUnits;
}
}
}
答案 0 :(得分:0)
我认为您可以使用Stopwatch
进行一些基本的分析。你可以考虑设置一个毫秒或迭代限制&#39;在思考方法上 - 所以它不会超过5毫秒/ 200次迭代或其他任何工作。
答案 1 :(得分:0)
这里只是猜测,但是......
在while
循环中重复查找和转换对我来说有点狡猾:_Owner[PositionToCheck] as TileFlowing
。我很想把它作为变量拉出来,看看会发生什么。