Symbian中的游戏编程(一)

类别:软件工程 点击:0 评论:0 推荐:

翻译:huwell

*以下内容选译自《Programming Games for Series 60》,这是Nokia公司专门整理的一篇论文,主要讲述了在symbian系统中进行游戏编程的相关内容。

所有的系统端事件都有这样的一个通用性质,那就是可以被一个应用程序所捕获。当一个系统事件发生时,最前面的应用程序会失去焦点。这会导致该应用程序user interface类(CAknAppUi)的HandleForegroundEventL函数被调用。如果不考虑处理这个函数,应用程序可以处理相关的动作,如暂停程序。

应用程序需要注意内存的节约,这不仅包括在运行时内存的使用,还包括编译后代码的大小,总之,在处理RAM使用中最重要的一条原则就是,我们应该尽早的释放这些已分配的内存,每一步都要提醒自己这样做了吗。在目标机器上,OS上的核心会保持跟踪每个线程的内存,并且在程序退出后可以重新释放它们。这就保证了当一个应用程序退出后,所有的内存都会被释放掉。如果他们没有被正确的释放掉应有资源,那结果一定数量的资源会被保存在系统中。

在Symbian OS中,每个线程都有它自己的内存堆栈,在线程已经运行时,这个堆栈大小是不可以被改变的。在S60中缺省的堆栈大小仅有20KB,所以在使用时要格外的小心。这里要注意的是,在模拟器上运行时,是没有这个堆栈大小限制的,因为它是根据windows来运行的,这就是程序们每个步骤都应该在硬件(手机)上试试。大部分的堆栈溢出是由堆栈里描述符所引起的,这种情况可以通过在堆上分配描述符来避免。

注意递归的使用是很耗费内存的,如果递归程序是不可避免的,那我们每次传入的参数以及部分变量都应该最小化,以防止溢出。

为了减少编译后代码的大小,我们应该遵循如下的方针:)
1、除非必要,否则不要导出method
2、不要生成没有必要的虚函数(有附加的虚函数表,增加了体积)
3、不要过分的使用TRAP捕捉陷阱
4、避免复制代码
5、找到可以分解的函数
6、使用一般的控制和组件

对TRAP宏的使用要小心,并不是说避免过度使用他们是因为他们会增长代码大小。在symbian os中由应用程序框架已经提供了不少的TRAP,我们没有必要在到处放置我们自己的TRAP了:)

在游戏中,因为会大量使用位图,所以对内存的消耗特别大,最有效的方法不是减少位图的使用,而是降低他们的颜色深度(colour depth),Symbian支持24位位图,一共可以处理16777216种颜色,但实际的色彩大小是根据硬件来说的,我们应该尽量以目标硬件所能支持的色彩量为限度,举例来说,一个sprite,使用8bit colour就应该能满足大部分sprite的需要了,而mask更应该只使用1bit的位图!

在symbianOS中,最缺乏的几项服务之一就是timing services,这里OS并不支持底层的timer interrupts,它只提供一个核心端的时间发生器,其最大的发生频率是64Hz,而在模拟器上,最大的tick rate可以达到10Hz,这使得程序的测试更加烦琐。系统中的最大tick rate可以通过访问UserHal::TickPeriod来获得。其定义在e32hal.h中,可以参见下面的图表:


border=0>
(timerserivces.gif)

核心端的timer可以通过访问RTimer来获得,它是一个指向系统端服务的句柄。它提供了一个简单的API来发送三个不同的时间事件:在一段给定时间后的事件,在一个给定时间上的事件以及an event which completes at a given fraction of a second.为了更方便的使用RTimer,symbian OS提供了一个抽象的活动对象:CTimer,它封装了RTimer的使用,在上图中也能看出,CTimer中必定有RTimer的存在。

用户只需要从CTimer派生类即可,另外还需要重载RunL函数,它在一个请求被完成时进行调用。因为是非强占性的,所以在时间片上可能有延时,造成不准确,我们应该尽量减少RunL中代码运行的时间。此外我们还应该把处理时间片的活动对象的优先级提到最高,以防止其他对象切入。

在SymbianOS中,提供了两个CTimer的派生类:CPeriodic和CHeartbeat,CPeriodic中,事件的间隔在microseconds中,而CHeartbeat中,间隔在一秒的片段中,这定义在TTimerLockSpec枚举中。最小的时间片段是1/12秒,CHeartbeat提供了一个方法来与系统时间同步,当有timer event遗漏时,它的回调函数Synchronize就会被调用,从而给应用程序一个途径采取修正措施。

