一起谈.NET技术,Silverlight 2.5D RPG游戏技巧与特效处理:(二)纸娃娃系统

简介:   纸娃娃系统,或许大家听起来并不陌生。早在十几年前,当时不论是文字游戏“泥巴(Mud)”或是交友、社交网站,我们只能通过屏幕上的文字来传达与交互信息;随着技术不断进步,2D/3D图形技术高速崛起,通过在基础模型上由客户随意挑选、任意更换各种造型(素材),即可打造出真正属于“自我”独特风格的网络虚拟形象,QQ秀便是我们耳熟能详的代表,更贴近真实的如(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 & lt; 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 来说还无法对UIElementOpacity进行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,坐骑前半部分/坐骑后半部分)cd则与前面一致。当然,这或许仅符合我个人的思维习惯,自认为如此配置更便于理解和使用,还是那句老话,只要能给程序的编写带来便利,依旧是仁者见仁,智者见智,并无定论。

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

  大伙应该会留意到,与以前编写的结构有所不同,此时的Sprite的意义得到了更广泛的延伸,是一次新的诠释,它指代所有基于场景坐标系布局中的对象(映射到现实世界中即指一切活动着得对象),比方说角色(如英雄,怪物,宠物,动物,NPC,动画,魔法等),道具(如火焰,植物,飞箭等),特效(如云雾缭绕,打雷闪电,刮风下雨,花叶纷飞)等等,我们均可将其纳入“游戏精灵”的行列。外加上对角色的Coordinate(场景中的Point坐标属性)Position(游戏画布中的Point坐标属性)进行了更完美的协调,于是整个游戏控件项目(Controls)重构后层次关系更趋合理,耦合度降低,重用性更高,更利于后期功能的拓展。

  最后还是得特别强调下,Silverlight游戏中尽量使用小尺寸图片,因为图像的尺寸越大越消耗UI线程。作者曾经尝试过对英雄的4个部件均使用510*510尺寸的帧图像,即精灵每动一下就会同时切换4510*510的图片;此时同屏仅共存10个该英雄便已让CPUFPS痛苦不堪;而如果将该4个部件的每张图像多余的透明部分裁剪掉,即每张帧图片均只有不到100的宽和高,然后通过TranslateTransform偏移到共同位置上,性能较之前几乎提升了几十个数量级,同屏1004件套精灵FPS照样不下30,开发者们切记了:

  本节源码下载地址:Demo1.rar

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

