首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

3.2  缺陷类型

下面让我们来看看软件本身吧。软件有很多的地方会产生问题。如果根据缺陷被引入、被发现,甚至在未来如何被避免来对缺陷进行分类是非常有用的。由IBM公司开发的正交分类(ODC)系统就是为此目的而开发的。按照开发活动,该系统定义了多种分类方法。本章将讨论八种类型,检查它们与游戏的相关性。类型按被引入代码的方式进行分类。随着我们的讲解,请记住每种要么是执行结果不正确,要么就是缺少代码。下面列举出来的类型总括造成游戏的代码的不同要素:

●       功能(Function)

●       赋值(Assignment)

●       检查(Checking)

●       时间控制(Timing)

●       构造/包装/合并(Build/Package/Merge)

●       算法(Algorithm)

●       文档(Documentation)

●       接口(Interface)

注 意

如果你记忆这个清单有困难,请试着记住其首字母缩写“FACT BADI”。

这里举一个例子,该实例摘自游戏《卡米罗特的黑暗时代》(Dark Age of Camelot)1.70i 的声明,该声明发布于2004年7月1日。《卡米罗特的黑暗时代》是一种大型多人在线角色扮演游戏,该游戏通过不断修正,以便于继续扩充和增强玩家的游戏体验。因此,它不断地发布补丁以达到修复bug、增加或修正游戏性能的双重目的。当补丁被开发时,可以趁机对马上要发布修复的游戏进行再一次的检查。

缺陷描述本身并不能说明缺陷是怎样被引入到代码中来的,而缺陷的类型可以描述。由于不能通过加入到开发团队的缺陷追踪系统来得知这种bug是怎样发生的,所以让我们以一个具体的bug为例,看看它是怎样由某种缺陷类型的错误所导致。

下面是游戏《卡米罗特的黑暗时代》补丁中的一个缺陷示例,本章的剩余部分都会用到它:

“当使用时,隐藏(Vanish)功能可以报告你有多少秒的超隐形能力。”

如果这就是它正常使用时的情形,那么bug被记录的就是相反的情形了,其描述类似于:

“当使用时,隐藏功能不能报告你有多少秒的超隐形能力。”

关于这种隐藏功能,请参见下面框中的具体描述。

隐藏功能

描述:

给玩家提供超级隐形能力,且不会被破坏。会损失玩家的一些点数和生命数,但能对隐形的人物进行控制。这种能力能持续1到5秒钟,具体时间取决于“隐藏”的关卡。隐形者也能如下面所示的那样提升速度。运用了这一能力之后,30秒钟内隐形者不能发起攻击。

效果:

一级 — 正常速度,1秒钟的免疫力

二级 — 速度1,2秒钟的免疫力

三级 — 速度5,5秒钟的免疫力

类型:活动

重玩:10分钟

一级:5

二级:10

三级:15

以上内容源自于Allakhazam网站,网址为 http://camelot.allakhazam.com/ability.html?cabil=73。

3.2.1  功能

功能错误是一种影响游戏性能及用户体验的错误。该错误可能是由提供这一功能的代码丢失或不正确造成的。

下面是一个代码片段,展示了用来建立和初始化Vanish功能的代码。玩家的隐藏能力级别通过一个特殊的处理方式转变为隐藏功能。这个方式需要执行所有能激活这种功能的函数调用。g_vanishSpeed 和g_vanishTime数组用来为这种能力的三种级别储存数值,其中0数值对应0级别。这样的数组以前缀“g_”来命名,以指出它们是全局的,因为它适合用于有这种能力的所有角色。这些值都以大写字母的形式出现,表明这些都是常数。

缺少对显示作用时间的例程的调用是此类代码常见的功能缺陷。也许这种代码缺陷是从别的能力那里复制来的,加上了 “vanish”,但没有添加显示时间的代码。或者,对于这种能力的提供可能没有表达清楚,那么,程序员就不知道是否应该显示计时器了。

void HandleVanish(level)

{

if (level == 0)

return; // player does not have this ability so leave

PurgeEffects(damageOverTime);

IncreaseSpeed(g_vanishSpeed[level]);

SetAttack(SUSPEND, 30SECONDS);

StartTimer(g_vanishTime[level]);

return;

} // oops! Did not report seconds remaining to user–hope they don’t notice

或者,已经加入了显示时间的代码,但是调用的是一个或更多的错误值:

ShowDuration(FALSE, g_vanishTime[level]);

3.2.2  赋值