Symbian OS是一个事件驱动的系统,所有的应用程序和服务都可以被看作是事件处理器。如按键事件的处理,见下图:


border=0>
(keyeventflow.gif)

以上是当一个用户按下某键后的key event流程图。

当用户按下一个键后,keyboard hardware就会生成一个中断,由keyboard driver捕捉,之后分解出这次按键事件的key code,然后driver将它发送到系统端的一个线程——被称为window server,而window server又会把它发向在window group中拥有焦点的那个应用程序中,这个步骤是使用一个control environment(CONE)来完成的,它是window server和user interface library之间的一个API函数。

这时到了应用程序端了,这里key events被OfferKeyEventL函数(由window server所调用)所处理,每次按下键都会产生三个不同的事件,第一个事件是EEventKeyDown,它是当一个键被按下后发生的,接着是EEventKey,我们通常对这个事件最感兴趣,最后是当按键松开后的EEventKeyUp,这些事件的类型都在TEventCode枚举类型中被指明,它也是传递到OfferKeyEventL中的第二个参数,第一个是一个结构,TKeyEvent,它提供了更多的关于本次事件的信息。

如果一个键被按下后持续超过0.8秒,那window server就会发送另一个EEventKey到该程序,这就是长按键事件(long key press event);如按键的时间超过了那个限度,则认为这是一次重复按键事件,window server会在每隔0.25秒就发送key repeat events。这些时间的间隔定义都是s60的缺省值,可以在程序中改变的。

TKeyEvent有一个成员变量,iRepeats,它可以用来分辨是长按键事件还是重复按键时间。总的来说,如果要分辨最后那一次按键事件的类型,可以查看iRepeats,如果为0,那就是长按键事件,如果不是就是重复按键事件。这里iRepeats是一个32位的带符号整数。因为大部分的按键事件都会及时处理,所以这个变量并没有定义实际重复按键的次数,如果想要知道有多少键被重复按下,应用程序就要自己来计算重复按键的次数了。

游戏,对按键的处理更多,它可以设置自己的按键重复率,通过调用window server的SetKeyboardRepeatRate来调用key repeat time。它有着两个参数。

在s60系列中,大部分的按键缺省状态下都是阻塞(blocked)的,只有电源键和编辑键是非阻塞的。因为键操作对游戏来说是非常重要的,如用户可以同时按下两个键的操作情况。这就是s60为什么提供了API来取消键阻塞的缘故了,在应用程序的UI基类中,CAknAppUi,提供了SetKeyBlockMode方法来取消键阻塞。它使用了TAknKeyBlockMode这个枚举变量做为参数,它可以有两个可能的值EDefaultBlockMode和ENoKeyBlock,注意同样这里对键的操作也是系统端的设置,我们应该在游戏到后台时恢复到原来的缺省值。

在SymbianOS中,播放和管理声音是通过media server来处理的,media server支持各种各样的audio文件格式,如wav,au以及wve,并且提供一个API来拓展新的文件格式。Media server的客户端API是从三个不同的接口派生来的:audio sample editor,audio tone player以及audio sample player。这里audio sample editor接口提供了高级的audio管理方法,它可以用来录音、编辑以及播放。而audio tone player接口则允许应用程序生成和播放综合的声音。最后audio sample player接口可以用来重放sample data files。

media servcer接口的使用需要一个active scheduler,他们一起在同一个线程进行使用。

对于大多数游戏来说,audio sample player接口就可以满足一般的声音需求了。这个接口包括MMdaAudioPlayerCallback和CMdaAudioPlayerUtility类。这里MMdaAudioPlayerCallback是一个提供了回调的混合类,它用于在完成对sample的初始化或播放后来通知client class。而CMdaAudioPlayerUtility类则提供了方法来装入和播放一个sample,并可以调节音量。这个类只能和一个sample数据联系在一起,因此如果程序有多个不同的sample data files那就需要生成它的多个实例了。参见下面的代码:

// Create a sample player and load a sample from a file
CMdaAudioPlayerUtility* samplePlayer = CMdaAudioPlayerUtility::NewFilePlayerL(KSampleFileName, *this );
// Play the sample
samplePlayer->Play();

在s60中,每个应用程序都为各键准备了一个缺省声音,它可以依据不同的按键类型划分为短按键,长按键或重复按键的声音。s60应用程序的UI类,CAknAppUi,为应用程序指定他们自己的按键声音提供了支持,主要在一个资源文件中:
RESOURCE AVKON_SKEY_LIST r_example_skey_list

list =

