实例介绍
【实例截图】
【核心代码】
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.IO;
using QX.Game.PathFinder;
using System.Windows.Media.Animation;
using WPFGameTutorial.Controls;
using WPFGameTutorial.Logic;
namespace WPFGameTutorial {
public partial class Window20 : Window {
public Window20() {
InitializeComponent();
ResizeMode = ResizeMode.CanMinimize; //设置窗口不能最大化
InitSpirit();//初始化主角
InitMask(); //初始化地图遮罩层
InitMap(); //初始化地图
InitMatrix(); //初始化障碍物
//注册界面刷新线程
CompositionTarget.Rendering = new EventHandler(Timer_Tick);
}
QXSpirit Spirit = new QXSpirit();
private void InitSpirit() {
Spirit.X = 300;
Spirit.Y = 400;
Spirit.Timer.Interval = TimeSpan.FromMilliseconds(150);
//设置角色身体及装备
Spirit.Equipment[0] = 0;
Spirit.Equipment[1] = 1;
Spirit.EachActionFrameRange = new int[] { 5, 8, 7, 6, 8 }; //这5个数字在第十七节中讲了多次
Spirit.DirectionFrameNum = 34; //每个方向行上有34列(第十七节中有说明)
Spirit.TotalWidth = 5100;
Spirit.TotalHeight = 1200;
Spirit.CenterX = 75;
Spirit.CenterY = 110;
//加载角色图片源(具体参数意思可以到QXSpirit中了解)
Spirit.Source = Super.EquipPart(Spirit.Equipment, Spirit.DirectionNum, Spirit.DirectionFrameNum, Spirit.TotalWidth, Spirit.TotalHeight, Spirit.SingleWidth, Spirit.SingleHeight);
Carrier.Children.Add(Spirit);
}
//创建遮罩层
Image Mask = new Image();
private void InitMask() {
Mask.Width = 202;
Mask.Height = 395;
Mask.Source = new BitmapImage((new Uri(@"Map\Mask3.png", UriKind.Relative)));
Mask.Opacity = 0.7;
Carrier.Children.Add(Mask);
Canvas.SetZIndex(Mask, 612);
}
Image Map = new Image();
private void InitMap() {
Map.Width = 1750;
Map.Height = 1440;
Map.Source = new BitmapImage((new Uri(@"Map\Map4.jpg", UriKind.Relative)));
Carrier.Children.Add(Map);
Canvas.SetLeft(Map, -320);
Canvas.SetTop(Map, -200);
Map.SetValue(Canvas.ZIndexProperty, -1);
}
Rectangle rect;
private void InitMatrix() {
for (int y = 0; y < Matrix.GetUpperBound(1); y ) {
for (int x = 0; x < Matrix.GetUpperBound(0); x ) {
//默认值可以通过在矩阵中用1表示
Matrix[x, y] = 1;
}
}
//构建障碍物
for (int y = 60; y <= 68; y ) {
for (int x = 80; x <= 100; x ) {
//障碍物在矩阵中用0表示
Matrix[x, y] = 0;
//rect = new Rectangle();
////目前暂时不新创一个自定义控件,而把坐标储存在Tag属性中
//rect.Tag = x "," y;
//rect.Fill = new SolidColorBrush(Colors.GreenYellow);
//rect.Opacity = 0.3;
//rect.Stroke = new SolidColorBrush(Colors.Gray);
//rect.Width = GridSize;
//rect.Height = GridSize;
//Carrier.Children.Add(rect);
//Canvas.SetZIndex(rect, 10001);
}
}
}
int UnitMoveCost = 70; //移动一个单元格距离所需要的时间(毫秒)
int Scrollspeed = 5; //定义地图滚动速度
//游戏窗口刷新主线程间隔事件
private void Timer_Tick(object sender, EventArgs e) {
//窗口中的所有对象物体位置时时改变
AllMove();
//判断主角是否移动到了目标,如果是则动作切换成停止
if (ArriveTarget()) {
Spirit.Action = Actions.Stop;
} else if (!Spirit.IsAStarMoving && WillCollide()) {
//在寻路移动模式中,主角100%会饶过障碍物的,
//因此只有在非寻路模式中才需要时时判断主角是否将要碰撞障碍物
AStarMoveTo(Spirit.Destination);
Spirit.IsAStarMoving = true;
}
}
//判断是否将要碰撞到障碍物(障碍物预测法)
private bool WillCollide() {
switch ((int)Spirit.Direction) {
case 0:
return Matrix[(int)(Spirit.X / GridSize), (int)(Spirit.Y / GridSize) - 1] == 0 ? true : false;
case 1:
return Matrix[(int)(Spirit.X / GridSize) 1, (int)(Spirit.Y / GridSize) - 1] == 0 ? true : false;
case 2:
return Matrix[(int)(Spirit.X / GridSize) 1, (int)(Spirit.Y / GridSize)] == 0 ? true : false;
case 3:
return Matrix[(int)(Spirit.X / GridSize) 1, (int)(Spirit.Y / GridSize) 1] == 0 ? true : false;
case 4:
return Matrix[(int)(Spirit.X / GridSize), (int)(Spirit.Y / GridSize) 1] == 0 ? true : false;
case 5:
return Matrix[(int)(Spirit.X / GridSize) - 1, (int)(Spirit.Y / GridSize) 1] == 0 ? true : false;
case 6:
return Matrix[(int)(Spirit.X / GridSize) - 1, (int)(Spirit.Y / GridSize)] == 0 ? true : false;
case 7:
return Matrix[(int)(Spirit.X / GridSize) - 1, (int)(Spirit.Y / GridSize) - 1] == 0 ? true : false;
default:
return true;
}
}
//全体对象进行相对移动
private void AllMove() {
double MapLeft = Canvas.GetLeft(Map);
double MapTop = Canvas.GetTop(Map);
//鼠标进入地图边缘引导地图移动
switch (GetMouseArea()) {
case 0:
if (MapTop < 0) {
Canvas.SetTop(Map, Canvas.GetTop(Map) Scrollspeed);
}
break;
case 1:
if (MapTop < 0) {
Canvas.SetTop(Map, Canvas.GetTop(Map) Scrollspeed);
}
if (Map.Width MapLeft > this.ActualWidth) {
Canvas.SetLeft(Map, Canvas.GetLeft(Map) - Scrollspeed);
}
break;
case 2:
if (Map.Width MapLeft > this.ActualWidth) {
Canvas.SetLeft(Map, Canvas.GetLeft(Map) - Scrollspeed);
}
break;
case 3:
if (Map.Width MapLeft > this.ActualWidth) {
Canvas.SetLeft(Map, Canvas.GetLeft(Map) - Scrollspeed);
}
if (Map.Height MapTop > this.ActualHeight) {
Canvas.SetTop(Map, Canvas.GetTop(Map) - Scrollspeed);
}
break;
case 4:
if (Map.Height MapTop > this.ActualHeight) {
Canvas.SetTop(Map, Canvas.GetTop(Map) - Scrollspeed);
}
break;
case 5:
if (Map.Height MapTop > this.ActualHeight) {
Canvas.SetTop(Map, Canvas.GetTop(Map) - Scrollspeed);
}
if (MapLeft < 0) {
Canvas.SetLeft(Map, Canvas.GetLeft(Map) Scrollspeed);
}
break;
case 6:
if (MapLeft < 0) {
Canvas.SetLeft(Map, Canvas.GetLeft(Map) Scrollspeed);
}
break;
case 7:
if (MapTop < 0) {
Canvas.SetTop(Map, Canvas.GetTop(Map) Scrollspeed);
}
if (MapLeft < 0) {
Canvas.SetLeft(Map, Canvas.GetLeft(Map) Scrollspeed);
}
break;
}
//遮罩层跟随移动
Canvas.SetLeft(Mask, MapLeft 793);
Canvas.SetTop(Mask, MapTop 217);
//主角跟随地图同时移动
Canvas.SetLeft(Spirit, Spirit.X - Spirit.CenterX MapLeft);
Canvas.SetTop(Spirit, Spirit.Y - Spirit.CenterY MapTop);
Canvas.SetZIndex(Spirit, (int)Spirit.Y); //时时的更新它的层次(画家算法)
//所有障碍物实体同样跟随移动(实际中不需要显示出来,这里只为了测试)
foreach (UIElement uie in Carrier.Children) {
if (uie is Rectangle) {
Rectangle r = uie as Rectangle;
Point p = GetPointFromTag(r.Tag);
Canvas.SetLeft(r, MapLeft p.X * GridSize);
Canvas.SetTop(r, MapTop p.Y * GridSize);
}
}
}
//判断是否移动到目的地
private bool ArriveTarget() {
return (storyboard != null && storyboard.GetCurrentProgress() == 1) ? true : false;
}
//从方块障碍物的Tag属性中分离出它的坐标Point
private Point GetPointFromTag(object tag) {
string[] str = tag.ToString().Split(new char[] { ',' });
return new Point(Convert.ToDouble(str[0]), Convert.ToDouble(str[1]));
}
//根据鼠标的位置获取鼠标所处的区域代号
//0代表正上方区域(即12点位置)然后其他7个区域按顺时针依次为1,2,3,4,5,6,7
int distance = 80; //定义距离边缘多少即开始牵引地图
private int GetMouseArea() {
Point MousePosition = Mouse.GetPosition(Carrier); //获取鼠标当前处于窗口中的位置
int result = -1;
//如果鼠标未超出窗口
if (MousePosition.X >= 0 && MousePosition.Y >= 0) {
//根据8种情况返回8个数字
if (MousePosition.X >= 190 && MousePosition.X <= 570) {
if (MousePosition.Y <= distance) {
result = 0;
} else if (MousePosition.Y >= 500 - distance) {
result = 4;
}
} else if (MousePosition.Y >= 125 && MousePosition.Y <= 375) {
if (MousePosition.X <= distance) {
result = 6;
} else if (MousePosition.X >= 760 - distance) {
result = 2;
}
} else if ((MousePosition.X < 190 && MousePosition.Y <= distance) || (MousePosition.Y < 125 && MousePosition.X <= distance)) {
result = 7;
} else if ((MousePosition.X > 570 && MousePosition.Y <= distance) || (MousePosition.Y < 125 && MousePosition.X >= 760 - distance)) {
result = 1;
} else if ((MousePosition.X > 570 && MousePosition.Y >= 500 - distance) || (MousePosition.Y > 375 && MousePosition.X >= 760 - distance)) {
result = 3;
} else if ((MousePosition.X < 190 && MousePosition.Y >= 500 - distance) || (MousePosition.Y > 375 && MousePosition.X <= distance)) {
result = 5;
}
}
return result;
}
Storyboard storyboard; //创建移动动画故事板
//直线移动
private void NormalMoveTo(Point p) {
//总的移动花费
double totalcost = Math.Sqrt(Math.Pow(p.X - Spirit.X, 2) Math.Pow(p.Y - Spirit.Y, 2)) / GridSize * UnitMoveCost;
//创建移动动画
storyboard = new Storyboard();
//创建X轴方向属性动画
DoubleAnimation doubleAnimation = new DoubleAnimation(
Spirit.X,
p.X,
new Duration(TimeSpan.FromMilliseconds(totalcost))
);
Storyboard.SetTarget(doubleAnimation, Spirit);
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("X"));
storyboard.Children.Add(doubleAnimation);
//创建Y轴方向属性动画
doubleAnimation = new DoubleAnimation(
Spirit.Y,
p.Y,
new Duration(TimeSpan.FromMilliseconds(totalcost))
);
Storyboard.SetTarget(doubleAnimation, Spirit);
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("Y"));
storyboard.Children.Add(doubleAnimation);
//创建主角朝向属性动画
double direction = Super.GetDirectionByTan(p.X, p.Y, Spirit.X, Spirit.Y);
doubleAnimation = new DoubleAnimation(
direction,
direction,
new Duration(TimeSpan.FromMilliseconds(totalcost))
);
Storyboard.SetTarget(doubleAnimation, Spirit);
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("Direction"));
storyboard.Children.Add(doubleAnimation);
//动画播放
storyboard.Begin();
}
//A*移动
IPathFinder PathFinder = null;
byte[,] Matrix = new byte[1024, 1024]; //地图寻路用二维矩阵
double GridSize = 10; //单位格子大小
System.Drawing.Point Start = System.Drawing.Point.Empty; //移动起点坐标
System.Drawing.Point End = System.Drawing.Point.Empty; //移动终点坐标
private void AStarMoveTo(Point p) {
//进行坐标系缩小
int start_x = (int)(Spirit.X / GridSize);
int start_y = (int)(Spirit.Y / 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); //设置终点坐标
if (Start == End) {
//MessageBox.Show("点到自己脚底啦!");
} else {
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, path[i].Y);
}
storyboard = new Storyboard();
//创建X轴方向逐帧动画
DoubleAnimationUsingKeyFrames keyFramesAnimationX = new DoubleAnimationUsingKeyFrames();
//总共花费时间 = path.Count * UnitMoveCost
keyFramesAnimationX.Duration = new Duration(TimeSpan.FromMilliseconds(path.Count * UnitMoveCost));
Storyboard.SetTarget(keyFramesAnimationX, Spirit);
Storyboard.SetTargetProperty(keyFramesAnimationX, new PropertyPath("X"));
//创建Y轴方向逐帧动画
DoubleAnimationUsingKeyFrames keyFramesAnimationY = new DoubleAnimationUsingKeyFrames();
keyFramesAnimationY.Duration = new Duration(TimeSpan.FromMilliseconds(path.Count * UnitMoveCost));
Storyboard.SetTarget(keyFramesAnimationY, Spirit);
Storyboard.SetTargetProperty(keyFramesAnimationY, new PropertyPath("Y"));
//创建朝向动画
DoubleAnimationUsingKeyFrames keyFramesAnimationDirection = new DoubleAnimationUsingKeyFrames();
keyFramesAnimationDirection.Duration = new Duration(TimeSpan.FromMilliseconds(path.Count * UnitMoveCost));
Storyboard.SetTarget(keyFramesAnimationDirection, Spirit);
Storyboard.SetTargetProperty(keyFramesAnimationDirection, new PropertyPath("Direction"));
for (int i = 0; i < framePosition.Count(); i ) {
//加入X轴方向的匀速关键帧
LinearDoubleKeyFrame keyFrame = new LinearDoubleKeyFrame();
//平滑衔接动画(将寻路坐标系中的坐标放大回地图坐标系中的坐标)
if (i == 0) {
keyFrame.Value = Spirit.X;
} else if (i == framePosition.GetUpperBound(0)) {
keyFrame.Value = Spirit.Destination.X;
} else {
keyFrame.Value = framePosition[i].X * GridSize GridSize / 2; // GridSize / 2为偏移处理
}
keyFrame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(UnitMoveCost * i));
keyFramesAnimationX.KeyFrames.Add(keyFrame);
//加入X轴方向的匀速关键帧
keyFrame = new LinearDoubleKeyFrame();
if (i == 0) {
keyFrame.Value = Spirit.Y;
} else if (i == framePosition.GetUpperBound(0)) {
keyFrame.Value = Spirit.Destination.Y;
} else {
keyFrame.Value = framePosition[i].Y * GridSize GridSize / 2; // GridSize / 2偏移处理
}
keyFrame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(UnitMoveCost * 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(UnitMoveCost * i));
keyFramesAnimationDirection.KeyFrames.Add(keyFrame);
}
storyboard.Children.Add(keyFramesAnimationX);
storyboard.Children.Add(keyFramesAnimationY);
storyboard.Children.Add(keyFramesAnimationDirection);
//故事板动画开始
storyboard.Begin();
//------------------------
//用白色点记录移动轨迹(此段代码只做测试用,真实使用时请注释掉,否则将严重影响系统性能)
//------------------------
//for (int i = path.Count - 1; i >= 0; i--) {
// rect = new Rectangle();
// rect.Tag = path[i].X "," path[i].Y;
// rect.Fill = new SolidColorBrush(Colors.Snow);
// rect.Stroke = new SolidColorBrush(Colors.Silver);
// rect.StrokeThickness = 2;
// rect.Width = 8;
// rect.Height = 8;
// rect.RadiusX = 10;
// rect.RadiusY = 10;
// Carrier.Children.Add(rect);
//}
}
}
}
private void Carrier_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
Point p = e.GetPosition(Map); //点击的地方在Map中的坐标点
//假如点击的地点不是障碍物
if (Matrix[(int)(p.X / GridSize), (int)(p.Y / GridSize)] != 0) {
Spirit.Destination = p; //设置主角的最终移动目的地
Spirit.Action = Actions.Run; //主角动作切换成跑步状态
Spirit.IsAStarMoving = false; //非寻路模式
NormalMoveTo(p); //两点间建立直线移动
}
}
//换装
private void ChangeEquipment(object sender, RoutedEventArgs e) {
Spirit.Equipment[0] = Convert.ToInt32(comboBox1.SelectionBoxItem.ToString());
Spirit.Equipment[1] = Convert.ToInt32(comboBox2.SelectionBoxItem.ToString());
Spirit.Source = Super.EquipPart(Spirit.Equipment, Spirit.DirectionNum, Spirit.DirectionFrameNum, Spirit.TotalWidth, Spirit.TotalHeight, Spirit.SingleWidth, Spirit.SingleHeight);
}
}
}
标签: 适合初学者图像处理
小贴士
感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。
- 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
- 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
- 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
- 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。
关于好例子网
本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明


网友评论
我要评论