目录
相关文章
|
1天前
|
人工智能 开发框架 量子技术
【专栏】.NET 技术:驱动创新的力量
【4月更文挑战第29天】.NET技术,作为微软的开发框架,以其跨平台、开源和语言多样性驱动软件创新。它在云计算、AI/ML、混合现实等领域发挥关键作用,通过Azure、ML.NET等工具促进新兴技术发展。未来,.NET将涉足量子计算、微服务和无服务器计算,持续拓宽软件开发边界,成为创新的重要推动力。掌握.NET技术,对于开发者而言,意味着握有开启创新的钥匙。
|
1天前
|
开发框架 .NET C#
【专栏】理解.NET 技术,提升开发水平
【4月更文挑战第29天】本文介绍了.NET技术的核心概念和应用,包括其跨平台能力、性能优化、现代编程语言支持及Web开发等特性。文章强调了深入学习.NET技术、关注社区动态、实践经验及学习现代编程理念对提升开发水平的重要性。通过这些,开发者能更好地利用.NET构建高效、可维护的多平台应用。
|
1天前
|
机器学习/深度学习 vr&ar 开发者
【专栏】.NET 技术:引领开发新方向
【4月更文挑战第29天】本文探讨了.NET技术如何引领软件开发新方向,主要体现在三方面:1) 作为跨平台开发的先锋,.NET Core支持多操作系统和移动设备,借助.NET MAUI创建统一UI,适应物联网需求;2) 提升性能和开发者生产力,采用先进技术和优化策略,同时更新C#语言特性,提高代码效率和可维护性;3) 支持现代化应用架构,包括微服务、容器化,集成Kubernetes和ASP.NET Core,保障安全性。此外,.NET还不断探索AI、ML和AR/VR技术,为软件开发带来更多创新可能。
|
1天前
|
开发框架 Cloud Native 开发者
【专栏】剖析.NET 技术的核心竞争力
【4月更文挑战第29天】本文探讨了.NET框架在软件开发中的核心竞争力:1) .NET Core实现跨平台与云原生技术的融合,支持多操作系统和容器化;2) 提升性能和开发者生产力,采用JIT、AOT优化,提供C#新特性和Roslyn编译器平台;3) 支持现代化应用架构,包括微服务和容器化,内置安全机制;4) 丰富的生态系统和社区支持,拥有庞大的开发者社区和微软的持续投入。这些优势使.NET在竞争激烈的市场中保持领先地位。
|
1天前
|
开发框架 .NET 开发者
【专栏】领略.NET 技术的创新力量
【4月更文挑战第29天】.NET技术自ASP.NET起历经创新,现以.NET Core为核心,展现跨平台能力,提升性能与生产力,支持现代化应用架构。.NET Core使开发者能用同一代码库在不同操作系统上构建应用,扩展至移动和物联网领域。性能提升,C#新特性简化编程,Roslyn编译器优化代码。拥抱微服务、容器化,内置安全机制,支持OAuth等标准。未来.NET 6将引入更快性能、Hot Reload等功能,预示着.NET将持续引领软件开发潮流,为开发者创造更多机会。
|
1天前
|
物联网 vr&ar 开发者
【专栏】.NET 技术:为开发注入活力
【4月更文挑战第29天】本文探讨了.NET技术的创新,主要体现在三个方面:1) .NET Core实现跨平台开发革命,支持多种操作系统和硬件,如.NET MAUI用于多平台UI;2) 性能提升与生产力飞跃,C#新特性简化编程,JIT和AOT优化提升性能,Roslyn提供代码分析工具;3) 引领现代化应用架构,支持微服务、容器化,内置安全机制。未来,.NET 7将带来更多新特性和前沿技术整合,如量子计算、AI,持续推动软件开发创新。开发者掌握.NET技术将赢得竞争优势。
|
1天前
|
人工智能 前端开发 Cloud Native
【专栏】洞察.NET 技术的开发趋势
【4月更文挑战第29天】本文探讨了.NET技术的三大发展趋势:1) 跨平台与云原生技术融合,通过.NET Core支持轻量级、高性能应用,适应云计算和微服务;2) 人工智能与机器学习的集成,如ML.NET框架,使开发者能用C#构建AI模型;3) 引入现代化前端开发技术,如Blazor,实现前后端一致性。随着.NET 8等新版本的发布,期待更多创新技术如量子计算、AR/VR的融合,.NET将持续推动软件开发的创新与进步。
|
1天前
|
人工智能 前端开发 Devops
【专栏】洞察.NET 技术在现代开发中的作用
【4月更文挑战第29天】本文探讨了.NET技术在现代软件开发中的核心价值、应用及挑战。.NET提供语言统一性与多样性,强大的Visual Studio工具,丰富的类库,跨平台能力及活跃的开发者社区。实际应用包括企业级应用、Web、移动、云服务和游戏开发。未来面临性能优化、容器化、AI集成等挑战,需持续创新。开发者应深入理解.NET,把握技术趋势,参与社区,共创美好未来。
|
1天前
|
开发工具 C# 开发者
【专栏】理解.NET 技术,开创美好未来
【4月更文挑战第29天】本文探讨了.NET技术在软件开发中的关键作用,强调其核心优势,如语言多样性、丰富类库、强大的开发工具和跨平台能力。.NET在现代应用开发中涉及企业级应用、云服务集成、微服务、移动应用和游戏开发。未来,.NET将持续创新,提升性能,拓展应用场景,并促进更紧密的社区合作,通过跨平台框架扩大应用范围。开发者应深入学习.NET,抓住技术趋势,共创美好未来。
|
1天前
|
机器学习/深度学习 人工智能 开发者
【专栏】.NET 技术:为开发带来新机遇
【4月更文挑战第29天】本文探讨了.NET技术如何为软件开发带来新机遇,分为三个部分:首先,.NET的跨平台革命,包括.NET Core的兴起、Xamarin与.NET MAUI的移动应用开发、开源社区的推动及性能优化;其次,介绍了云服务与微服务架构的集成,如Azure云服务、微服务支持、DevOps与CI/CD,以及Docker容器化;最后,讨论了AI与机器学习集成,如ML.NET、认知服务、TensorFlow和ONNX,使开发者能构建智能应用。面对这些机遇,开发者应不断学习和适应新技术,以创造更多价值。

热门文章

最新文章