AVKON_SKEY_INFO { key=EStdKeyLeftArrow; sid=EAvkonSIDNoSound;},
AVKON_SKEY_INFO { key=EStdKeyLeftArrow; sid=EAvkonSIDNoSound; type=ESKeyTypeLong;},
AVKON_SKEY_INFO { key=EStdKeyLeftArrow; sid=EAvkonSIDNoSound; type=ESKeyTypeRepeat;}
};

有效的sound ids,SIDs,在avkon.hrh头文件中指定。在游戏中,如果一个键有被长时间按下的情况,那repeat sound应该取消,我们可以指定按键事件的声音ID为EAvkonSIDNoSound,这是因为每次接到一个按键重复事件而播放一个重复声音时都会消耗一段处理时间。如果程序需要一个后继的声音,那应该使用sample player来替代它。

Installation
————-
下面给段pkg文件的例子:
; MyGame.pkg
; Specifies an installation file for MyGame
;Languages
&EN
;Header
#{"MyGame"},(0x1000ABCD),1,0,0
; Required line for Series 60 devices. (Added by NOKIA)
(0x101F6F88), 0, 0, 0, {"Series60ProductID"}
"epoc32releasethumburelMyGame.app"-
"!:systemappsMyGameMyGame.app"
"epoc32releasethumburelMyGame.rsc"-
"!:systemappsMyGameMyGame.rsc"

里面;标注的都是注释行,头一个非;开头的行指明所支持的语言类型。sis文件可以支持多种语言选择,只是一次只能有一个本安装。第二行表明了程序的名字和ID,还有版本号,以及一个build number。最后是s60 product Uid,它提供了该应用程序能在哪种s60平台上安装的信息。有多个s60 product Uid可以被使用,如下:
Nokia 7650
0x101F6F87
Nokia 3650
0x101F7962
Nokia N-Gage
0x101F8A64
SX1
0x101F9071
Series 60 Platform v0.9 0x101F6F88
Series 60 Platform v1.0 0x101F795F

最后几行说明了要打包成sis的源文件和目标文件,注意使用!可以让用户自由选择存储介质。

Graphics
———-
对游戏开发者来说最重要的恐怕莫过于系统对图形的支持了,下面就来讲述一下symbian系统中对图形的支持。

Graphics Architecture
———————-
symbian系统中对图形的支持定义在系统的图形设备接口中(GDI),GDI定义了原始绘图功能,提供了函数绘制文字,分形处理以及位图处理。系统中所有的图形组件都实际上都依靠GDI来处理,参见下图:


border=0>
(graphicscomponent.gif)

在symbianOS中,绘图是通过graphics contexts和graphics devices来处理的。GDI提供了一个抽象的graphics context类,CGraphicsContext,它是所有graohics context的基类。它定义了基本的绘图设置,如笔和画刷的样式,并封装了可在应用程序中使用的GDI图形函数。实际绘画时我们是通过graohics device来完成的,而它正使用了graphics context中的设定。这里也有个device类的基类:CGraphicsDevice,which specifies the attributes of a device the drawing is assigned to.如图:


(contextdevice.gif)

具体的context和device类都是在BITGDI中完成的(参见上上幅图),which is a screen and bitmap-specific graphics component. 它使用高度优化的汇编代码编写的,可以提供良好快速的图形绘制能力。The BITGDI implements rasterising and rendering of images and it supports drawing in on- and off-screen bitmaps.

Font and Bitmap Server
————————
Font和bitmap server(FBS)的主要任务是管理字体和位图。因此它们可以在系统中所有的线程之间共享。This allows for major memory savings as only one instance of particular data is maintained in memory.

当应用程序从用户数据区那装入字体或位图后,FBS就把它们放在一个共享堆上。在这个server端,管理着一个引用计数,用来统计各数据单元有多少客户在使用它们。当这个计数为0时,它就可以被安全的释放了。window server可以直接访问这个堆,这样就可以减少从FBS的内存中拷贝到window server的内存中这些不必要的步骤了。所有的基于ROM中的图形和字体都可以从ROM中直接使用。

我们可以通过RFbsSession类来访问FBS,它是由window server生成的。它是通过CFbsFont和CFbsBitmap类来使用的,它们提供了管理字体和位图的方法。当一个FBS拥有的数据项被一个客户释放后,RFbsSession会执行一个回调,它允许在数据被真正从共享堆中删除前,由window server执行一次pending redraw request:)

文章来源:http://symbian.org.cn/bbs/viewtopic.php?t=906

本文地址:http://com.8s8s.com/it/it31910.htm