一直以来,我不断的在思考着如何能让Silverlight在游戏制作方面更简单。《圣经》说:上帝创造了世界;“游戏”无论在何等年代,都被视为生物在一定程度上模拟真实世界而进行的特定活动。基于爱因斯坦的时空理论,存在于世界中的万物其根本结构为3维空间 + 1维时间,于是乎脑海中浮现出了一个很棒的游戏基类:GameBase

“物体”,用以形容世界中的一切对象,作为客观存在,我们无法实例化它:public abstract class GameBase。所有的“物体”都具备3维空间特性,简单的说即XYZ

        /// <summary>

        /// 获取或设置XY坐标

        /// </summary>

        public virtual Point Coordinate {

            get { return new Point(Canvas.GetLeft(this), Canvas.GetTop(this)); }

            set { Canvas.SetLeft(this, value.X); Canvas.SetTop(this, value.Y); }

        }

        /// <summary>

        /// 获取或设置Z层次深度

        /// </summary>

        public int Z {

            get { return Canvas.GetZIndex(this); }

            set { Canvas.SetZIndex(this, value); }

   }

Coordinate定义为Point类型为了方便被Storyboard动画所兼容,并且作为一个virtual虚属性,子类可以根据自身特定需求选择是否重写。

另外,我们还必须赋予“物体”一个用以容纳其他部件的外在“躯体”,以至我们能在视觉上看到它:

GameBase : Canvas

Canvas是非常不错的选择。相较于Image,它拥有同样出色的Background属性用于“躯体”图片的呈现;而对比Grid,同样作为继承自Panel的容器控件,它能轻松布局所包容的一切对象于身体的任何角落。总的评价:超薄、轻量,简约而不简单。

最后,我们还得赋予“物体”一个能获取外部图片进行“皮肤”包装的渠道,又见GetImage方法:

        /// <summary>

        /// 获取指定路径的图片

        /// </summary>

        protected virtual BitmapImage GetImage(string uri) {

            return new BitmapImage((new Uri(string.Format(@"../Images/{0}", uri), UriKind.Relative))){

                 CreateOptions = BitmapCreateOptions.None

            };

        }

    该方法指定了图片为时时下载即呈现的获取方式。当然,作为virtual虚方法,任何“物体”的子类同样可以根据自身需要进行重写。举个简单的例子,我希望精灵在还未下载完56张所有帧序列图片前仅用一张图例来做呈现,那么我可以通过在精灵控件中重写GetImage方法,利用WebClient或其他的什么方式去实现相关的逻辑行为,只要把握好图片资源从下载到呈现这整个流程,实现起来还是比较简单的。

接下来,我将向游戏中引入最最重要的“一维时间”。

在我们的真实世界里,“物体”从广义上讲可以划分为“动态物体”和“静态物体”。唯有“动态物体”才会主动与时间进行交互,而“静态物体”则在无任何外部介入的前提下永恒不变。回述到游戏世界,游戏中最具代表性的“动态物体”莫过于家喻户晓的精灵(Sprite),它以各种形态广泛的存在于所有游戏中:

(素材来源:卡嘎亚精灵)

至于“静态物体”,同样是每款游戏必不可少的HUD最具代表性。何谓HUD?其英文全称为Head Up Display,即永远呈现在屏幕最前端的对象。有些抽象,大家不妨先看下图:

    该图为CS的游戏截图,其中3个红色框框住的区域内的面板即为HUD,它的最大特点是永远于游戏的最前端呈现;并且,它其中的数据更新是受主角相关信息影响所至而非自行实现。举个例子,主角开枪射击,消耗了子弹,此时才会提交给最下面的HUD更新其中子弹的显示数量;同样的,主角不停的移动、变向,从而触发左上角雷达地图数据的更新等等。至于如何实现这些相关触发,在Silverlight中我们可以通过在精灵内部定义相关委托和事件轻松实现,这些都是后话了。

回到主题,于是乎我们再次得到两个均继承自GameBase的抽象子类DynamicObjectStaticObject。根据前文的分析,只有DynamicObject才能拥有时间的概念;在Silverlight中,我优先选择使用DispatcherTimer来描述这1维时间。其优点在于:我们无须考虑任何跨线程的相关问题;并且,通过DispatcherTimerTick事件(类似线程的Join而非创建新的线程),“物体”的任何活动都将直接被游戏世界所呈现,如同一颗心脏,每一次强有力的跳动都证明着生命的存在。

到此,一个非常非常简单且高度通用可拓展的游戏框架底层诞生了~。在未来的日子里,无论是为游戏中加入“动态”的精灵、魔法、场景亦或是“静态”的WindowFloatableWindowHUD等对象,我们仅仅需要做的是让它们分别继承自相应的DynamicObjectStaticObject即可;而根据实际不同类型游戏的设计需要,我们还可以将某些不能再被继承的类如场景(Scene)密封(Sealed)以提高性能,而将精灵类(Sprite)继续抽象,并衍生出诸如LeaderNPCMonster等等的实体密封子类,很有趣对吧~嘿嘿。其实一切不都源于我们这个真实的世界啊~

至此,我们将得到类似上图这样的游戏基础框架。其实游戏底层的搭建方式多种多样,你可以面向更具体的对象或将抽象定义得更广泛些;另外,游戏底层框架的构建也必须准确的建立在你所想要开发的游戏类型与它的具体表现形式上。总体说来:万变不离其宗。惟有准确的把握好游戏中元素的共性,把握住游戏中各元素之间千丝万缕的联系,那么你初期架设的游戏根基将为您后期的游戏开发奠定强健的基石。好比一座建筑,根基的质量决定了它能盖多高,同样也决定了它的寿命以及抗灾害风险的能力。

工欲善其事必先利其器。按照本文的思路,大家不难看出作者已毅然决定采用以场景为主要对象的游戏架构模式。场景作为游戏中各元素之间的重要联系枢纽,为了让它变得更灵活且易于管理,于是便诞生了上一节我向大家介绍的Silverlight游戏场景编辑器。使用过的朋友一定觉得非常有意思,其最大的特别便在于“灵活”,同样也是该编辑器的灵魂所在。工具如果只能针对局限性小的范围对象,它是失败的;以基于本文叙述的框架为基础,作者用最少量且易于理解的简单代码实现了该编辑器的所有功能。那么下一节,我将进一步向大家讲解该编辑器的具体制作流程与分析,敬请关注。

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