把基于Series 60的智能电话作为一种游戏设备
本章将深入研究Series 60和Symbian OS,描述它们作为一个游戏平台的特性。此外,智能电话的需求和限制也将被讨论。
需求
与许多其它用于游戏的设备不同,智能电话需要在游戏中或者任何其他的应用程序运行的时候能够通知用户各种系统事件。 应用程序需要考虑到可能的中断,例如因为打进来的电话或者消息,并且它们需要根据情况进行处理。 应用程序还应该不消耗设备资源,例如过度的使用内存或者电量。
通知用户的大多数系统消息使用系统自己的对话框,称为全局通知。 这个对话框具有比任何应用程序都要高的窗口优先权,因此它们出现在应用程序的前端。 在系统事件中,一个异常是打进来的电话造成电话通信应用程序变成最前端的应用程序而把被中断的应用程序留作背景。 然而,所有的系统端事件具有一个公共特征,可以被一个应用程序捕捉。 当一个系统事件发生时,最前端的应用程序失去焦点。 这造成应用程序用户接口类(CAknAppUI)的HandleForegroundEventL方法被调用。 通过覆盖这个方法,应用程序可以执行需要的动作,例如暂停正在进行的游戏。
应用程序需要注意电池使用量。 当一个电话在预定的时间内没有使用的情况下,它会进入睡眠方式以便消耗最少的电量。 如果一个应用程序持续做后台处理,例如在一个循环中查询一个变量,电话可能不会进入休眠状态。 所有的查询应该在程序块循环中执行,并且所有的计时器应该在游戏暂停的时候停止。 一旦一个计时器需要来维护和另一个终端的连接,这个计时器的频率应该降到最小水平。 当在预先设定的时间间隔里一直没有用户活动的时候,应用程序还可以从一个系统端计时器中取得事件。 要做到这些可以使用e32std.h头文件中可以找到的RTimer::Inactivity方法。 在电池供电的设备中,软件需要对突然断电有所准备。 电池可能会没电,或者用户可能把它从设备中拿出来。 如果重要的用户数据被编辑的时候,就更应该注意这些情况了。 每隔一段时间这些数据就应该被保存,并且能够在重新启动之后恢复。 此外,应用程序应该对被损坏的数据有所准备,并且能够安全地从这种情况下恢复。
限制
除了有限的存储量之外,智能电话和PC相比有其他几个限制。 首先,智能电话没有像PC一样高效的处理器。 智能电话基本都没有数学处理器,因此时间单位计算应该使用整数实现。 Symbian OS作为一种游戏平台还有一些约束。 例如,这个平台不支持可写的静态数据,而静态数据经常被用于在游戏中来最优化访问广泛使用的数据。
智能电话还有相对限制的硬件。 显示屏幕有有限的分辨度、尺寸和彩色深度。 小键盘只有有限数目的按键,并且按键的布局可能不便于玩游戏。 在不同硬件解决方案之间,键盘的布局可能会有很大的不同,因此游戏应该提供用户重新定义按键的功能。 智能电话还没有足够的端口来支持各种游戏控制器,像方向盘和游戏杆,这些都是在PC环境中很常见的。 所有这些限制决定了哪些游戏可以被实现并且安装到一部智能电话上,而不会降低它们的可玩性。 然而从长远的观点来看,智能电话中使用的技术将越来越精巧,新的特性和解决方案将被引入。
内存
在内存有限制的设备中,内存管理处于一种非常重要的地位。 这关系到运行时间内存使用和最后的编译代码长度。 大部分基于Symbian OS和Series 60的设备只有8 MB内存甚至更少。 除了内存以外,这种设备只有用于预先安装的软件和用户数据区域的只读存储器,只读存储器用于安装应用程序和系统的可写入数据和持久数据文件。 此外,便携式存储卡,例如小型闪存( CF)卡或者多媒体卡( MMC),可能依靠硬件支持。
内存使用最重要的规则是所有分配的存储器都应该在在尽可能的早期释放。 Symbian OS仿真程序提供一个宏用于内存检验,默认情况下所有应用程序都有图形用户界面。 如果这个宏不能释放内存,这个宏将使应用程序变混乱,这样就会出现内存泄漏。 在一个目标硬件中,OS的核心记录每个线程的内存并且在线程退出的时候自动的释放它。 这保证所有的内存都在应用程序退出的时候被释放。 运行时间一长,应用程序和服务器可能出现一个问题。 它们在结束使用它们的时候,如果它们不释放已经不需要的资源,那么就会有很多资源被系统保留起来而不能被应用程序所使用。
当实现一个应用程序时,堆栈存储器的使用值得注意。 在Symbian OS中,每个线程都有自己的存储堆栈,不能在线程已经启动以后增大。 Series 60中的应用程序的默认堆栈尺寸只有20kB,所以它应该谨慎使用。 仿真程序环境和可用的堆栈空间中的目标硬件之间还有分歧。 仿真程序中的堆栈尺寸不像在硬件中那样受限制,因为取而代之的是使用Windows自己的堆栈。 这就是为什么所有的软件都应该在开发早期在硬件上测试,并且使用它们最大尺寸的堆栈变量。 大多数的堆栈都会因为使用堆栈描述符而溢出。 这可以通过从堆栈分配描述符并且通过使用自动对象来避免。 还有,递归的使用可能是一个非常消耗堆栈的事情。 如果真的无法避免递归程序设计,那么传递的参数和递归内部的局部自动变量也应该尽量最小化。
为了最小化编译代码的大小,应该遵循下面的方针:
1. 除非有必要,否则不要导出方法。
2. 不创建不必要的虚拟方法。
3. 不过度使用TRAP宏。
4. 避免重复代码。
5. 找到可分解的函数
6. 使用公共的控件和组件
为了能够从一个动态链接库外访问函数或者数据,被导出的方法被列在一个动态链接库导出表中。 虽然在Symbian OS中,这些方法是以序号而不是以名字来导出的,所有的不必要导出的方法都会导致导出表的大小增大。 这是为什么方法应该只在它们有目的的用于它们被引入的库之外的时候,它们才被导出。 这同样应用到记录在动态链接库中的虚拟函数表中的虚拟方法。
TRAP宏的使用应该小心地设计。 这不意味着它们会过度使用,因为它们的负面影响是增大编译的代码的尺寸。 Symbian OS的应用框架提供的TRAP经常对于应用开发者来说已经足够了,他们不需要编码他们自己的TRAP。
在列表中的最后三个条目是最常用的方法,以最小化所有平台上的代码尺寸,并且不需要被详细的讨论。
由于游戏的图形特性,位图经常占用大量的内存使用量。 这应用于随机存取存储器和用户数据区域使用量。 有助于减少使用量,而不减少位图的数目的最有效的方法是减少它们的颜色数。 Symbian OS支持16777216种颜色的24位位图,但是实际的最大颜色数受目标硬件限制。 这就是为什么所有的位图都不应该转化为比硬件设置最大颜色数更高。 小型的低细节的不需要许多颜色的位图,应该参考上的规则转化为比最大颜色数要低的颜色数。 例如8位颜色适合于大多数的子图形。 而所有的mask都应该被转化为1位的位图。
计时器
对于大多数游戏来说,计时是必不可少的。 系统端提供的计时服务用于各种目的。 在更复杂的游戏中,游戏的场景经常更新,并且用户的输入在一秒中被读很多次。 在更简单的游戏中,计时被用于处理游戏玩家的回合或者评估一个游戏玩家是否成功的解决一个给定的问题等等。 大多数的游戏都需要来自系统的计时支持。
在Symbian OS中,最不足的服务之一就是计时服务。 OS不支持低级计时中断,它只提供一个有最大频率为64 Hz的核心端计时器。 相同的时钟频率还被用于线程的循环调度。 仿真程序环境中,最大时钟频率是10 Hz,这会导致游戏的测试出错。 系统的最大时钟频率可以使用UserHal::TickPeriod方法访问,是用一种平台无关性的方法来给出时钟周期。 这个方法在e32hal.h头文件中被引用。 图3.1是Symbian OS计时器类的类图。
图3.1 Symbian OS计时服务的类图
核心端计时器可以使用RTimer类取得,这是一个到系统端服务器的操作。 它提供一个简单的应用编程接口来请求三个不同的计时事件:一段给定时间之后的事件,给定时间的事件和一个完成一秒钟的给定分段的事件。 这个应用程序编程接口需要TRequestStatus作为一个参数被传送进去,交付给应用程序开发者来使用活动对象作为事件处理程序。 为了简化RTimer的使用,Symbian OS提供一个抽象活动对象CTimer,封装了RTimer的使用。 这可以使用一个简单的封装完成,应用程序开发者需要从CTimer中派生出这个类,然后覆盖当一个请求完成时被调用的RunL方法。 然而,由于在计时服务中使用了活动的对象,真实计时事件处理可能延迟。 当一个计时器请求完成的时候,另一个活动的对象可能已经被执行,并且正在处理这个记时事件的活动的对象直到另一个活动的对象结束它的RunL的时候它才会被列入计划。 这虽然不可避免,但是对于计时精度的影响可以通过让所有的RunL方法尽可能短时间运行来达到最小化。 如果另一个带有更高优先权的活动对象被列在前面的话,这个事件处理可能还是会被延迟。 这可以通过使处理计时事件的活动对象比其它活动对象具有更高的优先权来避免。
Symbian OS还提供两个CTimer导出类来重复的进行计时事件:CPeriodic和CHeartbeat。这两个类都在一个事件发生时调用一个回调方法。 对于CPeriodic,事件的间隔可以以微秒给出,而对于CHeartbeat来说,事件的间隔仅仅以秒划分,这两个类都是被TTimerLockSpec枚举定义的。 最小的分数是十二分之一。 在CPeriodic中,给定的间隔近似到最接近的系统计时分辨率。 CHeartbeat提供了一个方法来使计时器与系统计时器同步。 如果错过了一个或多个记时事件,那么它的回调方法Synchronize将被调用,并且使用这种方法,它可以提供一个应用程序执行必要的恢复动作的可能性。 上面提到的计时类可以从e32std.h和e32base.h头文件中找到。
按键事件处理
Symbian OS是一个事件驱动系统--所有的应用程序和服务器都可以被看作为事件处理程序。 象按键事件这样的事件都是被活动对象操作的,使事件处理非抢先式计划。 一个用户按下一个键的事件流程的示例如图3.2所示。
图3.2按键事件流程
当一个用户按下一个按键时,键盘硬件生成一个中断,被键盘驱动程序捕获。 在分解按键事件之后,驱动程序把它发送到一个称为窗口服务器的系统端线程中。 窗口服务器发送事件到窗口群具有焦点的应用程序中。 这是使用一个控制环境( CONE)来实现的,这是一个介于窗口服务器和用户界面库之间的应用编程接口。 CONE和窗口服务器在第四章中做出了解释。
在应用程序端中,按键事件是在窗口服务器调用的OfferKeyEventL方法处理的。 每按下一个键就会生成三个单独的事件。 第一个事件是EEventKeyDown,这是当一个键被按下的时候生成的。 然后是EEventKey,当键被松开的时候生成了EEventKeyUp事件。 这些事件类型是被TEventCode枚举定义的,被传送到OfferKeyEventL作为第二个参数。 第一个参数是一个结构(struct)TKeyEvent,指定关于事件的更详细的信息。 如果一个键按下的时间长于0.8秒,窗口服务器发送另一个EEventKey事件到应用程序,长时间按键事件。 如果这个键按下的时间比这个还长,那么窗口服务器每隔0.25秒钟发送一次按键重复事件。 这些时间帧对于Series 60来说是缺省值,它们可以被应用程序修改。
TKeyEvent有一个成员变量iRepeats,用于从按键重复事件中分离出长时间按键事件。 一旦这个变量不为零,应用程序必须知道当最后一次按键事件被接收的时候这个值为多少。 如果最后的事件的iRepeats等于零,那么一个长时间按键事件就被接收,而如果不为零的话,就接收一个按键重复事件。 iRepeats变量是一个32位带符号整数,定义了自从最有一次处理的事件之后定义的事件数。 因为大部分的案件事件在某个地方被处理,所以这个变量不定义自从第一个按键事件之后的实际重复数。 如果它们想知道键到底被按下去多长时间,这就是为什么应用程序需要它们自己计算重复的次数。 TKeyEvent和TEventCode的定义可以在w32std.h头文件中找到。
经常需要按键事件的游戏应该设置它们自己的按键重复率。 按键重复期间可以使用窗口服务器的带有两个参数的SetKeyboardRepeatRate应用编程接口来更改。 第一个参数在第一次按键重复事件之前指定时间,这相当于一个长时间按键事件,而第二个参数指定后继按键重复事件之间的时间。 设置时间框相等产生了一个线性的重复速度,也就是说第一次按键事件和后续按键事件之间的时间框相等。 因为重复速度是全系统范围的设置,所以它们应该在另一个应用程序进入前台的时候被改回默认值。
默认情况下,在Series 60中大部分的按键被阻塞;仅有电源键和编辑键是非阻塞按键。 无论如何,按键覆盖对于游戏来说是非常重要的,因为游戏中往往需要用户能同时按下两个键。 这就是为什么Series 60提供能用于禁止按键阻塞的应用编程接口。 应用程序用户界面的基本类CAknAppUi提供SetKeyBlockMode方法,可用于禁止按键阻塞。 这个应用编程接口接受一个TAknKeyBlockMode枚举作为一个参数,这个枚举可以有两个可能的值:EDefaultBlockMode和ENoKeyBlock。 按键覆盖也是一个全系统的设置,应该在游戏不在前台的时候被恢复到缺省值。
声音
在Symbian OS中,播放和操作声音是由媒体服务器处理的。 媒体服务器支持各种声音文件格式,比如 wa、au和wve,并且提供一个应用编程接口以便应用程序能够开发补充的文件格式插入式模块。 媒体服务器的客户端应用编程接口被分为三个不同的接口:音频示例编辑器,音频音调播放器和音频示例播放器。 音频示例编辑器接口提供了高级音频操作方法,可以用来录音、编辑和播放声音。 音频音调播放程序接口启动应用程序来创建和播放合成的声音。音频示例播放程序接口可用于播放样本数据文件。 媒体服务器接口的使用需要在同一线程中运行一个活动的调度程序。
对于大多数游戏来说,音频示例播放器接口提供了所有需要的特性来实现要求的音响效果。这个接口由MMdaAudioPlayerCallback和CMdaAudioPlayerUtility类组成。 MMdaAudioPlayerCallback是一个mixin类,提供回调方法来通知客户端类样本的初始化或者播放已经完成。 这就是为什么这个使用样本播放程序接口的类需要从mixin类中继承而来。 CMdaAudioPlayerUtility类提供了加载和播放样本的方法,并且可以设置播放的音量。 这个类可以仅仅和单一样本数据关联,这样一个应用程序有多少个不同的样本数据文件,它就需要创建多少CMdaAudioPlayerUtility的实例。 下面的代码是使用的CMdaAudioPlayerUtility类的示例。
∥创建一个帮本播放程序并且从一个文件装入样本
CMdaAudioPlayerUtility* samplePlayer =
CMdaAudioPlayerUtility::NewFilePlayerL(
KSampleFileName, *this );
∥播放样本
samplePlayer->Play();
在Series 60中,每个应用程序对于每个键都有一个默认的声音。 这个声音还可以取决于这个按键事件是否是短时间、长时间或者重复按键。 Series 60应用程序用户界面类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;}
};
}
可用的声音标示符SIDs在avkon.hrh头文件中指定。 在游戏中,如果一个键被长时间按下,重复声音应该通过指定这个按键事件的声音标识符为EAvkonSIDNoSound来禁止。 这是因为每当一个按键重复事件接收的时候播放重复声音会消费大量处理时间。 如果一个游戏需要连续的声音,应该使用音频样本播放器来代替。
安装
在Symbian OS中,应用程序的安装是使用安装文件sis文件来完成的。 Sis文件包含要被安装的文件以及安装时需要的信息。 sis文件中的数据被压缩以节省存储器,并且最小化sis文件传送到终端的时间。 应用程序的安装可以直接从一台安装了Series 60 PC Suite的个人计算机上运行相应的sis文件来完成。 Sis文件还可以通过首先使用各种通信技术例如WAP、蓝牙和Infrared Data Association(红外线数据协会)来下载文件,然后在一个通信应用程序中打开它。
Sis文件使用程序包文件pkg文件构造,让必要的信息汇编为一个sis文件:
; 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"} "\epoc32\release\thumb\urel\MyGame.app"- "!:\system\apps\MyGame\MyGame.app" "\epoc32\release\thumb\urel\MyGame.rsc"- "!:\system\apps\MyGame\MyGame.rsc" |
上面的这几行里,凡是前面带有分号的,都是注解行。 第一个非注解行指定支持的语言变体。 一个sis文件可能包含多于一个的语言变体,虽然每次只能安装一个变体。 第二行专留作一个程序包头,指定应用程序的名称和标识符,主要的和次要的版本号以及构造号。 在此之后,是Series 60 Product Uid。 这个指出这个应用程序可以安装在哪个Series 60平台版本和设备。 可以使用多个Series 60 Product Uid。 下面是大部分公共Series 60 Product Uid:
Nokia 7650 |
0x101F6F87 |
Nokia 3650 |
0x101F7962 |
Nokia N-Gage? Mobile Game Deck |
0x101F8A64 |
SX1 |
0x101F9071 |
Series 60 Platform v0.9 |
0x101F6F88 |
Series 60 Platform v1.0 |
0x101F795F |
下面的几行定义将要被安装的文件。 每行指定PC中的源路径,以及在终端上的目标路径。如果目标驱动器字母被指定为一个感叹号,用户可以在安装时选择驱动器。 程序包文件格式还支持一些可以被使用的可选参数,例如指定和语言有关的文件。 sis文件是使用一个称为makesis的命令行工具汇编的,接收一个相应的pkg文件作为一个参数。