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);

小结

其他的自己看源码,自己理解好了。源码

posted @ 2012-07-19 09:01  但,我知道  阅读(1182)  评论(3编辑  收藏  举报