专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »DotNet » wpfsilverlight:C#开发WPF/Silverlight动画及游戏系列教程(Game Course):( 2十一)主位式地图移动模式 »正文

wpfsilverlight:C#开发WPF/Silverlight动画及游戏系列教程(Game Course):( 2十一)主位式地图移动模式

来源: 发布时间:星期三, 2009年9月2日 浏览:103次 评论:0
  是否期待了很久?本节就来个重量级做为开场白吧:主位式地图移动模式何谓主位式地图移动模式即以主角为中心移动带动着所有对象包括地图、物体对象、其他玩家、怪物等等相对移动这些对象移动都是以主角为参照物最典型例子莫过于当前流行MMORPG了你控制角色在地图中永远是处于窗口正中心位置(除了8个角落外)这就是主位式地图移动模式(如下图)



  有朋友开始焦躁了:我妈呀才刚学完牵引式地图移动模式还没完全吸收呢又来个什么鬼主位式地图移动模式头都大冽!

  其实点也不用担心个结构贼清晰只需要您次轻轻手术即可以实现功能革新是否对谦谦魔术记忆犹新?神奇时刻即将降临Let me show you the principle first:



  游戏窗口尺寸仍然暂定为800 * 600如上图我将游戏地图(尺寸1750 * 1440)分为9个部分;当主角处于左上(Spirit.X<=400 && Spirit.Y<=390)、右上(Spirit.X>=950 && Spirit.Y<=390)、左下(Spirit.X<=400 && Spirit.Y>=1050)、右下(Spirit.X>=950 && Spirit.Y>=1050)这4个角落区域时地图静止主角如第 9节中样可以任意在窗口中移动直接讲就是主角在窗口中显示位置即是它图片左上角点(X-CenterX,Y-CenterY);

  当主角处于中上(Spirit.X>400 && Spirit.X<950 && Spirit.Y <=390)、中下(Spirit.X>400 && Spirit.X<950 && Spirit.Y >=1050)这2个边缘区域时主角在水平方向上始终居中移动时它在窗口中只会做上下移动水平方向上通过地图相对反向移动形成主角水平方向前进视觉效果;

  当主角处于左中(Spirit.>390 && Spirit.Y<1050 && Spirit.X<=400)、右中(Spirit.>390 && Spirit.Y<1050 && Spirit.X>=950)这2个边缘区域时主角在垂直方向上始终居中移动时它在窗口中只会做左右水平移动垂直方向上通过地图反向移动形成主角垂直方向前进视觉效果;

  当主角处于正中区域也就是游戏中主角最多时候主角此时始终处于游戏窗口正中位置(定位到脚底)当它移动时在窗口中通过地图反向移动从而在视觉上形成主角在移动(实际上主角是静止只做方向动画移动)这和第 2十节中牵引式地图移动模式有异曲同工的处只是两者刚好相反:前者主角不动地图反向移动;后者为地图随鼠标牵引而移动主角不动最后得出结论:我们只需将第 2十节中AllMove思路方法进行修改即可以实现完美模式转换

  原理就这么简单至于其中数字是如何得到我们只需要预先定义好两个参数WindowCenterXWindowCenterY它们其实就是游戏窗体尺寸5折(如果有标题栏则需要减去标题栏高度约20像素)以800*600窗口模式游戏窗体为例那么它WindowCenterX=800/2 =400WindowCenterY=(600-20)/2=390那么1024*768呢?以此类推理请了思路接下来就让我们来实现主位式地图移动模式下AllMove思路方法这里我以主角位于左上这个区域为例:

  private void AllMove {

   (Spirit.X <= WindowCenterX && Spirit.Y <= WindowCenterY) {

   //地图左上

   //所有精灵以主角为参照相对移动

   for ( i = 0; i < Carrier.Children.Count; i) {

   (Carrier.Children[i] is QXSpirit) {

   //假如子Control控件为精灵类型则获取的

   QXSpirit spirit = Carrier.Children[i] as QXSpirit;

   //设置精灵在游戏窗口中显示位置

   Canvas.SetLeft(spirit, spirit.X - spirit.CenterX);

               Canvas.SetTop(spirit, spirit.Y - spirit.CenterY);

   //画家思路方法使所有精灵的间遮挡关系由近及远

   Canvas.SetZIndex(spirit, Convert.ToInt32(spirit.Y);

   } (Carrier.Children[i] is Image) {

                    //假如是地图/遮罩

   Image Map = Carrier.Children[i] as Image;

   Canvas.SetLeft(Map, 0);

   Canvas.SetTop(Map, 0);

   }

   }

   }

  ……

  }

  我们首先判断主角是否在左上区域(Spirit.X <= WindowCenterX && Spirit.Y <= WindowCenterY)如果是那么我们循环遍历画布中所有子Control控件假如某个Control控件是精灵类型(QXSpirit)那么我们捕获它由于此时主角处于是地图左上区域按我们前面分析它在此区域内显示位置就是它坐标减去中心点值(CenterX,CenterY)精灵坐标是定位到脚底而窗口显示它位置时是定位到精灵图片左上角点那么其他方向以次类推(源码中有这里就不再列罗列)

  做到这有朋友忍不住要问了:

   对于遍历子Control控件我可拿手了用Foreach不是更能胜任为何还要用老土For呢?

  深蓝色:这涉及到在Foreach中动态添加和删除子Control控件问题举个最简单例子游戏中有个怪物(monster)个如来神掌不小心把它给挂了(monster.Le=0)那么画布就需要对其Control控件进行移除(Carrier.Children.Reomve(monster));好此时问题来了Carrier.Children这个Collection集合内容发生了变化(少了个monster)这将导致系统十分不高兴:*!谁动了我怪!(抛出InvalidOperationException异常)这就是臭名昭著在Foreach遍历中由于对Collection内容进行更改而引发血案!如何屏蔽它?用Try{}Catch{}?我非常拒绝在我代码中出现这对兄弟还剩下谁?惟有善良且和谐For能肩此重任

  又有朋友问了:我们先判断了子Control控件是否为QXSpirit类型这很好很强大;但是后面接着将地图和遮罩当作Image来判断是不是有些太牵强?

  深蓝色:嘿嘿!等你多时了伟大地图Control控件华丽登场:

  有了第十 4节有关创建精灵Control控件知识这地图Control控件只需要依葫芦画瓢个轻松那么我们依照第十 4节中创建QXSpiritControl控件思路方法在Controls文件夹上点右键添加个用户Control控件取名叫QXMap



  并为其添加如下属性:

   #region (地图表层/遮罩)属性

   // 地图关键点X定位到左上角0>

   public CenterX { get{…}; {…}; }

   // 地图关键点Y定位到左上角0

   public CenterY { get{…}; {…}; }

   // 地图X坐标

   public double X { get{…}; {…}; }

   // 地图Y坐标

   public double Y { get{…}; {…}; }

   // 地图宽

   public double Width_ { get{…}; {…}; }

   // 地图高

   public double Height_ { get{…}; {…}; }

   // 地图图片源

   public ImageSource Source { get{…}; {…}; }

   // 地图透明度

   public double Opacity_ { get{…}; {…}; }

  由于地图和遮罩拥有几乎属性因此为了简单且统我只建立个名为QXMapControl控件进行实现(当然您将的分成QXMap和QXMask两个Control控件亦可)下文中为了区分我均称地图为地表图层(简称地表)遮罩为遮罩图层(简称遮罩)这样可以让大家更好理解QXMap区别实现回到它属性上其中XY代表坐标如果是地表则为0它自己相对于自身坐标当然是(0,0);如果是遮罩那么它XY则是它图片左上角位于地表中坐标CenterXCenterY目前暂时不会用到因此均默认为0即可;至于其他属性都很好理解这里就不再讲解

  地图Control控件创建完成接下来我们将原先:Image Map = Image;用QXMap MapSurface = QXMap;代替Image Mask = Image; 用QXMap Mask = QXMap;代替并设置好相应属性这样就完成了通过地图Control控件对地表和遮罩

  到此第 2位朋友问题已经云开见日我们只需轻轻扫键盘:

  ……

   (Carrier.Children[i] is QXMap) {

   //假如是地图/遮罩

   QXMap Map = Carrier.Children[i] as QXMap;

   Canvas.SetLeft(Map, 0);

   Canvas.SetTop(Map, 0);

    }

   ……

  这样完美多了不是嘿嘿得瑟

  深蓝色!我还有问题!

  更加深邃了我心中理念:青春就是热血和激情!

  深蓝色!我发誓这是最后个问题:

  你前面不是说游戏后期还会加入怪物(monster)、NPC(npc)等乱 7 8糟东西那么在判断时候不是要这样写:

  ……

   for ( i = 0; i < Carrier.Children.Count; i) {

   (Carrier.Children[i] is QXSpirit) {

   ……

   } (Carrier.Children[i] is QXMap) {

   ……

   } (Carrier.Children[i] is QXMonster) {

   ……

   } (Carrier.Children[i] is QXNpc) {

   ……

   }

      }

  这不是没完没了了呀?而且这还是左上区域实现代码还有其他8个区域呢?维护起来不成了是典型发而动全身?

  不提我还真差点给忘记了如何将这些对象物体Control控件进行个归类呢?分析:首先这些Control控件均为用户Control控件用户Control控件继承自UserControl类;这道好了在C#中只能单类继承UserControl类在用户Control控件出生时候就已经将这个尊位给踞为己有杂办可好??郁闷的时接口天籁般魔音再次缭绕于我耳边:老大还有我们捏!对呀!差点把软哥赐予我们神圣接口姐妹给忘了使用接口即可以对这众多对象物体用户Control控件进行规范标准又能被类对多继承很酷不是吗?

  那么接下来我们添加个接口取名叫:QXObject.cs并对其进行如下设定:

   erface QXObject {

   CenterX { get; ; }

   CenterY { get; ; }

   double X { get; ; }

   double Y { get; ; }

   ……

  }

  如此只要对继承此接口类设定好如上属性再对现有QXSpirit和QXMap两个Control控件添加对此接口继承:

  public partial QXSpirit : UserControl, QXObject { …… }

  public partial QXMap : UserControl, QXObject { …… }

  最后再次对前面思路方法进行如下修改:

   ……

   for ( i = 0; i < Carrier.Children.Count; i) {

   (Carrier.Children[i] is QXObject) {

   QXObject Object = Carrier.Children[i] as QXObject;

   Canvas.SetLeft(Object, Object.X - Object.CenterX);

   Canvas.SetTop(Object, Object.Y - Object.CenterY);

   Canvas.SetZIndex(Object, Convert.ToInt32(Object.Y));

   }

   }

   ……

  忽忽大功告成!

  当我们将AllMove9区域代码均补充完整后替换掉第 2十节中AllMove思路方法其他代码个也不用改结果就像变魔术地图移动模式转眼由牵引式地图移动模式转变成主位式地图移动模式地图、遮罩、就连障碍物都同样被无缝移植了这难道不是奇迹吗?欣赏下自己劳动成果吧:



   瞬间模式转换是否让大家感到措手不及匆忙中让太多代码和属性显得臃肿冗余且无章可循那么下节我将对本教程源码进行第次大规模重构从设计升华到艺术这是每位开发者无上追求敬请关注



  源码下载:http://flashview.ddvip.com//2009_07/78.rar



0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: