引言
英雄的故事将要谢幕,似乎每段传奇都该有个华丽的结局。于是我打算用全新的魔法、炫酷的特效和再一次的重构为这期教程画上句号;虽然依旧伴随着一些客观因素导致的瑕疵,然而更让我欣慰的是这款快被敲烂了的Silverlight MMORPG客户端引擎正逐步迈向成熟。或许Silverlight 5 的新特性能给这款引擎带来质的飞跃,又或者我们更加期待Web 3D 游戏时代的来临,那望眼欲穿的公元2011年春。
14.1完结篇[一期] (交叉参考:HLSL自定义渲染特效之完美攻略(上) HLSL自定义渲染特效之完美攻略(中) HLSL自定义渲染特效之完美攻略(下) )
上一节的魔法效果是否已足够打动你的心?或许你会感叹原来Silverlight开发的RPG可以这么美的。其实更奇妙的风景还在下面,为了演绎这场华丽的结局,全新编写的4个魔法旨在换取您的惊叹,一切源于Silverlight,因此您无须复杂的代码照样可以实现绝非简单的游戏特效。
圆月斩,附带HLSL编写的空间扭曲动画效果,技能施放时游戏场景会出现短暂的3D波动效果(空间扭曲动画),这是HLSL给我们所有Silverlight游戏开发者带来的惊喜,2KB成就超绚特效。虽然目前Silverlight 4还无法利用硬件加速进行HLSL渲染,不过令人兴奋的是Moonlight近期已实现了对HLSL编写的渲染进行GPU硬件加速,相信同样的效果不久将出现在Silverlight新版本中,相当期待:
落焰斩,多次伤害附加Storyboard实现的屏幕震动模仿地震效果。其中素材由两部分动画拼接而成;一部分是落下的火焰,另一部分是着地后产生的熔岩喷发。为了轻松的处理多对象间的Z轴层次顺序,可借助切分/分离某些图片/动画部件以达到更加逼真的2.5D游戏场景效果:
碧月斩,直线穿梭类型技能,伤害范围为一个多边形,游戏中的激光等魔法同样可以类似处理,另外与圆月斩类似,附带弹开效果:
地裂斩,直线波浪发散逐环伤害,附带Storyboard实现的击飞效果,如能配上很酷的素材可呈现更加壮观的战场画面。
基于不同类型魔法都拥有各异特性,编写方式也就迥然不同。作为一款能够拓展到商业级别的Silverlight RPG游戏Demo,其动画/魔法的配置文件必须在游戏初期/初始化时被动态下载/加载(同一类的所有Info.xml归到一个文件中)。另外,基于结构的考虑,我重构了魔法控件这一大块,不再有Magic类,所有的技能/魔法依旧由Animation创建,而至今所制作的7类技能/魔法均重新继承自携带抽象方法Run且名为MagicBase的基类:
using
System.Collections.Generic;
using
System.Windows;
using
System.Reflection;
namespace
Controls.Base {
#region
类
public
class
MagicArgs {
///
<summary>
///
获取或设置代号
///
</summary>
public
int
Code {
get
;
set
; }
///
<summary>
///
获取或设置出现坐标
///
</summary>
public
Point Coordinate {
get
;
set
; }
///
<summary>
///
获取或设置目标
///
</summary>
public
Point Target {
get
;
set
; }
///
<summary>
///
获取或设置Z层次
///
</summary>
public
int
Z {
get
;
set
; }
///
<summary>
///
获取或设置等级
///
</summary>
public
int
Level {
get
;
set
; }
///
<summary>
///
获取或设置最小伤害
///
</summary>
public
int
DamageMin {
get
;
set
; }
///
<summary>
///
获取或设置最大伤害
///
</summary>
public
int
DamageMax {
get
;
set
; }
///
<summary>
///
获取或设置范围半径
///
</summary>
public
int
Radius {
get
;
set
; }
///
<summary>
///
获取或设置魔法类型
///
</summary>
public
MagicTypes MagicType {
get
;
set
; }
///
<summary>
///
获取或设置装饰特效
///
</summary>
public
SpecialEffects SpecialEffect {
get
;
set
; }
///
<summary>
///
获取或设置附加效果
///
</summary>
public
AdditionalEffects AdditionalEffect {
get
;
set
; }
///
<summary>
///
获取或设置数量
///
</summary>
public
int
Number {
get
;
set
; }
}
#endregion
#region
枚举
///
<summary>
///
魔法类型
///
</summary>
public
enum
MagicTypes {
///
<summary>
///
圆形
///
</summary>
CircleMagic
=
0
,
///
<summary>
///
扇形
///
</summary>
SectorMagic
=
1
,
///
<summary>
///
十字
///
</summary>
CrossMagic
=
2
,
///
<summary>
///
圆发散波浪
///
</summary>
CircleWaveMagic
=
3
,
///
<summary>
///
圆多段
///
</summary>
MultipleHitCircleMagic
=
4
,
///
<summary>
///
矩形穿梭
///
</summary>
LinearShuttleMagic
=
5
,
///
<summary>
///
矩形波浪
///
</summary>
LinearWaveMagic
=
6
,
}
///
<summary>
///
特殊效果
///
</summary>
public
enum
SpecialEffects {
///
<summary>
///
无
///
</summary>
None
=
-
1
,
///
<summary>
///
麻痹
///
</summary>
Mine
=
7
,
///
<summary>
///
风切
///
</summary>
Wind
=
8
,
///
<summary>
///
灼烧
///
</summary>
Fire
=
9
,
///
<summary>
///
冰冻
///
</summary>
Ice
=
10
,
///
<summary>
///
爆裂
///
</summary>
Explosion
=
12
,
///
<summary>
///
爆裂1
///
</summary>
Explosion1
=
20
,
}
///
<summary>
///
附加效果
///
</summary>
public
enum
AdditionalEffects {
///
<summary>
///
击伤
///
</summary>
Injure
=
0
,
///
<summary>
///
弹开
///
</summary>
Bounce
=
1
,
///
<summary>
///
击飞
///
</summary>
BeKnock
=
2
,
}
#endregion
///
<summary>
///
魔法类
///
</summary>
public
abstract
class
MagicBase {
#region
属性
///
<summary>
///
获取或设置参数
///
</summary>
public
MagicArgs Args {
get
;
set
; }
///
<summary>
///
获取或设置目标精灵集合
///
</summary>
public
List
<
Sprite
>
Targets {
get
;
set
; }
#endregion
#region
构造
public
MagicBase() {
Targets
=
new
List
<
Sprite
>
();
}
#endregion
#region
方法
///
<summary>
///
触发/运行
///
</summary>
///
<param name="caster">
施法者
</param>
///
<param name="scene">
所在场景
</param>
///
<param name="args">
魔法参数
</param>
public
abstract
void
Run(Sprite caster, Scene scene, MagicArgs args);
#endregion
}
}
剩下的仅仅是当技能/魔法被释放时,通过反射来动态加载并创建对应类型的魔法实例,短短几行代码即轻松搞定任意类型技能/魔法的运行:
//
开始技能/魔法攻击
void
sprite_DoCasting(
object
sender, DoCastingEventArgs e) {
Sprite caster
=
sender
as
Sprite;
//
通过反射来加载并实例化各类型魔法
Assembly assembly
=
Assembly.Load(
"
Controls,Version=1.0.0.0
"
);
MagicBase magic
=
assembly.CreateInstance(
string
.Format(
"
Controls.Magic.{0}
"
,e.MagicArgs.MagicType.ToString()))
as
MagicBase;
magic.Run(caster, scene, e.MagicArgs);
}
到此,整个Controls控件项目库的结构已脉络清晰,关系图如下:
技能/魔法在逻辑处理上变化很大,需要对配置参数更细的划分方能适应,比如类型、子对象个数、伤害特效、爆破时特效、是否持续伤害及间隔等等,某些特殊的魔法甚至作为单独的类存在,当然好处也是相对的,游戏的趣味性与众不同,整体可玩性将大幅度提升。
另外,关于精灵资源的布局一直以来让我头疼不已,目前来说我能想到的较好解决方案仅以下两种:
1、 通过独立存储来保存和配置。
2、 也是本节源码中我所重构的方案,即将每个精灵的资源独立存放在新建的Silverlight应用程序中,这样编译后将产生对应包含该精灵资源的XAP文件,需要时通过动态下载XAP再读取其中图片的方法呈现精灵。过程中需要将资源缓存到内存中以保存这些图片的数据流,相对之前的那些方案,该方案在性能和内存方面都可以达到一个折中且偏上的效果,不仅维护方便,且对于资源来说也起到一定的保密作用:
当然,最好的方案我更期待Silverlight 5 为我们提供的3D模型与骨骼动画的原生态支持。
快告一段落了,至今朋友们会发现第一期几乎囊括了MMORPG单机部分的所有功能除了任务系统外。没错,我一直的主张是如果一切都透彻了那就真的失去了继续探索的价值,毕竟当前Silverlight网游开发依旧处于摸索阶段,我和朋友们长期以来都在不断的专研中前行,思索着如何改进与提升。以下也列出了一些个人意见,若想在本课程示例代码上进行更深度的修正以达到较完美境界,还需要补充及完善的内容大致如下:
1、改进及优化动态障碍物系统(包括其中的障碍物碰撞检测、以及精灵AI)。
2、重新封装游戏对象的3个坐标属性以满足各种情况需求:窗口坐标系精确值,游戏坐标系精确值,游戏坐标系整值。
3、编写更多的游戏开发辅助工具以提升效率,比如游戏资源管理器等。
4、PNG图片格式在未来软件应用中显得尤为重要,包括8位和24位,甚至它的动画格式MNG,因为无论是2D还是3D;无论是Silverlight、Flash还是HTML5;无论是在网页上还是客户端亦或是WP7,PNG格式将在未来的2D图像呈现方面长期处于统治地位。
5、更多技巧需要相互的取长补短以习得,做游戏就得从玩游戏开始一点没错。强烈推荐开发者去参考目前已运营的商业游戏,利用浏览器缓存查看它们的资源布局、配置存储、压缩加密以及动画的处理手法等等,高手均诞生于普通人之中,只是看得多了,做的多了就成了师傅,尔耳。
[一期]结束语:一款成功的商业游戏必须具备天时、地利、人和。优秀的美工团队意味着游戏成功了一半,这绝非危言耸听;而像我们这类做程序的则常挂嘴边最多的词不外呼“性能”二字,回归原点,性能的关键取决于细节。完美的画面加上优秀的游戏架构和引人入胜的体验将缔造成功参数中的“人和”,假以时日在那即将载入史册的场景,旷世之巨作莅临终将显得毫无悬念。
牢记这么一句话:成功的秘诀不在别人嘴里,而在你自己手里。
Silverlight网游开发真诚期待您的加入,行动吧!那就从今天开始。
(完)
本课源码:点击进入目录下载
参考资料:中游在线[WOWO世界] 之 Silverlight C# 游戏开发:游戏开发技术
教程Demo在线演示地址:http://cangod.com