前面的章节主要针对地图表现层进行讲解。通常来说,简单的游戏光有它就足够了;但是为了达到更加真实的光影效果,模拟真实的虚拟世界,我们还得继续在地图上下大工夫。本节将就如何实现地图中的遮罩层,即物体对角色的遮挡进行详细讲解。

    首先我们来看一张比较完善的地图应该包含哪些内容:

    从上图可以看到,我将一张地图引擎结构分成了3层(难道这就是传说中的地图三层架构?汗一个先。。。)。中间的图片代表地图的表现层,也就是我们视觉上直接看到的地图界面。关于它,前面的章节中已有非常多的讲解,这里就不再累述了。接着我们再看最下面那张:地图底层,它由黑白两大颜色组成,似乎还有一圈黄色在右小角呢。有的朋友觉得它很奇怪,似乎摸不着头脑,好象和地图没啥关系吧?其实只要将它和第二张图进行分析比较就会发现,它上面的黑色就是地图中障碍物区域,白色则为可以通行的区域,那黄色呢??还有朋友要问了:前面的章节不是有讲A*寻路吗?通过Matrix[]数组来构建障碍物不是很完美吗?那为什么还要多此一举再为每张地图构造一张同比例的障碍物底层图呢?我只想告诉广大的朋友们:它的作用可大了,尤其尤其在目前的Silverlight游戏开发中,它的作用及拓展性可谓承前启后,用科学发展观的话讲就是:面向对象的思维开发Silverlight游戏。太多太多的悬念,才能有更多的期待,那么关于这张神秘底层图的讲解,请听下回分析。

    读者声音:同志,你也太假了吧,这样就讲完这节啦?BS你一下。

    作者:安啦,怎么可能嘛,这叫倒叙懂不?(啥叫倒叙其实俺也不太。。。?嘿嘿)

  不瞎扯啦,还剩一张图没讲呢,对啦,本节的主角就是它了:地图遮罩层。

  首先来讲讲实现原理吧:我们可以从地图表现层(下文直接就称之地图好了)中看到,遮挡人物的只有一棵树。那么我们想要在此地图上实现遮罩效果,首先就得用Photoshop将这棵树给截出来,当然越精确越好,然后将它单独保存成一张背景透明的图片(通常Windows桌面RPG游戏中会将所有的遮挡物统一规格,例如50*50一张(如大于则分两张、三张等等),然后将全部遮挡物图片放进一个庞大的二进制文件中,显然这对于Silverlight基于网页的游戏是不容许的),如果一张地图上有多个遮挡物,同样将他们都截取出来然后依次命名保存。准备工作做完后,我们就需要将遮罩层的图片放在顶层,将地图放在底层,人物等放在中间层。最后分别将遮罩层的所有图片布局到它们应该遮挡的位置上,这样就完成了所有的遮挡工作了。好了。下面我将用代码来实现它。

    这里我以下图作为地图实例:

    很明显该地图有三处障碍物,两处遮挡物。障碍物我用绿色区域描绘出来了,遮挡物则为两棵数,我用Photoshop将它们分别截取了出来命名为:Mask1.pngMask2.png

  匆忙了点,截得不好可不要见怪哪!谁让这两棵树长得如此奇怪呢?嘿嘿。

    OK,接下我以第九节的代码为基础进行修改,首先构建障碍物:

            //构建障碍物

            for (int y = 22; y <= 24; y++) {

                for (int x = 5; x <= 16; x++) {

                    //障碍物在矩阵中用0表示

                    Matrix[x, y] = 0;

                    rect = new Rectangle();

                    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.SetLeft(rect, x * GridSize);

                    Canvas.SetTop(rect, y * GridSize);

                }

            }

            for (int y = 11; y <= 14; y++) {

                for (int x = 27; x <= 31; x++) {

                    //障碍物在矩阵中用0表示

                    Matrix[x, y] = 0;

                    rect = new Rectangle();

                    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.SetLeft(rect, x * GridSize);

                    Canvas.SetTop(rect, y * GridSize);

                }

            }

            for (int y = 18; y <= 21; y++) {

                for (int x = 33; x <= 37; x++) {

                    //障碍物在矩阵中用0表示

                    Matrix[x, y] = 0;

                    rect = new Rectangle();

                    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.SetLeft(rect, x * GridSize);

                    Canvas.SetTop(rect, y * GridSize);

                }

            }

    三个循环分别构建了上图中的三处障碍物,这几章都对它进行了修改,大家应该再熟悉不过了。接下来就是遮挡物那两棵树了,这里我用Image控件作为遮挡物的容器:

        //创建遮罩层

        Image Mask1 = new Image();

        Image Mask2 = new Image();

        private void InitMask() {

            Mask1.Width = 238;

            Mask1.Height = 244;

            Mask1.Source = new BitmapImage((new Uri(@"Map\Mask1.png", UriKind.Relative)));

            Mask1.Opacity = 0.7;

            Carrier.Children.Add(Mask1);

            Canvas.SetZIndex(Mask1, 10000);

            Canvas.SetLeft(Mask1, 185);

            Canvas.SetTop(Mask1, 220);

            Mask2.Width = 198;

            Mask2.Height = 221;

            Mask2.Source = new BitmapImage((new Uri(@"Map\Mask2.png", UriKind.Relative)));

            Mask2.Opacity = 0.7;

            Carrier.Children.Add(Mask2);

            Canvas.SetZIndex(Mask2, 10000);

            Canvas.SetLeft(Mask2, 466);

            Canvas.SetTop(Mask2, 11);

        }

    这样就将遮挡物加入进了游戏窗体。有了前面那么多章节关于Image控件的使用知识,上面的代码应该不难理解。这里特别要说一下的是为什么要将它们的Opacity设置为0.7:因为这样的遮挡物会有一定的透明度,当角色置身其中时会若隐若现,从而达到真实模拟MMORPG的效果。至于为什么要将遮挡物的Zindex属性设置为10000呢?这关系到游戏运行时地图中不光只有一个角色,还会有非常多的物体及对象角色的存在,它们之间也同样有着相互遮挡与被遮挡的关系。而在WPF/Silverlight游戏中,物体的遮挡顺序一样可以使用画家算法,该算法原理简单描述就是近物遮挡远物,幸运的是在WPF/Silverlight中,我们可以很方便的只要动态更新(一个对象的Zindex属性)=(它的Y属性)即可以巧妙的实现此效果,是不是有点邪恶?嘿嘿。所以要将遮盖物的ZIndex设置得足够大以防止任何一个物体它的Y属性大过遮盖物的Zindex属性,从而造成画面显示BUG

    其他的代码均和第九章的一样,到这,本节的目标已经达到了。那么让我们运行测试一下吧:

  大家可以随便在地图上点击,会发现只要主角有经过这两棵树的地方都会被树以0.7的透明度遮挡,并且障碍物也同样并行存在着,主角如有经过同样会饶过它。障碍物,人物,遮罩层次分明,互不干预,完美默契的并行着。

    至此,地图引擎就基本完成了。下一节将讲解本节开始所提到的神秘第三层,它在WPF/Silverlight游戏辅助方面起着非常大的拓展作用,敬请关注。

WPF/Silverlight
作者:深蓝色右手
出处:http://alamiye010.cnblogs.com/
教程目录及源码下载:点击进入(欢迎加入WPF/Silverlight小组 WPF/Silverlight博客团队)
本文版权归作者和博客园共有,欢迎转载。但未经作者同意必须保留此段声明,且在文章页面显著位置给出原文连接,否则保留追究法律责任的权利。
posted on 2009-06-17 21:13  深蓝色右手  阅读(13927)  评论(15编辑  收藏  举报