傳統「坐標還原法」會造成畫面彈跳;本節採用「啟發式預測法」:直線移動中即時檢測前方單元格,發現障礙物才切換 A* 繞行。
private bool WillCollide()
{
switch ((int)Spirit.Direction)
{
case 0: return Matrix[(int)(Spirit.X/GridSize), (int)(Spirit.Y/GridSize)-1] == 0;
case 1: return Matrix[(int)(Spirit.X/GridSize)+1, (int)(Spirit.Y/GridSize)-1] == 0;
case 2: return Matrix[(int)(Spirit.X/GridSize)+1, (int)(Spirit.Y/GridSize)] == 0;
case 3: return Matrix[(int)(Spirit.X/GridSize)+1, (int)(Spirit.Y/GridSize)+1] == 0;
case 4: return Matrix[(int)(Spirit.X/GridSize), (int)(Spirit.Y/GridSize)+1] == 0;
case 5: return Matrix[(int)(Spirit.X/GridSize)-1, (int)(Spirit.Y/GridSize)+1] == 0;
case 6: return Matrix[(int)(Spirit.X/GridSize)-1, (int)(Spirit.Y/GridSize)] == 0;
case 7: return Matrix[(int)(Spirit.X/GridSize)-1, (int)(Spirit.Y/GridSize)-1] == 0;
default: return true;
}
}
關鍵變量 totalcost 計算兩點距離對應毫秒數,配合 DoubleAnimation 於 Storyboard 時間軸上實現 360° 全方位等速移動:
int totalcost = (int)Math.Sqrt(Math.Pow(p.X-Spirit.X,2)+Math.Pow(p.Y-Spirit.Y,2))/GridSize*UnitMoveCost;
double direction = Super.GetDirectionByTan(p.X,p.Y,Spirit.X,Spirit.Y);
var anim = new DoubleAnimation(direction, direction, new Duration(TimeSpan.FromMilliseconds(totalcost)));
Storyboard.SetTarget(anim, Spirit);
Storyboard.SetTargetProperty(anim, new PropertyPath("Direction"));
storyboard.Children.Add(anim);
storyboard.Begin();
if (ArriveTarget()) { Spirit.Action = Actions.Stop; }
else if (!Spirit.IsAStarMoving && WillCollide())
{
AStarMoveTo(Spirit.Destination);
Spirit.IsAStarMoving = true; // 防重複啟動
}
IsAStarMoving 為直線/A* 切換樞紐:A* 一旦啟動必達終點,無需再判斷碰撞。