当程序所使用的值被错误地初始化或设置,或当一个所需的参数值丢失时,出现的错误就被定义为赋值类型。许多这种任务发生在游戏开始、进入一个新关卡或一种游戏模式时。下面是各种游戏中存在赋值的示例:

体育游戏

团队日程表

为每次比赛初定分数

团队的先发阵容

球场、足球场、冰球场等玩游戏的地方

天气状况和时间

角色扮演游戏(RPG),探险游戏

地图上的出发点

开始时的状态、技能、装备和能力

初始化当前地图的数据

初始化日志

赛车游戏

初始化轨道/赛道数据

赛车开始时的燃料或能量的初始值

道具和障碍的定位

天气状况和时间

赌城游戏、集卡游戏、象棋游戏

开始时的初始点数或金钱数

初始的卡或放置的牌

联赛中的初始关卡/ 种子选手

比赛桌上的位置和发牌顺序

拳击游戏

健康、能量的初始值

拳击场或竞技场中的初始位置

联赛中的初始关卡/种子选手

拳击场、竞技场等拳击发生的地方

策略游戏

单位初始值

资源初始值

开始的位置单位和可用的资源

当前的目标

第一人称射击 (FPS)游戏

健康、能量的初始值

装备和弹药的初始值

玩家的初始位置

电脑对手的数量和力量

填字游戏

填字游戏开始时的配置

分配的时间和完成填字游戏的标准

填字的数量或目标值

填字游戏进行的速度

从上面列出的这些清单可以看出,任何变化都可能导致对玩家或电脑有利。因此游戏程序员将很多注意力集中在平衡所有的游戏要素上。初始值分配对提供游戏的公平性很重要。

甚至“隐藏”缺陷都可能是赋值问题的结果。例如,“隐藏”功能可以通过一个函数被其他功能调用而被激活。

ABILITY_STRUCT         realmAbility;

realmAbility.ability = VANISH_ABILITY;

reamAbility.purge = DAMAGE_OVER_TIME_PURGE;

realmAbility.level = g_currentCharacterLevel[VANISH_ABILITY];

reamAbility.speed = g_vanishSpeed[realmAbility.level]

realmAbility.attackDelay = 30SECONDS;

realmAbility.duration = g_vanishTime[realmAbility.level];

realmAbility.displayDuration = FALSE; // wrong flag value

HandleAbility(realmAbility);

或者,displayDuration标志的赋值可能会缺少。

同样,剪切和粘贴也可能会导致引入有缺陷的代码,在部分程序员看来,这类有缺陷的代码可能是本身就是错的,或是被当成错误而被忽略,或是对需求的误解导致的。

3.2.3  检查

当代码在被使用前不能适当地验证数据时,就产生了检查类型的缺陷。这可能是未能对某一情况进行检查或检查未能被适当地定义引起的。

在C代码中一些不适当的检查的例子如下:

●       用“=”代替“==”对两种值的比较。

●       括号未能匹配产生了对操作数优先级不明的错误。

●       边界比较,例如使用“<=”代替“<”。

●       用数值(*pointer)与NULL相比较而不是用地址(pointer)来比较——要么直接来自一个存储变量,要么是来自函数调用的一个返回值。

●       忽略或未检查诸如strcpy之类的C库函数的返回值。下面显示了一个检查缺陷的脚本,此脚本中没有正确检查显示隐藏持续时间的标志而是检查了错误的标志。

HandleAbility (ABILITY_STRUCT ability)

{

PurgeEffect(ability.purge);

if (ability.attackDelay > 0)

StartAttackDelayTimer(ability.attackDelay);

if (ability.immunityDuration == TRUE)

// should be checking ability.displayImmunityDuration!

DisplayAbilityDuration(ability.immunityDuration);

}

3.2.4  时间控制

时间控制缺陷与资源的共享、资源的实时管理相关。有些进程,例如在硬盘上储存游戏信息,要给出开始时间或结束时间。这类操作在数据上执行,应完成对数据的操作后才能终止。一个用户友好的界面应显示一个进度条,例如显示一个带有进度条的动画式场景。进度条表明玩家的信息正在被保存。一旦完成了保存操作,游戏就重新开始。

其他需要时间控制的操作包括预加载声音和图形,以便于玩游戏时画面流畅,减少停滞。许多这样的功能现在在游戏硬件中得到处理,但是软件仍然可能需要为等候的用户给出某种提示,例如“正在处理”,“正在加载进程”,“正在复制数据”等。

注 意

作为用声音通报系统事件的一个例子,微软的DirectMusic提供了一个AddNotificationType例程,程序员能用它来通知游戏,何时音乐被播放、停止、从队列中删除、循环或结束。SetNotificationHandle被用来分配一个事件处理器(由CreateEvent函数创建),当游戏通过通知处理程序调用WaitForSingleObject时,就要使用事件处理器,然后呼叫GetNotificationPMsg来通报事件。

有时用户的输入也要求考虑特别的时间控制。比如双击或重复按一个键会在游戏中产生特别的行为。在游戏平台操作系统中可能有机制来处理它,或者游戏团队会将相应模块嵌入代码。

在MMORPG(大型多人在线角色扮演游戏)和多玩家的手机游戏中,信息在玩家间和游戏服务器间来回传递。这些信息必须以适当的顺序被处理,否则游戏行为就会产生错误。有时,当游戏正在等待更新过的游戏信息时,游戏软件尽力预测和填写正在进行什么工作。这会导致“抖动”或“位移”。比如你的人物正在四处奔跑,跑了一段距离以后,突然,你看到你的人物在它所在位置的靠后一些的地方遭受袭击。

回到大家熟悉的“隐藏”bug,让我们看看时间控制缺陷的脚本。在这个案例中,假装启动动画,以计算“隐藏”能力。当动画结束后,一个全局变量g_animationDone就被设定了。

一旦g_animationDone为TRUE,应该隐藏持续时间。如果ShowDuration函数被调用,而未等待指示Vanish动画就已完成,那么一个时间控制的隐藏就出现了。这个动画将复写任何在屏幕上显示的东西。代码的缺陷部分可能看起来如下所示:

StartAnimation(VANISH_ABILITY);

ShowDuration(TRUE, g_vanishImmunityTime[level]);

而正确的代码应是:

StartAnimation(VANISH_ABILITY);

while(g_animationDone == FALSE)

; // wait for TRUE

ShowDuration(TRUE, g_vanishImmunityTime[level]);

3.2.5  构造/包装/合并

构造/包装/合并,或简单地说,“构造缺陷”是由于配置游戏源代码库系统,变更游戏文件的管理,或版本识别和控制引起的错误。

构造就是编辑和链接源代码和图形、文本和声音文件等的游戏素材的行为,以创造一个可执行的游戏。配置管理软件常被用来帮助管理和控制游戏文件的使用。其中每个文件可能包含不止一个素材或代码模块。且每个文件实例都有唯一的版本标识符以进行区别。

配置说明中详细说明了每个要构造的版本。若详细说明要构造的每个版本要花很多时间,就有可能出错。因此,配置管理系统提供了标识每个版本的功能。在配置说明中,一组具体的文件版本能够通过配置文件中的一个标签得以识别。

表3-1给出了标签的一些典型用法。你的团队也可以不采用下面的标签名称,但实质都是相同的。

表3-1  典型的标签及其用法

标    签

用    法

[DevBuild]

标识程序员使用的文件,该文件包含程序员的一些新想法或bug修复计划

[PcOnly]

针对多平台开发的游戏可能要同一个文件加入不同的版本信息,此标签标识每个版本只适用于唯一的一个平台

[TestRelease]

标识测试人员使用的文件。通过这些文件暗示版本的变化,有些变化会生效。如果测试成功,下一步就将它改为“官方”版本号的标签

[Release1.1]

成功地构造和测试后,一个版本标签会用来“标识”使用了哪些文件。如果出现问题或是由于调试新问题要恢复到以前的版本时这是非常有用的

每个文件都有一系列主要的修订版本,它们构成了主线。从主线上的文件衍生的任何新版本都被称为支线。支线上的文件也可能有其他的新支线。在一个或多个支线上的文件,可以与其同层次的文件合并从而生成变更文件的合集。合并可以手工地、自动地或在配置管理系统的帮助下进行,例如突出显示不同版本的哪些具体代码合并在一起。一个版本树形图提供了一个文件的所有版本及其相互关系的视图。参看图3-1到图3-3,可以看到,通过采用不同的方式增添和更新文件,版本树形图是怎样发生演变的。

图3-1  一个简单版本树形图的主线

图3-2  带有一个支线的版本树形图

图3-3  合并

当一个程序员想运用一个配置管理系统来改变一个文件,该文件就得被检验。然后,一旦程序员对变化满意,想将新文件作为原版本的一个新版本,就需要对文件进行登记。如果程序员改变了主意,那么已通过检验的文件会被取消,这样就不对该文件的原版做任何改变。

在配置说明中,一个错误的版本或标签仍然会导致一个可执行游戏的产生,但是它不会像原来计划的那样生效。它可以是一个文件错误,该错误只能当一个特殊脚本中的一种类型运用它才会被发现。这类错误就使得测试员有事可做了。

也有一种可能是配置说明正确,但程序员不能对需要被构造的版本进行恰当地标识。此时,标签可能被遗漏了,或是字被打错了,所以它不能跟配置说明中的标签完全匹配。

另一个问题是有关于合并的。如果代码的一部分在每个版本中都有变化,那么得采用一定的技巧才能使得文件的合并能保留每个版本的变化。当文件的一个版本删除了与其合并的版本中更新的代码时,合并的复杂性就增加了。如果是手工合并,而不是计算机来处理,这些问题可能更容易被发现。

有时从代码可以看出构造中的错误。比如,出现类似于// TAKE THIS OUT BEFORE SHIPPING!的注释可能是一种提示,表明构造过程开始前,程序员忘了改动标签或检查文件的一个新版本。

参看图3-3,下面对“隐藏”模块代码做一下假设:

(1) 版本1和版本2不能显示“隐藏”的持续时间。

(2) 版本1.1引入了显示持续时间的代码。

(3) 将版本2和版本1.1合并产生版本3,但是删除了版本1.1 中显示持续时间的代码部分。

对于“隐藏”的显示bug,下面是一些属于构造类型的缺陷示例:

●       产生版本3的合并删除了版本1.1中显示持续时间的代码部分。版本3得以构建,但我们却不能显示持续时间。

●       版本1.1和版本2得到了恰当的合并,所以版本3中的代码会显示持续时间。但是,构造说明中所使用的标签没有被从版本2移到版本3中,所以版本2得以构造,而我们却不能显示持续时间。

●       版本1.1和版本2被恰当地合并,所以版本3中的代码会显示持续时间。构造标签也从版本2移到了版本3中。不过,构造说明对构造这个文件的版本2很难编码,所以我们不能显示持续时间。

3.2.6  算法

算法缺陷包括一些计算过程或选择结构中出现的有关时间复杂度或正确性的问题。算法可以视为得出一个数值(例如,42)或实现一个结果(例如,打开门)的过程。

每个游戏中都有很多算法,如果它们运转正常,你甚至都不会觉察到。不合适的算法常常会导致人们从游戏中获得意外的权利。下面是一些你在游戏中会遇到的不同的类型的算法:

体育游戏

人机对战的玩法、形式和替换选择

电脑的应对策略

给一个现实中的对手定义玩家和策略的模型

游戏双方每个队员的人工智能行为控制

随着人物移动到球场的不同的地方,改变摄影机角度

确定惩罚和裁判规则

划分运动员的伤势

赛季期间运动员的状态发展

授予特殊的力量、奖励或比赛模式(NBA街头篮球l. 2和NCAA足球游戏2005)

角色扮演游戏(RPG),探险游戏

对手和友好人物的对话反应

对手和友好人物的战斗决定和行动

基于技能、盔甲、武器类型和力量等的损失计算

保存投掷的计算值

确定使用一种技能,例如隐形、工艺、劝说等的结果

经验点的计算和奖励措施

能力值、持续时间和效果

使用或具备的能力及物品所需的资源与条件

武器和能力目标、效果域和随着时间而累计的损失

赛车游戏

PC驾驶员的特征、决定和行为——何时进站加油,何时使用道具等

汽车的损害和耐久性计算以及使汽车受损的行为

修复汽车的损害

自动换挡

模拟诸如轨道表面、储量和天气之类的环境影响因素

PC驾驶员的嘲弄

赌城游戏、集卡游戏、象棋游戏

对手的风格和技能关卡

游戏规则应用

其他规则,例如何时庄家必须留住黑桃A

选择赌注和支出/酬金

结果的等可能性分布,例如产生随机性强的结果(牌、掷骰子、转轮盘等)

拳击游戏

PC对手的出拳(攻击性的)和格拳(防卫性的)选择

PC团队选择和格斗期间的上场和下场

损伤/点数计算,包括环境效果

计算和修正格斗对环境的影响

计算和分解疲乏的因素

支持特殊的移动、连击等

策略游戏

PC对手的运动和格斗决定

PC单位的创建和部署决定

资源和单位构造规则(前提、需要的资源等)

损耗和效益计数

支持新单位、武器、技术和装置等的使用

第一人称射击 (FPS)

