WPF游戏摘记--地图编辑器(1)
一、前言
最近看了这么多的博客文章,发现些博文的好处,情不自禁也准备写点,不是什么知识分享,就自己那点墨水,我知道,怕拿出来还误人子弟呢,而且都是别人的东西呢,也就不借花献佛了,呵呵:)先这个WPF游戏系列主要1:是给自己巩固巩固知识,学海无涯啊,怎么学都学不完呢,那就把已经学会的好好整理整理;2:做个学习笔记,以后相关知识点查找起来也方便呢。嗯差不多了,先救这个目的吧,下面进入正题了哦:)
----------------------------------------------------华丽的分割线---------------------------------------------------------
今天的笔记主题是:地图编辑器,给他个红色加粗,醒目啊,有木有。
二、需求
作为一款正真的游戏,地图编辑器很重要啊,有木有。本地图编辑器主要作用是实现游戏里面地图的逻辑数组,怎么说呢,简单点吧,我举个例子先哦。大家都知道游戏里面的地图我们看到的是一张图片,可是机器哪管你什么图片怎么漂亮啊,怎么华丽啊,他不管,他只知道数据。所以跟这个死板的家伙交流就不用多少辞藻了,直接把,我就这么告诉他,我就指着一个数组说,这是一张很华丽,很浪漫的地图,然后她就相信了。。。呵呵,开个玩笑先。也就是我们可以用一个数组来表示地图的数据结构,然后我们规定1是通的,0时不通,当然你也可以用其他数字或者字母啊,没事的,只要你不嫌麻烦,你的机器也不嫌麻烦的。
简单的,代码不不给你了,挺占地方的,听听就行,不明白看源码吧。 原理很简单,但是有个问题,如果图片的数据很少,想五子棋的地图,直接在地图数据上编辑还是挺轻松的,但是我们眼光放大点,像某些网游的地图,很大、很多种,像某些竞技类游戏,类似LOL啊,就一张,很大,试想像,在一大堆0、1的数组上编辑有效的地图数据。。。哦,想想就头大了,不是我多说,看到那么庞大的数字,你想死的心都有。所以地图编辑器应声而出。呼,终于始出来啊:)本地图编辑器的作用是可编辑任何地图,只要你是图片,然后直接在图片上面创障碍物,进而设置开始点和终点进行自动寻路(是A*算法哦,莫心动)模拟人物运动,验证无误后,导出障碍物数据,接下来你就可以直接使用该障碍物数据构建完整的游戏。另外,还可以导入上次构建的障碍物数据进行再次增改,那样就可以应付大型的复杂的地图了,很人性呢有木有。
二。原理
先看程序首页吧:
然后就是原理了,先小题一下,我是用两个Grid来布个小局的。左边是两个ScrollViewer,因为ScrollViewer可以有横、竖滑竿。给底层的ScrollViewer的Content添加个Image来显示地图图片,
Xaml:
<ScrollViewer Grid.Row="0" Grid.Column="0" HorizontalAlignment="Stretch" Name="MapscrollViewer" VerticalAlignment="Stretch" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" >
<ScrollViewer.Content>
<Image Name="Map"></Image>
</ScrollViewer.Content>
</ScrollViewer>
后台:
知识点1:用OpenFileDialog导入文件的方法
OpenFileDialog loadMap = new OpenFileDialog()
{
CheckFileExists = true,
CheckPathExists = true,
Multiselect = false,
Filter = "图像文件(*.jpg,*.png)|*.jpg;*.png",
};
loadMap.FileOk += new System.ComponentModel.CancelEventHandler(loadMap_FileOk);
loadMap.ShowDialog();
void loadMap_FileOk(object sender, System.ComponentModel.CancelEventArgs e)
{
mapName = (sender as OpenFileDialog).FileName;
Map.Source = new BitmapImage(new Uri((sender as OpenFileDialog).FileName, UriKind.Absolute));
}
上面一层是逻辑层,往其content里面动态添加(就是写代码了)一个Grid,通过往这个Grid里面添加ColumnDefinition和RowDefinition实现网格,使Grid的ShowGridLines为True还是使网格显示出来。
知识点二:Grid做网格的用法
grid = new Grid()
{
ShowGridLines = IsShowGrid.IsChecked.Value,
Width = width,
Height = height,
};
int gridwidth ;
if (!int.TryParse(gridWidth.Text,out gridwidth))
{
MessageBox.Show("请?输º?入¨?数ºy字Á?!ê?");
return;
}
for (int x = 0; x < grid.Width/gridwidth; x++)
{
ColumnDefinition col = new ColumnDefinition()
{
Width = new GridLength(gridwidth),
};
grid.ColumnDefinitions.Add(col);
}
int gridheight = int.Parse(gridHeight.Text);
if (!int.TryParse(gridHeight.Text,out gridheight))
{
MessageBox.Show("请?输º?入¨?数ºy字Á?!ê?");
return;
}
for (int i = 0; i < grid.Height/gridheight; i++)
{
RowDefinition row = new RowDefinition()
{
Height = new GridLength(gridheight),
};
grid.RowDefinitions.Add(row);
}
ShowGridLines可是个好属性啊,设为True就能将本来用来布局的RowDefinitions和ColumnDefinitions都显示出来。
最后是两个ScrollViewer绑在一起共进退,
知识点3:两个ScrollViewer的绑定
private void ObstructionViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
ScrollViewer scrollviewer = sender as ScrollViewer;
MapscrollViewer.ScrollToVerticalOffset(scrollviewer.VerticalOffset);
MapscrollViewer.ScrollToHorizontalOffset(scrollviewer.HorizontalOffset);
}
这样,你就可以在上面那个ScrollViewer也就是ObstructionViewer注册个点击事件,初始化个地图数据数组,
Matrix = new byte[256, 256];
for (int i = 0; i < Matrix.GetUpperBound(1); i++)
{
for (int x = 0; x < Matrix.GetUpperBound(0); x++)
{
Matrix[x,i] = 1;
}
}
通过点击设置数组的值,
Point p = e.GetPosition(ObstructionViewer);
SetObstructionMatrix((int)p.X / gridwidth, (int)p.Y / gridheight, (byte)0);
并在上面画个矩形显示出来。
Rectangle rect = new Rectangle()
{
Width = gridwidth,
Height = gridheight,
Fill = new SolidColorBrush(Colors.White),
};
grid.Children.Add(rect);
grid.RegisterName("rect" + x + "_" + y, rect);
rect.SetValue(Grid.ColumnProperty, x);
rect.SetValue(Grid.RowProperty, y);
知识点4:UiElement的注册和管理
grid.RegisterName("rect" + x + "_" + y, rect);
通过向Grid注册个名字,就可以用这个名字来管理了,
Rectangle rect = grid.FindName("rect" + x + "_" + y) as Rectangle;
grid.UnregisterName("rect" + x + "_" + y);
grid.Children.Remove(rect);
我这里用来取消方块,很好用,但是这个注册并不是靠Grid来管理的,所以你要全部清除注册不能靠注销Grid来实现,我是手动实现的,
for (int y = 0; y < Matrix.GetUpperBound(1); y++)
{
for (int x = 0; x < Matrix.GetUpperBound(0); x++)
{
if (Matrix[x,y]==0)
{
grid.UnregisterName("rect" + x + "_" + y);
}
}
}
知识点5:A*算法的使用
我这里是用来某位大神封装好的算法,所以只要调用就行。先导入引用,
初始化,
IPathFinder pathFinder;
string start, end;
写了个方法来调用,使用A*算法得到路径点集并画个方块显示一条龙服务,
/// <summary>
/// 利¤?用®?A*寻¡ã路¡¤测a试º?障?碍ã-物?层?的Ì?可¨¦用®?性?
/// </summary>
private void FindRoadTest()
{
//无T效¡ì性?判D断?
if (grid==null||start==""||end=="")
{
return;
}
//得Ì?到Ì?起e点Ì? ,ê?终?点Ì?
string[] str = start.Split('_');
int start_x = Convert.ToInt32(str[1]);
int start_y = Convert.ToInt32(str[2]);
str = end.Split('_');
int end_x = Convert.ToInt32(str[1]);
int end_y = Convert.ToInt32(str[2]);
//进?入¨?正y文?
pathFinder = new PathFinderFast(Matrix);
List<PathFinderNode> path=pathFinder.FindPath(new System.Drawing.Point(start_x,start_y),new System.Drawing.Point(end_x,end_y));
if (path==null)
{
MessageBox.Show("路¡¤径?不?存ä?在¨²!ê?");
}
else
{
//路¡¤径?存ä?在¨²
for (int i = 0; i < path.Count; i++)
{
PathFinderNode node = path.ElementAt(i);
Rectangle rect = new Rectangle();
rect = new Rectangle()
{
Width = gridwidth,
Height = gridheight,
RadiusX = 5,
RadiusY = 5,
Fill=new SolidColorBrush(Colors.Red),
};
grid.Children.Add(rect);
rect.SetValue(Grid.ColumnProperty, node.X);
rect.SetValue(Grid.RowProperty, node.Y);
}
}
}
知识点6,Linq的用法(一些)
下面介绍一些LInq在我的程序的用法,挺简单的,但是很好用。
导入xml文件,
XElement myEle = XElement.Load("Output.xml");
得到自身或者任意子节点,
XElement myRoot = myEle.DescendantsAndSelf("Items").Single();
创建个新的XElement,并附属性和值,放入MyRoot并保持的指定路径,
XElement newEle = new XElement("Item");
newEle.SetAttributeValue("ID", mapName);
myRoot.Add(newEle);
myEle.Save(savepath);
小结
其他的自己看源码,自己理解好了。源码
作者:但,我知道
出处:http://www.cnblogs.com/haichao/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。