原理在上圖右半部分的注釋中描述很清楚;我依據此原理寫了個通用判斷朝向的方法,精華哦:
/// <summary>
/// 通過正切值獲取精靈的朝向代號
/// </summary>
/// <param name="targetX">目標點的X值</param>
/// <param name="targetY">目標點的Y值</param>
/// <param name="currentX">當前點的X值</param>
/// <param name="currentY">當前點的Y值</param>
/// <returns>精靈朝向代號(以北為0順時針依次1,2,3,4,5,6,7)</returns>
public static double GetDirectionByTan(double targetX, double targetY, double currentX, double currentY) {
double tan = (targetY - currentY) / (targetX - currentX);
if (Math.Abs(tan) >= Math.Tan(Math.PI * 3 / 8) && targetY <= currentY) {
return 0;
} else if (Math.Abs(tan) > Math.Tan(Math.PI / 8) && Math.Abs(tan) < Math.Tan(Math.PI * 3 / 8) && targetX > currentX && targetY < currentY) {
return 1;
} else if (Math.Abs(tan) <= Math.Tan(Math.PI / 8) && targetX >= currentX) {
return 2;
} else if (Math.Abs(tan) > Math.Tan(Math.PI / 8) && Math.Abs(tan) < Math.Tan(Math.PI * 3 / 8) && targetX > currentX && targetY > currentY) {
return 3;
} else if (Math.Abs(tan) >= Math.Tan(Math.PI * 3 / 8) && targetY >= currentY) {
return 4;
} else if (Math.Abs(tan) > Math.Tan(Math.PI / 8) && Math.Abs(tan) < Math.Tan(Math.PI * 3 / 8) && targetX < currentX && targetY > currentY) {
return 5;
} else if (Math.Abs(tan) <= Math.Tan(Math.PI / 8) && targetX <= currentX) {
return 6;
} else if (Math.Abs(tan) > Math.Tan(Math.PI / 8) && Math.Abs(tan) < Math.Tan(Math.PI * 3 / 8) && targetX < currentX && targetY < currentY) {
return 7;
} else {
return 0;
}
}
由於Math.Tan()函數的參數為弧度單位,因此需要將角度通過公式換算成弧度,並且至於之中的比較算法邏輯是否為最優,仍然那句老話:仁者見仁,智者見智。或許你寫的算法更優秀呢?
有了該方法,接下來就是在鼠標左鍵點擊事件中獲取目標點,並且將主角的當前動作切換成跑步狀態,並啟動A*尋路:
private void CarrIEr_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
Point p = e.GetPosition(Map); //點擊的地方在Map中的坐標點
Spirit.Action = Actions.Run; //主角動作切換成跑步狀態
AStarMoveTo(p); //開始尋路
}
上兩節的AstarMoveTo()方法中的Storyboard動畫只創建X,Y序列點,而為了實現角色時時朝向,我們還需要創建對應的角色方向(Direction)序列點,因此我們還需要對本節中的AstarMoveTo()方法進行如下改進:
private void AStarMoveTo(Point p) {
……
//創建X軸方向逐幀動畫
DoubleAnimationUsingKeyFrames keyFramesAnimationX = new DoubleAnimationUsingKeyFrames();
//總共花費時間 = path.Count * cost
keyFramesAnimationX.Duration = new Duration(TimeSpan.FromMilliseconds(path.Count * cost));
Storyboard.SetTarget(keyFramesAnimationX, Spirit);
Storyboard.SetTargetProperty(keyFramesAnimationX, new PropertyPath("X"));
//創建Y軸方向逐幀動畫
DoubleAnimationUsingKeyFrames keyFramesAnimationY = new DoubleAnimationUsingKeyFrames();
keyFramesAnimationY.Duration = new Duration(TimeSpan.FromMilliseconds(path.Count * cost));
Storyboard.SetTarget(keyFramesAnimationY, Spirit);
Storyboard.SetTargetProperty(keyFramesAnimationY, new PropertyPath("Y"));
//創建朝向動畫
DoubleAnimationUsingKeyFrames keyFramesAnimationDirection = new DoubleAnimationUsingKeyFrames();
keyFramesAnimationDirection.Duration = new Duration(TimeSpan.FromMilliseconds(path.Count * cost));
Storyboard.SetTarget(keyFramesAnimationDirection, Spirit);
Storyboard.SetTargetProperty(keyFramesAnimationDirection, new PropertyPath("Direction"));
for (int i = 0; i < framePosition.Count(); i++) {
//加入X軸方向的勻速關鍵幀
LinearDoubleKeyFrame keyFrame = new LinearDoubleKeyFrame();
//平滑銜接動畫(將尋路坐標系中的坐標放大回地圖坐標系中的坐標)
keyFrame.Value = i == 0 ? Spirit.X : framePosition[i].X * GridSize;
keyFrame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(cost * i));
keyFramesAnimationX.KeyFrames.Add(keyFrame);
//加入X軸方向的勻速關鍵幀
keyFrame = new LinearDoubleKeyFrame();
keyFrame.Value = i == 0 ? Spirit.Y : framePosition[i].Y * GridSize;
keyFrame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(cost * i));
keyFramesAnimationY.KeyFrames.Add(keyFrame);
//加入朝向勻速關鍵幀
keyFrame = new LinearDoubleKeyFrame();
keyFrame.Value = i == framePosition.GetUpperBound(0)
? Super.GetDirectionByTan(framePosition[i].X, framePosition[i].Y, framePosition[i - 1].X, framePosition[i - 1].Y)
: Super.GetDirectionByTan(framePosition[i + 1].X, framePosition[i + 1].Y, framePosition[i].X, framePosition[i].Y)
;
keyFrame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(cost * i));
keyFramesAnimationDirection.KeyFrames.Add(keyFrame);
}
storyboard.Children.Add(keyFramesAnimationX);
storyboard.Children.Add(keyFramesAnimationY);
storyboard.Children.Add(keyFramesAnimationDirection);
……
}