PC对手和队友AI

敌手和友好人物进行战斗的决定和行动

基于技能、盔甲、武器类型和力量等的损失计算

武器目标、效果域和随时间累计的损失

环境对速度、运动员的损伤、武器的歪斜或反弹(例如,《虚幻锦标赛》中的车轮将对撞墙作出防范)等影响

填字游戏

点数、奖励和计数

完成一个回合或进入下一关卡的标准

确定填字游戏目标的成功,例如形成一个特殊的字,或匹配一定数量的块

提供特殊的道具、奖励或游戏模式

为了使游戏更复杂,有些游戏属于多个游戏“类型”及算法。例如,《星际大战:旧共和国武士》(TOTOR)是一种角色扮演 (RPG)/探险游戏,在这种游戏的故事情节中有些地方,你可以跟非队员玩纸牌和赛车游戏——尽管两者不能同一时间进行。《虚幻锦标赛2004》(Unreal Tournament 2004)被典型地认为是一种第一人称射手游戏,但是在竞标赛的不同阶段,它也整合了冒险游戏和体育运动游戏的要素。

在其他一些地方,算法类型的缺陷能出现在同一游戏代码中,如一些图形,体现着引擎和路线、z缓冲排序、冲突探测和将进程减少到最少以刷新屏幕。

对于隐藏功能可能出现的这类bug,可以考虑一个查找算法缺陷的脚本,用这个脚本计算时间的持续值,而不是将其从一列或一个文件中取出。也可以假设存在持续时间不会显示在屏幕上。如果因为总是产生0或负的数字结果而致使计算(算法)失败,或计算不能继续了,那么持续时间就显示不出来了。

由隐藏导致的免疫性持续时间在第一级时是1秒,第二级时是2秒,第三级时是5秒。这种关系能通过下面的公式得到说明

vanishDuration = (1 << level) – level;

因此在第一级,就变成了2 – 1 = 1;对于第二级,则4 – 2 = 2;第三级,则8 – 3 = 5。参考说明,这正是我们需要的结果。

但如果使用了取余(%)而不是按位左移(<<)算法,那会发生什么事呢?这可能会得出一个这样的结果,对第一级而言, 0 – 1 = –1,对第二级而言,1 – 2 = –1,而对第三级而言,则1 – 3 = –2。尽管代码能适当地向用户显示持续时间,但负的持续时间不会得到显示。一个算法缺陷由此产生。

3.2.7  文档

文档缺陷发生在游戏的已确定下来的数据素材中,包括文本、音频和图形文件内容,如下所列:

●       文本

●       对话框

●       用户界面要素(标签、警告、提示符等)

●       帮助文本

●       说明

●       搜索日志

●       音频

●       声音效果

●       背景音乐

●       对话(人类、外星人、动物)

●       音响效果(流水、鸟鸣等)

●       欢呼声

●       视频

●       剧情介绍

●       分镜头

●       环境目标

●       游戏关卡的定义

●       身体部位和服装选择

●       物品(武器,交通工具等)

这种特殊的类型不是不适宜的代码导致的结果。错误存在于从文件中重新得到的字节型数据中或定义的常量中。这些数据常被那些屏幕上显示文字、播放音频、或将数据写在文档上的语句或函数调用。通过阅读文本、听音频、检查文件和仔细查看图形,这类缺陷是可以被检测到的。

源代码中的字符串常量也是文档类型错误的潜在来源,这些字符串常量常常需要显示出来或写入文件。当游戏有多语言选择时,将字符串常量直接插入代码中会产生一个缺陷。即使它可能会在一种语言中显示恰当的字符串,但如果用户选择了另一种语言,有可能会没办法显示相应的语言。

下面是《卡米洛的黑暗时代1.70i版》里的一些bug修正:

●       如果你遭遇DoT,然后死亡,你将看到“一个现在死了的敌人攻击你,使你受到X级损害”,而不是看到垃圾信息。

原来的垃圾信息可能是一种文档类型的缺陷,可能是提供了空字符串或根本没有字符串用来作为信息,从而不能正确显示上面的信息文本。但是,在代码中可能有其他的原因。请注意这个问题有个条件“……然后死亡”,所以可能有一个检查步骤得被加上,以便于重新得到特殊的文本字符串。此处要记住的一点是,对缺陷的描述通常不足以决定具体的缺陷类型,尽管它有助于缩小类型的范围。但有时不得不进入有问题的代码中去确定缺陷是怎样发生的。

