游戏的精灵框架到此为止算告一段落,让我们一同来体验它带来的神奇效应。

    一个安静的黄昏,主角悠闲的甩着它帅气的毛发独跑于林阴大道。怎知天色已晚即将进入月亮的领地,嘿嘿,我们的故事就从这里开始:Be careful,前方怪物出没!

    实在不忍心让主角空有一身武艺而无处施展,本节为了不再让它孤单,我将向游戏中加入可爱的妖精妹妹与之为伴:

    好象在哪见过呢?对,就是她了,可爱吧(QXGameEngine中的怪物,^_^||难怪这眼熟)。

    妖精怪物属于精灵类型,因此要让它在游戏中出现,我们只需创建QXSpirit的实例;这里首先我添加一个刷怪方法InitMonster,接着循环添加怪物精灵实例及参数:

        QXSpirit[] Monster;

        /// <summary>

        /// 初始化怪物(刷怪)

        /// </summary>

        /// <param name="num">怪物数量</param>

        private void InitMonster(int num) {

            Monster = new QXSpirit[num];

            Random random = new Random(); //测试用,随机数坐标

            for (int i = 0; i < num; i++) {

                if (Carrier.FindName("Magicer" + i.ToString()) == null) {

                    Monster[i] = new QXSpirit();

                    Monster[i].Name = "Magicer" + i.ToString();

                    Carrier.RegisterName(Monster[i].Name, Monster[i]);

                    Monster[i].Equipment[0] = 100;

                    Monster[i].Equipment[1] = 0;

                    ……

                    Monster[i].X = 2000 - random.Next(1000);

                    Monster[i].Y = 1500 - random.Next(1000);

                    ……

                    Carrier.Children.Add(Monster[i]);

                }

            }

        }

    代码太多我就不一一罗列了,下面我提取部分重要的参数进行讲解。首先我定义一个Random类型随机函数用于随机数值的产生,接着判断每一个有名字的精灵是否存在,如果不存在则加入到游戏中。这里为精灵注册名字的目的是为了以后方便管理,例如精灵死掉了,我们需要找到它的实例并将之移除,而惟有通过它的名字或ID之类的方能将之捕获;同时,在定时刷怪的机制下,我们得首先判断某精灵是否还存在,如果存在则不可能再多刷一个,就好比网络游戏中大家是否都有过蹲点等刷BOSS暴装备的经历,一个萝卜一个坑,同一点上刷出两个双胞胎BOSS,这是很匪夷所思的事。那么定义完怪物的名字后,我们接着还需要定义它的身体图片代号,本例中它的代号为100(为了与主角类精灵用的身体图片区别,我以0-99代表主角类身体(衣服)代号范围,100-N代表怪物(NPC)类身体代号范围),最后通过前面的random来定义它们的初始坐标:以(2000,1500)为中心边长为1000的正方形范围内的随机位置。

    就这么完啦?对,简单吧,还是那句老话,拓展性优良的架构是经得起全方位的考验滴。嘿嘿,刷它30个怪(InitMonster(30))测试一下:

    怪物满天飞,主角此时激动的心情是难以用人类的语言来形容的。可是,当主角向四周望去时,蒙了:杂都和我名同姓捏?作者你脑神经搭到脚底了吧?。。。我还真没注意到哪。在前面的章节里,我将精灵的3个身份描述都定义在了xaml里面(Faction门派,Clan家族,Sname名字)。此时,我们对精灵的重命名必须在精灵初始化的同时进行。但由此带来的新问题:如果每个怪物都有不同的身份描述,而且需要经常性的修改调整,写在内存里的东西是无法扩展的。这不禁让我联想到了第二十二节中的xml配置文件解决方案。网络游戏的服务器会根据地图区域代号加载相应的地图xml配置文件,其中包括地图及遮挡物以及地图上相当重要的精灵对象信息。当需要时我们只需对xml文件进行稍稍修改,例如精灵怪物的位置,描述,等级等相关信息即可以达到更新的目的。根据此原理,我对原有的Config配置文件进行如下改进,并添加怪物参数设置:

        <Map Sign="1">

            <Surface Src="Map\1\0.jpg" Width="3200" Height="2400" X="0" Y="0" CenterY="0" Opacity="1"></Surface>

            <Masks></Masks>

            <Spirits>

                <Monster Name="Magicer001" Faction="倾城之恋" Clan="美丽呀" SName="倒影在心房" FrameRange0="10" FrameRange1="12" FrameRange2="11" FrameRange3="13" FrameRange4="15" SingleWidth="150" SingleHeight="150" TotalWidth="9150" TotalHeight="1200" Equipment0="100" Equipment1="0" CenterX="75" CenterY="125" X="700" Y="300" Direction="4" Speed="120"></Monster>

                ……

                <Monster Name="Beast001" Faction="精英" Clan="食人族酋长" SName="绝对无敌" FrameRange0="8" FrameRange1="8" FrameRange2="9" FrameRange3="6" FrameRange4="9" SingleWidth="200" SingleHeight="200" TotalWidth="8000" TotalHeight="1600" Equipment0="101" Equipment1="0" CenterX="100" CenterY="170" X="200" Y="1200" Direction="0" Speed="125"></Monster>

            ……

            </Spirits>

        </Map>

    以上为xml文件的部分代码,大家将代码中黄色背景的数据与InitMonster方法中的赋值属性进行对照会发现它们之间基本上是一一对应的。设置完后,我们只需通过第二十二节中加载xml数据的方法:

            XElement mapdata = Super.GetTreeNode(Super.SystemConfig, "Map", "Sign", "1");

            ……

            //抽离精灵数据中的怪物集合并用其来初始化地图上的怪物

            InitMonster(mapdata.Element("Spirits").Elements("Monster"));

