技术开发 频道

开发Silverlight游戏教程:A*寻径动态动画

    【IT168技术文档】

    转载自:深蓝色右手的博客

    本节将紧接着上一节,在它的基础上实现鼠标点击动态创建完美的A*寻路动画。(模拟游戏中人物的真实移动,这次可是有障碍物的,可以说基本上完成了人物移动引擎的一半了呢)

    首先,在上一节的代码前部分加入一个叫做player的圆形作为我们将要控制的对象(模拟游戏中的主角,下文均称之为“主角”):

 Ellipse player = new Ellipse(); //用一个圆来模拟目标对象

private void InitPlayer() {

player.Fill = new SolidColorBrush(Colors.Blue);

player.Width = GridSize;

player.Height = GridSize;

Carrier.Children.Add(player);

//开始位置(1,1)

Canvas.SetLeft(player, GridSize);

Canvas.SetTop(player, 5 * GridSize);

}

接下来,我们在窗体构造函数中加入InitPlayer()方法:

public Window8() {

InitializeComponent();

ResetMatrix(); //初始化二维矩阵

InitPlayer(); //初始化目标对象

}


    如果大家对上一节的障碍物觉得还不过瘾,可以随便再添加,直到你觉得足够复杂来测试我们的A*动画,这里我也在上一节设定的障碍物基础上进行了一些改进,稍微复杂了些。那么我们直接进入本节的重点:如何实现鼠标点击窗体中任意点,实现主角从它当前位置移动到鼠标点击的点,并且幽雅平滑的通过A*用最短的路径越过所有的障碍物,这整个过程都是动态创建的,没有一点xaml的痕迹,嘿嘿,小得意了一下呢。当然讲解之前还是请各位朋友先熟悉前面章节的动画原理,否则还是比较难理解的。接下来看看代码:

 

private void Carrier_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {

Point p = e.GetPosition(Carrier);

//进行坐标系缩小

int start_x = (int)Canvas.GetLeft(player) / GridSize;

int start_y = (int)Canvas.GetTop(player) / GridSize;

Start = new System.Drawing.Point(start_x, start_y); //设置起点坐标

int end_x = (int)p.X / GridSize;

int end_y = (int)p.Y / GridSize;

End = new System.Drawing.Point(end_x, end_y); //设置终点坐标

 

PathFinder = new PathFinderFast(Matrix);

PathFinder.Formula = HeuristicFormula.Manhattan; //使用我个人觉得最快的曼哈顿A*算法

List<PathFinderNode> path = PathFinder.FindPath(Start, End); //开始寻径

 

if (path == null) {

MessageBox.Show("路径不存在!");

} else {

Point[] framePosition = new Point[path.Count]; //定义关键帧坐标集

for (int i = path.Count - 1; i >= 0; i--) {

//从起点开始以GridSize为单位,顺序填充关键帧坐标集,并进行坐标系放大

framePosition[path.Count - 1 - i] = new Point(path[i].X * GridSize, path[i].Y * GridSize);

}

//创建故事板

Storyboard storyboard = new Storyboard();

int cost = 100; //每移动一个小方格(20*20)花费100毫秒

//创建X轴方向逐帧动画

DoubleAnimationUsingKeyFrames keyFramesAnimationX = new DoubleAnimationUsingKeyFrames();

//总共花费时间 = path.Count * cost

keyFramesAnimationX.Duration = new Duration(TimeSpan.FromMilliseconds(path.Count * cost));

Storyboard.SetTarget(keyFramesAnimationX, player);

Storyboard.SetTargetProperty(keyFramesAnimationX, new PropertyPath("(Canvas.Left)"));

//创建Y轴方向逐帧动画

DoubleAnimationUsingKeyFrames keyFramesAnimationY = new DoubleAnimationUsingKeyFrames();

keyFramesAnimationY.Duration = new Duration(TimeSpan.FromMilliseconds(path.Count * cost));

Storyboard.SetTarget(keyFramesAnimationY, player);

Storyboard.SetTargetProperty(keyFramesAnimationY, new PropertyPath("(Canvas.Top)"));

for (int i = 0; i < framePosition.Count(); i++) {

//加入X轴方向的匀速关键帧

LinearDoubleKeyFrame keyFrame = new LinearDoubleKeyFrame();

keyFrame.Value = i == 0 ? Canvas.GetLeft(player) : framePosition[i].X; //平滑衔接动画

keyFrame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(cost * i));

keyFramesAnimationX.KeyFrames.Add(keyFrame);

//加入X轴方向的匀速关键帧

keyFrame = new LinearDoubleKeyFrame();

keyFrame.Value = i == 0 ? Canvas.GetTop(player) : framePosition[i].Y;

keyFrame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(cost * i));

keyFramesAnimationY.KeyFrames.Add(keyFrame);

}

storyboard.Children.Add(keyFramesAnimationX);

storyboard.Children.Add(keyFramesAnimationY);

//添加进资源

if (!Resources.Contains("storyboard")) {

Resources.Add("storyboard", storyboard);

}

//故事板动画开始

storyboard.Begin();

//用白色点记录移动轨迹

for (int i = path.Count - 1; i >= 0; i--) {

rect = new Rectangle();

rect.Fill = new SolidColorBrush(Colors.Snow);

rect.Width = 5;

rect.Height = 5;

Carrier.Children.Add(rect);

Canvas.SetLeft(rect, path[i].X * GridSize);

Canvas.SetTop(rect, path[i].Y * GridSize);

}

}

}

0
相关文章