●       修正bug报告中版本信息、自动提示信息和严重性描述信息的语法错误。

这几乎可以肯定是一个文档类型的缺陷了。导致这种错误的原因不涉及特定的条件。这是语法上的错误,因此即使提供和显示了文本,文本本身也是有缺陷的。

●        Sabotage ML中不再不正确地显示攻城武器。

这个缺陷描述指的是在游戏中执行一个/delve命令来破坏大师级别(the Sabotage Master Level)的能力。这个快速得出的结论就是,这是一个通过纠正文本而得到修正的一个文档缺陷。另一个可能是,由于一个错误的数组下标导致delve的文本指向不正确的地方,也有可能是由于赋值或功能缺陷导致的。

3.2.8  接口

最后一个需要被讨论的类型是接口类型。一个接口缺陷可以发生于任何信息被转移或交换的地方。在游戏代码内,当一个模块调用另一个模块的方式有误时,接口缺陷就发生了。如果被传送的参数在某种程度上跟调用例程中的并不匹配,那么令人失望的结果就产生了。接口缺陷可以通过许多方式得以产生。幸运的是,这些属于逻辑范畴:

(1) 用一个或多个参数的错误值调用一种函数。

(2) 在参数值顺序不同的情况下调用函数。

(3) 在缺少参数的情况下调用函数。

(4) 用一个负值的参数调用函数。

(5) 用一个比特位颠倒的参数值调用函数。

(6) 用一个比预期值大的参数调用函数。

(7) 用一个比预期值小的参数调用函数。

下面说明了产生每个Vanish问题的原因。我们使用了ShowDuration函数,它在本章中的早些时候已被介绍了,下面给出了以下函数原型:

void ShowDuration(BOOLEAN_T bShow, int duration);

这个例程没有返回任何值,而是用一个布尔类型的参数来确定要不要显示数值,duration的值就是持续时间值,该值如果比0大就会被显示。所以,下面就是产生接口缺陷的7种原因的示例:

1. ShowDuration(TRUE, g_vanishSpeed[level]);

在这个例子中,错误的全局数组被用来保存持续时间(应该将g_vanishSpeed 中的Speed 改为duration)。这可能会导致错误值的显示,或者根本不会显示。

2. ShowDuration (g_vanishDuration[level], TRUE);

假设我们把BOOLEAN_T数据类型定义为int,那么在ShowDuration内,持续时间值(第一个参数)会与TRUE进行比较,而TRUE值(第二个参数)会被用来作为数字显示。如果持续值不能与TRUE相匹配,那么不会显示值。同样的,如果TRUE被定义为0或一个负值,由于ShowDuration的限制,即持续时间少于或等于0时不显示,那么就不会显示任何值。

3. ShowDuration(TRUE);

不提供任何持续时间值。如果默认0为ShowDuration例程内定义的一个局部变量的值,那么就不会显示任何值。

4. ShowDuration(TRUE, g_vanishDuration[level] | 0x8000);

在这个例子中,一些代码是不必要的,会引起麻烦。比如,在持续时间值中的最高比特的作用就类似于一个判断是否显示时间值的标志。这可能是根据一个包含这个功能的旧的游戏代码或其他具有类似功能的代码编写的。与预期的结果相反,这个示例改变了持续时间值的比特位,并否定了它的含义。既然在ShowDuration中的值会少于0,它就不会被显示。

5. ShowDuration(TRUE, g_vanishDuration[level] ^ TRUE);

这个示例更复杂,且导致了一个唯一的或操作,该操作在持续时间值上执行。同样,这也是一个可能的尝试,试图在持续时间值中使用某一比特位来决定要不要显示持续时间值。在TRUE是0xFFFF的例子中,这会颠倒持续时间中的所有比特位,使其以负值的形式得以传递,这样就能改变其值并使得它不被显示。

6. ShowDuration(FALSE, g_vanishDuration[level+1]);

这里的一个错误是:为了正确显示第一个持续时间,关卡值加1,从而使得数组元素从1开始显示。当关卡是3时,这可能所显示的持续时间为0,因为g_vanishDuration[4]没有被定义。这会导致值不能显示。

7. ShowDuration (FALSE, g_vanishDuration[level-1]);

与第6种的错误类似,为了正确显示第一个持续时间,关卡值减1,当关卡是3时,这可能返回一个0值,使得该值不能正确显示。

其他一些例子在此不再列举,总之要留意每个函数调用参数的方式,一旦值被错误地传递,一个微妙的、不容易被发现的严重的接口缺陷就产生了。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论