即可遍历xml配置文件中地图(Map)代号为1Sign==1)的精灵类型(Spirits)中的具体类型为怪物(Monster)的数据,然后将它们的值与精灵的参数属性进行一一对应赋值即可以动态的完成精灵怪物的初始化。

    仔细看的朋友一定注意到了xml文件中地图1中的怪物除了美丽的妖精外,还多了一个名字叫“绝对无敌”的家伙。嘿嘿,它可是我为了本教程特意花了1个小时处理出来的,以次感谢大家这段时间来对我的支持!同时也是对自己劳动的犒赏!挺值得的。那么我们都各刷它若干只测试一下:

    忽忽,怪物出现了。又到了本教程的国际惯例---“自恋一刻”!让主角在地图上逛逛吧~当到了怪多的地方突然会发现有些许的不连贯,问题出在线程上。本教程例子均使用封装好的线程DispatcherTimer,因此管理起来是相当方便的(比起直接使用Thread简单多了),仅仅需要设置它们的优先级别即可进行相应调整。经过再三考虑与调试,最终的线程结构如下:暂时将主角的生命线程表现级别设置为9(Normal),将怪物的生命线程表现级别设置为7(Render),并且分别对5个不同的动作(站立、跑步、战斗、施法、死亡)设置相应的后台处理级别分别对应为LowestNormalBelowNormalBelowNormalLowest。对这些线程的调试过程是件非常有趣的尝试,我曾反复尝试并乐在其中(太过瘾导致我无法自拔因而延误了教程的发布读者声音:“癫子”。^_^||)。不同的线程级别搭配将在游戏性能、画面及操作手感上产生相当明显的区别,大家不妨在我的代码基础上,将主角的生命线程表现级别由Normal调节为Background,或将跑步的级别由Normal调节到最低级别Lowest,然后分别运行一下,区别一目了然。

    最后发张最终的测试截图,嘿嘿,我的怪兽军团:

    轻轻松松就创建出了如此美妙的怪物精灵,这全都得归功于第十创建的精灵控件。上天入地如探囊取物,简单设置即可随意实现主角、怪物、NPC等等角色的创建,这就是控件封装给我们带来的完美体验。

    下一章我将讲解怪物的层次关系以及如何准确的判断及拾取鼠标下的精灵对象,敬请关注。

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