代码改变世界

Silverlight 游戏开发记录(1)

2010-12-17 16:07  老咩  阅读(3047)  评论(30编辑  收藏  举报

做为一个很早学习Silverlight 的银光人,07年底就开始使用了Silverlight了,08年末就冒出使用Silverlight开发网络游戏的想法。很可惜,09虽然成功的转身到了web游戏开发行业,最后因为种种原因,项目使用了as3,导致最后只能去写服务端(我只会C#)。

还是as3项目的原因,开发了一半,被迫结束.人员散的散,离的离。最后进入胶着状态。

公司因为某些原因,非常需要自己的mmo游戏。于是,又开始了新的项目,这回,我总算搞上Silverlight客户端了。不过,悲剧的是,公司完全没有招人计划,结果本人即要写客户端,也要写服务端。神啊....

 

我所知道的Silverlight开发的在运营的商业网络游戏,貌似是一款都没有,当然,有个窝窝世界在内测。还有其它好几款,不过都没有正式商业运营。这不能代表Silverlight比as3差。公平的说,Silverlight略胜 as3, 长远的说,大胜 as3.

 

大部分情况下,使用silverlight 和 as3 开发的都是2d, 或所谓 2.5d 的等角游戏,在基本效率上,2者是半斤八两,如果有人说 silverlight 快过 as3 多少多少倍,那绝对是瞎说。 在我看来,as3 的优势在于市面上不少成品,设计人员比较容易找(这里指UI设计)。劣势是相对as3程序员较少,并且开发工具实在是不如vs.

silverlight的优势在于,有大量的.net开发人员,这些人如果有丰富的c/s开发经验,或wpf经验,学习silverlight非常快。另外,如果团队小,人员吃紧,还可以调用客户端的去开发服务端。as3 可开发不了游戏服务器啊。当然,劣势在于设计人员非常难找,会使用Blend的人设计人员太少。

 

下面正式说客户端。

我看过窝窝世界部分代码,我比较佩服其开发人员,的确很周到和仔细,可能之前有过相关经验。但是,我认为毛病也是非常明显的,窝窝世界是一个典型的面向对象设计,重重继承,跟踪1个类到其基类,竟然继承了10多级。如果是自己开发,并自己维护,可能无所谓,如果有天自己干累了,想让别人接接手,估计那人就痛苦了。

 

一些游戏开发书籍上,以及很多游戏,比较流行的方式是组件式设计。颇有代表性的像游戏《孢子》。组件式,故名思意,就是像积木一样组合起来。有点插件系统的性质。

这种好处是显而易见的,容易修改,还容易做相关编辑器。

组件设计的原理是这样的,所有客户端里的游戏对象,有1个对象id,如果这个对象可视,就为这个对象增加1个可视组件,如果可被攻击,就为这个对象增加1个攻击组件。当然,我们有1个组件管理中心来负责游戏对象的功能注册。

这种设计一个无法绕过的缺点是,需要频繁的进行组件功能查询。不过,作为1个客户端,实际上同一时间内没有太多对象,所以,这里完全没有性能上的忧虑。

 

我的核心设计主要如下

 

 

额,比较臭美的用System结尾

NetWorkSystem: 网络库,其内部结构为

有些臃肿,主要是我从服务端通讯模块里“扒”出来的。这个结构,要偷偷汗颜一下,大部分从某开源里学习的,内部做了一些改进。

Silverlight MMO 游戏与传统的客户端游戏都是使用Socket进行通讯,通讯的过程就像用快递送东西。举个例子,你的公司如果是个游戏客户端,你是游戏里的英雄角色。如果有你的快递,会是个什么情况?------快递送东西送到前台,前台并不认识你。

1。也就是说,你首先得告诉前台你是谁,那部门的,坐哪个位置,分机号多少。

2。你的快递包裹到了,前台查了下人员名单,发现和你名字很像,于是打电话让你取掉。

3。你获得包裹,拆开看看,发现是报销发票,于是你就送到财务去报销去了。

 

设计思路就是



/// <summary>
    /// 接收消息接口,所有需要从网络接收消息的对象需要继承此接口
    /// </summary>
    public interface IReceive : IComponent
    {
        void OnNetMessageArrive(ushort MessageID, byte[] Message);
    }

所有需要进行消息处理的游戏对象都继承这个接口,然后注册到网络消息投递中心,当有消息来,就会把消息投递到游戏对象。

 

大部分游戏都是这样设计,由于从客户端的角度看来,通讯频率不高,所以这样做没什么错。

但是,通常,某个对象不可能只处理1条消息,于是方法内部就搞一堆的case 来处理不同消息号的消息。

其实,完全可以把功能也注册到消息投递中心。因为一般说来,某个消息号的消息只由1个固定类型的对象来处理。

最后就成了快递到了,前台先看了下快递单上的代号,查了下表,发现是你的报销单,于是直接帮你送到财务去了。

 

ObjectSystem: 前面说的组件系统,比如消息对象接口IReceive,就要继承组件接口IComponent

 

 

RenderSystem: 渲染系统,Silverlight里谈不上什么渲染,实际上就是能看见的可视对象,内部有个IRender 接口组件,所有需要显示的,例如角色,地物,地图,掉落物,动画,效果,统统继承该组件。并由渲染管理器进行管理。

 

ResourceSystem:资源系统,这个无论游戏还是其它程序都是必须的,就是负责加载和读取资源。主要需要从网络读取,从本地存储读取。

所以一般的,文件路径都是相对路径。这里,我有2个接口,1个IResource 资源接口,用来实现不同的游戏资源格式。1个IResourceLoad接口,用来实现资源的加载处理。这样,可以为同一类型的资源,修改使用不同的加载方式。

 

SceneSystem: 场景系统,主要进行游戏场景的管理。例如 处理地图的层。一般的,游戏场景都是由若干层叠加的。加载的时候默认可以逐层加载,比如先加载最下面一层,通常是背景层,然后加载地表层,地物层,拾取层,角色层。当然,你想分几层就分几层,最上面一层,也可以用于界面层。

 

ScriptSystem:脚本系统,我没去研究过lua,也不太会C++,所以完全不知道一般网络游戏的调用脚本方法。Silverlight 可以调用javascript。个人认为,还不如直接使用C#来做脚本。javascript 直接明文可见(不知隐藏方法),并且是解释式的。另外,服务端也需要脚本,直接一套下来不省事很多。

下面是脚本系统的测试图

其代码为:

 

代码
Console.WriteLine("脚本系统测试开始");
Console.WriteLine(
"新建生物");
Entity play
= new Entity("玩家");
Entity guard
= new Entity("城市守卫");


//加入受伤处理脚本
TestScript ts = new TestScript() { 攻 = play, 受 = guard };
guard.Hurt.AddHandler(ts);

//为城市守卫创建触发器
ScriptEvent dieEvent = guard.Events.CreateEvent("Die");
//为触发器加入死亡处理脚本
DieScript ds = new DieScript() { Et = guard };
dieEvent.AddHandler(ds);

Console.WriteLine(
"测试生物攻击");
guard.ByAttack(play);
guard.ByAttack(play);
guard.ByAttack(play);
guard.ByAttack(play);
guard.ByAttack(play);
Console.WriteLine(
"脚本系统测试结束");
Console.Read();

呵呵,很像魔兽争霸吧,额,就是模仿魔兽争霸编辑器滴。

 

 

WidgetSystem: 界面组件系统。这个没有考虑周全,本来是想设计成负责界面层的,后来发现貌似有点多余(想做成类似魔兽世界脚本开发界面)。其接口IWidget  继承渲染接口和消息接口。

 

还缺什么,好像还有声音,不过又不是3d游戏,声音就是播放下声音文件而已。暂时先没有设计,或者放到客户端主逻辑里设计也行。如果还缺其它关键系统,请留言指教。