`

Silverlight 2.5D RPG游戏技巧与特效处理:纸娃娃系统

阅读更多

 纸娃娃系统,或许大家听起来并不陌生。早在十几年前,当时不论是文字游戏“泥巴(Mud)”或是交友、社交网站,我们只能通过屏幕上的文字来传达与交互信息;随着技术不断进步,2D/3D图形技术高速崛起,通过在基础模型上由客户随意挑选、任意更换各种造型(素材),即可打造出真正属于“自我”独特风格的网络虚拟形象,QQ秀便是我们耳熟能详的代表,更贴近真实的如(RPG)游戏及虚拟现实中的换装/换肤系统同样亦得益于纸娃娃机制。
本节,我将向大家讲解如何最好的实现Silverlight 2.5D网络游戏中的纸娃娃系统,以最大程度控制性能损失为前提,将游戏资源占用最小化,综合效果及用户体验最优化。
以《Silverlight MMORPG网页游戏开发课程(Game Lesson)一期》的源码为基础,我将其又一次的进行了大规模重构。
素材来源于网络,取《封神榜3》中的角色系统(纸娃娃系统)做示例,每个角色大致都包含3个部件:铠甲(身体)、武器、骑乘(乘具)等,而其中的骑乘道具又由2个部份组成,比如异人(弓手)的翅膀分为左右两支;甲士(战士)的坐骑分为前后两半;而方士(法师)的飞剑则仅为单独对象:


2D/2.5D游戏中角色带翅膀飞行要考虑左右翼与身体的层次关系,骑马则需要考虑马头/马尾与身体间的层次问题。而且武器长短,角色朝向,行为姿势等也都可能影响到各部件的层次关系。因此,一些游戏为了简化设计,同时又不失华丽,于是乎诞生了比如“踏云”,“御剑”,“乘鹤”,“踩蝶”等诸多天马行空的驾驭模式,这些乘具的共同点就是均被踩在脚上,自然而然处理起来更简单明了。当然,如果角色是3D模型的话则无需考虑这么多层叠关系。
鉴于以上的参考分析,在Silverlight中构造装备纸娃娃系统框架便会轻松很多。暂时以带翅膀的弓手为例子,依葫芦画瓢,我们首先新建如下几个类:


如图,EquipBase乃装备(纸娃娃)系统中的核心,所有的装备部件类比如铠甲(身体)Armor/武器Weapon/翅膀Wing/坐骑Ride均继承自该类:

    /// <summary>
    /// 装备部件基类
    /// </summary>
    public abstract class EquipBase : ObjectBase {

        /// <summary>
        /// 加载完毕
        /// </summary>
        public event EventHandler Ready;

        /// <summary>
        /// 获取或设置部件名
        /// </summary>
        protected string partName { get; set; }

        long index = 0; //异步加载与换装同步协调
        public override int Code {
            get { return base.Code; }
            set {
                index++;
                if (value == -1) { base.Code = value; return; }
                string key = string.Format("{0}{1}", partName, value);
                if (Res.ContainsKey(key)) {
                    base.Code = value;
                    loadConfig(key);
                } else {
                    Downloader downloader = new Downloader();
                    downloader.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompleted);
                    downloader.OpenReadAsync(string.Format("{0}{1}.xap", partName, value), string.Format("{0},{1}", index, value), 2000);
                }
            }
        }

        void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e) {
            Downloader downloader = sender as Downloader;
            downloader.OpenReadCompleted -= webClient_OpenReadCompleted;
            string[] str = e.UserState.ToString().Split(',');
            if (Convert.ToInt64(str[0]) == index) {
                int code = Convert.ToInt32(str[1]);
                string key = string.Format("{0}{1}", partName, str[1]);
                if (!Res.ContainsKey(key)) { Res.Add(key, new StreamResourceInfo(e.Result as Stream, "application/binary")); }
                base.Code = code;
                loadConfig(key);
            }
        }

        Dictionary<string, Point> frameOffset = new Dictionary<string, Point>(); //各帧偏移
        /// <summary>
        /// 加载配置
        /// </summary>
        void loadConfig(string key) {
            XElement info = XElement.Load(Application.GetResourceStream(Res[key], new Uri("Info.xml", UriKind.Relative)).Stream).DescendantsAndSelf(partName).Single();
            FullName = info.Attribute("FullName").Value;
            //解析各帧偏移
            IEnumerable<XElement> iFrame = info.Element("Frames").Elements();
            frameOffset.Clear();
            foreach (XElement element in iFrame) {
                frameOffset.Add(element.Attribute("ID").Value, new Point() {
                    X = (double)element.Attribute("OffsetX"),
                    Y = (double)element.Attribute("OffsetY"),
                });
            }
            if (Ready != null) { Ready(this, null); }
        }

        bool _IsTurn;
        /// <summary>
        /// 获取或设置是否水平翻转
        /// </summary>
        public bool IsTurn {
            get { return _IsTurn; }
            set {
                if (_IsTurn != value) {
                    Transform = (_IsTurn = value) ? scaleTransform : null;
                }
            }
        }

        bool _Flash;
        /// <summary>
        /// 获取或设置是否闪光
        /// </summary>
        public bool Flash {
            get { return _Flash; }
            set {
                if (_Flash != value) {
                    //if (_Flash = value) {
                    //    dispatcherTimer.Start();
                    //} else {
                    //    this.Opacity = 1;
                    //    dispatcherTimer.Stop();
                    //}
                    this.Opacity = (_Flash = value) ? 0.4 : 1;
                }
            }
        }

        bool order = false;
        DispatcherTimer dispatcherTimer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(100) }; //换装时的闪光特效计时器
        public EquipBase() {
            dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
        }

        void dispatcherTimer_Tick(object sender, EventArgs e) {
            if (order) {
                this.Opacity = this.Opacity + 0.1;
                if (this.Opacity >= 1) { order = false; }
            } else {
                this.Opacity = this.Opacity - 0.1;
                if (this.Opacity <= 0.3) { order = true; }
            }
        }

        static Dictionary<string, Stream> equipRes = new Dictionary<string, Stream>();

        ScaleTransform scaleTransform = new ScaleTransform() { ScaleX = -1 };
        /// <summary>
        /// 呈现帧图
        /// </summary>
        public void Display(string key) {
            string resKey = string.Format("{0}{1}{2}", partName, Code, key);
            if (!equipRes.ContainsKey(resKey)) {
                equipRes.Add(resKey, Application.GetResourceStream(Res[string.Format("{0}{1}", partName, Code)], new Uri(string.Format("{0}.png", key), UriKind.Relative)).Stream);
            }
            this.StreamSource = equipRes[resKey];
            this.InternalOffset = frameOffset[key];
            if (IsTurn) { scaleTransform.CenterX = Center.X - frameOffset[key].X; }
        }

        public override void Dispose(object sender, EventArgs e) {
            dispatcherTimer.Stop();
            dispatcherTimer.Tick -= dispatcherTimer_Tick;
            base.Dispose(sender, e);
        }
    }
内容比较简明:当角色需要换装时,通过异步下载的方式获取该装备部件的XAP包,一旦下载完毕便将其缓存起来使用。当然,由于是异步,在整个Loading的过程中为了提高用户体验,我们可以在角色(Role)身上做些修饰以让玩家一看就明白该角色正处于换装Loading,比较有新意的做法是在角色中心添加一些描述性的文字,或使用一些旋转类的动画(Animation):


另外,我在其中还增加了一个名为Flash的方法,即当某个装备部件正处于Loading过程中时,该部件将执行时隐时现的Opacity动画,这种效果最完美了。不过,就目前的Silverlight 4 来说还无法对UIElement的Opacity进行GPU硬件加速,暂时该方案的拓展与取舍/取代问题只能交由大家一同探讨。
然后是关于换装系统中的素材资源的组织。对于像Silverlight这样基于动态加载的游戏开发技术来说,最大程度减少质量损失前提下的资源容量高度浓缩有利于网页游戏的动态加载,以及像Windows Phone这样磁盘空间相对较小的移动设备平台。以精致的2.5D网游中的角色为例,大都以8方向居多,当然我们也还是能够仅仅使用5个方向素材即达到减少资源开支的效果(比如对其中的东北、东、东南进行水平翻转):


此方法通过牺牲少量的性能进行水平翻转达到让资源总量减少近一半,且画质不打折扣之效果。唯一缺陷就是武器永远处于同一只手中,无论面朝左或是右;不过就整体而言,这不失为大多数网页游戏之首选。另外,对于Silverlight开发2.5D网页游戏来说,将图像资源PNG8化确实必要而关键。由于本节源码中的素材均来源于网络,所以效果很一般,如果是由3D美术原创的话,将逐帧图像导出并处理成颜色过渡均匀,边线条纹清晰流畅且无镂空的PNG8精美素材并非难事,最终将再次大幅度降低游戏整体资源及内存占用:


此时,大家应该有注意到本节中资源的命名规范与以往有了些变化,形如a-b-c-d.png的形式,对于铠甲(身体)和武器来说,a代表状态(打坐/步行/骑乘);b代表行为动作(停止/移动/攻击/受伤);c代表朝向;d代表帧号。而对于骑乘道具,比如翅膀和坐骑来说,a代表行为动作;b代表对象代号(比如翅膀1/翅膀2,坐骑前半部分/坐骑后半部分);c和d则与前面一致。当然,这或许仅能符合我个人的思维习惯,自认为如此配置更便于理解和使用,还是那句老话,只要能给程序的编写带来便利,结果依旧是仁者见仁,智者见智,并无定论。

当装备类及相关资源设置完毕后,我们便可通过一个Role控件作为容器进行统一包装管理,此时我们创建一个名为RoleBase的角色基类,游戏中一切主体生命对象均由此衍生而来,比如英雄(Hero)/怪物(Monster)/非控对象(NPC)等等:


大伙是否注意到,与以前编写的结构有所不同,此时的Sprite的意义得到了更广泛的延伸,是一次新的诠释,它指代所有基于场景坐标系布局中的对象(映射到现实世界中即指一切活动着得对象),比方说角色(如英雄,怪物,宠物,动物,NPC,动画,魔法等),道具(如火焰,植物,飞箭等),特效(如云雾缭绕,打雷闪电,刮风下雨,花叶纷飞)等等,我们均可将其纳入“游戏精灵”的行列。外加上对角色的Coordinate(场景中的Point坐标属性)和Position(游戏画布中的Point坐标属性)进行了更完美的协调,于是整个游戏控件项目(Controls)被大规模重构,层次关系更趋合理,耦合度降低,重用性更高,更利于后期功能的拓展。
最后还得特别强调下,Silverlight游戏中尽量使用小尺寸图片,因为图像的尺寸越大越消耗UI线程。作者曾经尝试过对英雄的4个部件均使用510*510尺寸的帧图像,即精灵每动一下就会同时切换4张510*510的图片;此时同屏仅共存10个该英雄便已让CPU和FPS痛苦不堪;而如果将该4个部件的每张图像多余的透明部分裁剪掉,即每张帧图片均只有不到100的宽和高,然后通过TranslateTransform偏移到共同位置上,性能较之前几乎提升了几十个数量级,同屏100个4件套精灵FPS照样不下30,开发者们切记了:

 

在线演示地址:http://silverfuture.cn/

分享到:
评论

相关推荐

    Silverlight 2.5D RPG游戏技巧与特效处理源代码与说明

    在"Silverlight 2.5D RPG游戏技巧与特效处理:(二)纸娃娃系统 - alamiye010 - CSDN博客.htm"这篇文章中,作者alamiye010详细介绍了如何在Silverlight中实现这一系统。该系统允许玩家在游戏过程中通过更换装备来...

    Silverlight项目获取天气、PM2.5信息

    【Silverlight项目获取天气、PM2.5信息】 在信息技术领域,Silverlight是一种由微软开发的富互联网应用程序(RIA)平台,它允许开发者创建交互性强、具有多媒体元素的Web应用程序。本项目专注于利用Silverlight技术...

    Silverlight MMORPG网页游戏源码

    其实更奇妙的风景还在下面,为了演绎这场华丽的结局,全新编写的4个魔法旨在换取您的惊叹,一切源于Silverlight,因此您无须复杂的代码照样可以实现绝非简单的游戏特效。 圆月斩,附带HLSL编写的空间扭曲动画效果,...

    软件工程师-Silverlight游戏开发小技巧.docx

    Silverlight 游戏开发小技巧 本文主要介绍了 Silverlight 游戏开发中的小技巧,具体来说是血条和进度条的实现方法。Silverlight 是.NET 技术中游戏开发的重要组成部分,本文将详细讲解如何使用 Silverlight 实现...

    C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial 前38节)

    C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三十三) 锦上添花之魔法特效装饰 C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三十四) 地图编辑器诞生啦! C#开发WPF/Silverlight...

    Silverlight RPG游戏开发课程(内容教案)

    在深入学习 Silverlight RPG 游戏开发之前,首先需要理解 Silverlight 的基本概念和技术优势。Silverlight 是微软推出的一种 Web 前端应用开发工具,它主要用于构建富互联网应用程序(RIA),并且以浏览器插件的形式...

    Silverlight编程基本知识及技巧

    【Silverlight编程基本知识及技巧】 Silverlight是微软推出的一种基于.NET Framework的浏览器插件,用于创建丰富的交互式Web应用程序。它提供了丰富的图形、动画、媒体播放和数据绑定功能,使得开发者可以构建出与...

    Silverlight网络游戏飞行岛2.0源码

    《Silverlight网络游戏飞行岛2.0源码》是一款基于Microsoft Silverlight技术开发的网络游戏,它展示了Silverlight在游戏开发中的应用。Silverlight是微软推出的一种RIA(Rich Internet Application)技术,用于创建...

    silverlight写的一个图片特效

    《使用Silverlight实现图片特效详解》 Silverlight,作为微软推出的一种富互联网应用程序技术,曾经在Web开发领域占据一席之地。它允许开发者创建具有丰富交互性和多媒体元素的Web应用,其中图片特效就是其一大亮点...

    一款Silverlight很酷的游戏

    标题中的“一款Silverlight很酷的游戏”表明我们讨论的主题是一款基于Silverlight技术开发的互动游戏。Silverlight是微软推出的一种富互联网应用程序(RIA)平台,主要用于构建和展示具有丰富媒体体验和交互性的Web...

    Silverlight富媒体特效地图实例

    通过这个实例,开发者不仅可以掌握Silverlight与Bing Maps的结合使用,还能了解到如何将富媒体特效融入地图应用,提升用户体验。这在地理信息系统、导航、房地产、旅游等领域都有广泛的应用价值。通过深入研究和实践...

    silverlight 网页聊天系统

    《基于Silverlight的网页聊天系统详解》 Silverlight,由微软公司开发,是一种强大的工具,用于构建具有丰富用户体验的Web应用程序。在这个特定的项目中,我们关注的是一个使用Silverlight 3技术构建的网页聊天系统...

    Silverlight空中躲避游戏源码.zip

    通过深入研究这个Silverlight空中躲避游戏的源码,开发者不仅可以掌握Silverlight的基本用法,还能了解到游戏开发的核心技巧,如游戏循环、碰撞检测、动画制作以及用户体验设计。这将对提升Silverlight开发技能,...

    silverlight书页翻动特效

    1. **UI元素动画**:Silverlight提供了强大的动画系统,可以对UI元素的各个属性如位置、大小、透明度等进行平滑的动画处理。书页翻动特效通常会用到旋转、缩放和透明度变化等动画来模拟纸张的翻转动作。 2. **3D...

    silverlight+wcf n维拼图游戏

    【银光+N维拼图游戏:Silverlight与WCF技术的完美融合】 本文将深入探讨一个基于Silverlight和WCF技术构建的n维拼图游戏的实现细节。Silverlight是微软推出的一种富互联网应用程序(RIA)平台,它允许开发者创建...

    silverlight做的小游戏

    silverlight做的小游戏,提供源代码!

    Silverlight星际竞技场游戏.rar

    3. **动画和特效**:Silverlight提供了强大的动画支持,可以创建动态的UI元素和游戏效果,如角色移动、技能释放等。 4. **网络通信**:游戏中的多人在线功能需要实现客户端和服务器之间的实时通信。Silverlight支持...

    使用Silverlight实现特效

    总的来说,实现Silverlight的翻书特效需要对Silverlight的UI设计、动画、3D支持和事件处理有深入的理解。通过结合C#编程技巧和ASP.NET的集成,你可以创建出一个生动、真实的在线阅读体验。不断学习和实践这些技术,...

    Silverlight空中躲避游戏源码

    总的来说,这个源码项目为学习Silverlight编程提供了一个实践平台,通过对各部分代码的分析和理解,我们可以深入掌握Silverlight的游戏开发技巧,同时也能对ASP.NET等相关技术有更深入的了解。这不仅有助于提升我们...

Global site tag (gtag.js) - Google Analytics