Programming 版 (精华区)

发信人: superman (☆风雨无阻☆), 信区: Programming
标  题: [转载] DOS到Windows的 游 戏 移 植(4)
发信站: 紫 丁 香 (Sat Aug 29 07:35:45 1998), 站内信件

【 以下文字转载自 cnprogram 讨论区 】
【 原文由 Young_Yang@bbs.ustc.edu.cn 所发表 】

                    从DOS 到Windows 的 游 戏 移 植(4)
                                      
                                   谭 翁
                                      
     _________________________________________________________________
                                      
  使 用 时 钟
  
   ---- Moby Dick DOS 使 用 了 一 个 时 钟 中 断 服 务 程 序, 以 确 保
   Ahab 和 鲸 鱼 以 一 种 独 立 于 处 理 器 速 度 的 稳 定 速 率 移 动
   。Windows 没 有 提 供 这 种 访 问 硬 件 的 机 制, 但 是 它 提 供 了 许
   多 的 时 钟(timer), 可 以 完 成 此 工 作。
   
   ---- 当 在 程 序 中 创 建 一 个 时 钟 时, 实 际 是 在 告 诉 Windows 以
   一 个 特 定 的 时 间 间 隔 来 做 某 事。 根 据 不 同 的 时 钟,Windows
   完 成 的 操 作 也 不 同。 标 准 时 钟, 使 用 SetTimer 来 调 用, 只 是
   简 单 地 寄 出 一 个 WM_TIMER 消 息, 该 消 息 分 发 给 标 准 的 窗 口
   过 程 或 一 个 已 经 定 义 好 的 特 殊 的 回 调 函 数。 多 媒 体 时 钟
   同 时 忽 略 消 息 队 列。
   
  标 准 时 钟 的 限 制
  
   ---- 标 准 时 钟 与 我 们 在 Moby Dick DOS 中 所 使 用 的 正 常 时 钟
   中 断 有 着 相 同 的 分 辨 率, 大 约 55 毫 秒。 换 句 话 说, 无 论 程
   序 员 设 定 的 时 间 输 出 值 是 多 少, 在 两 个 WM_TIMER 消 息 之 间
   至 少 要 有 55 毫 秒。 而 且 到 它 被 处 理 之 前 可 能 时 间 还 要 长
   , 因 为 Windows 不 给 时 钟 消 息 分 配 任 何 优 先 权, 它 们 只 能
   像 其 它“ 人” 一 样 在 那 里 排 队 等 候。
   
   ---- 同 样,WM_TIMER 消 息 也 像 WM_PAINT 消 息 一 样: 如 果 在 队 列
   中 已 经 有 一 个 在 等 待, 则 不 能 添 加 更 多 的。 所 以, 如 果
   Windows 恰 好 正 忙 于 其 它 的 事 情, 有 几 个 时 钟“ 滴 答” 被 消
   耗 掉, 程 序 没 有 得 到 机 会 响 应。 这 时, 只 是 简 单 地 跳 过 几
   个“ 拍 节”, 而 不 是 跑 着 去 追 回 来。 对 于 依 靠 频 繁“ 世 界”
   更 新 的 实 时 游 戏 来 说, 无 法 选 择 跳 过 一 个“ 拍 节”。
   
  多 媒 体 时 钟
  
   ---- Windows 的 多 媒 体 功 能 的 扩 展 包 括 一 个 高 分 辨 率 的 时
   钟, 可 以 用 timeSetEvent 来 调 用, 其 分 辨 率 可 高 达 1 毫 秒。 除
   了 具 有 非 常 高 的 分 辨 率 之 外, 该 时 钟 还 能 产 生 更 加 准 确
   的 结 果, 因 为 它 不 需 要 依 赖 通 过 队 列 发 送 的 WM_TIMER 消 息
   。
   
   ---- 事 实 上, 每 一 个 多 媒 体 时 钟 是 在 其 自 身 的 线 程 中 的,
   而 且 无 论 是 否 有 中 止 的 消 息 或 其 它 正 在 进 行 的 事 情, 其
   回 调 函 数 是 直 接 被 调 用 的。 这 一 点 很 重 要, 因 为 这 意 味
   着 在 处 理 全 局 数 据 时, 程 序 员 必 须 特 别 小 心。 变 量 有 可
   能 被 时 钟 程 序 改 变, 而 也 许 这 个 变 量 正 被 其 他 一 般 的 消
   息 响 应 函 数 所 使 用。 如 果 确 实 设 置 了 一 个 多 媒 体 时 钟,
   一 定 要 理 解 有 关 同 步、 信 号 语 言(semaphores)、 以 及 所 有 其
   它 的 多 媒 体 编 程 的 机 构(apparatus)( 对 此 不 熟 悉 的 程 序 员
   请 参 考 有 关 资 料)。
   
   ---- 要 使 用 timeSetEvent, 必 须 包 含 MMSYSTEM.H 并 且 要 链 接
   WINMM.LIB。
   
   ---- 尽 管 有 着 很 高 的 分 辨 率, 多 媒 体 时 钟 还 是 可 能 会 有
   延 迟, 而 且 这 种“ 潜 在 的 问 题” 在 Windows 95 下 比 在 3.1 下 要
   大 得 多。 对 于 那 些 需 要 非 常 高 的 时 钟 精 确 度 的 应 用 程 序
   , 可 能 不 得 不 在 16- 位 DLL 中 实 现 多 媒 体 时 钟。
   
  不 使 用 时 钟 来 完 成 工 作
  
   ---- 如 前 所 述, 使 用 一 个 多 媒 体 时 钟 将 自 动 在 程 序 中 创
   建 一 个 单 独 的 线 程, 可 能 还 同 时 产 生 了 安 全 防 卫 的 需 求
   , 以 避 免 对 数 据 的 不 适 时 访 问。 但 是 并 不 是 一 定 要 设 置
   好 信 号 语 言(semaphores) 和 关 键 段(critical sections), 因 为 程
   序 员 可 以 根 本 不 使 用 时 钟 也 同 样 能 够 简 单 地 编 写 出 游 戏
   , 这 种 方 法 就 是 轮 询 系 统 时 钟。 表 面 上 看 来, 轮 询 的 效
   率 好 象 比 较 低, 但 是 Windows 有 其 自 身 的 框 架 以 决 定 何 时
   触 发 一 个 时 钟, 所 以 这 两 种 方 法 的 差 别 可 能 没 有 那 么 大
   。
   
   ---- 有 两 个 在 高 分 辨 率 下 可 用 的 轮 询 函 数。 第 一 个 是
   timeGetTime, 它 返 回 自 Windows 启 动 以 来 经 过 的 毫 秒 数。 默 认
   的 分 辨 率 是 1 毫 秒, 但 在 Windows NT 中 除 外( 参 见 API 参 考)
   。 对 于 timeSetEvent, 要 使 用 该 函 数 需 要 链 接 到 多 媒 体 库。
   
   ---- 有 些 程 序 员 可 能 觉 得 1 毫 秒 的 分 辨 率 太 高 了, 以 至 在
   实 时 游 戏 中 并 不 需 要, 实 际 上 却 恰 恰 相 反, 有 时 会 发 生
   延 迟 事 件。 对 于 高 性 能 的 游 戏 来 说, 解 决 的 方 法 有 两 个:
   (1) 在 16- 位 的 DLL 中 使 用 多 媒 体 时 钟;(2) 转 移 到 所 有 时
   间 服 务 中 分 辨 率 最 高 的 QueryPerformanceCounter, 这 个 函 数 主
   要 是 为 配 置 而 设 计 的, 但 是 我 们 同 样 可 以 把 它 作 为 一 个
   普 通 目 的 的 时 钟 来 使 用。
   
   ---- 同 timeGetTime 一 样,QueryPerformanceCounter 也 返 回 自 系 统 启
   动 以 来 经 过 的 时 间。 度 量 的 单 位 是 由 硬 件 决 定 的; 对 于
   基 于 Intel 的 CPU, 大 概 是 0.8 微 秒。
   
   ---- 下 面 给 出 的 是 WinMain 函 数 的 一 段, 显 示 如 何 使 用
   QueryPerformanceCounter 来 使 游 戏“ 世 界” 每 1/10 秒 更 新 一 次。

#define UPDATE_TICKS_MS 100
        // 每 次“ 世 界” 更 新 的 毫 秒 数

_int64 start, end, freq, update_ticks_pc
MSG     msg;

// 获 得 性 能 计 数 器 的 每 秒“ 滴 答” 数
//   注 意, 必 须 将 类 型 强 制 转 换 为LARGE_INTEGER 结 构
if (!QueryPerformanceFrequency((LARGE_INTEGER*)&freq))
  return -1;  // 错 误 * 硬 件 不 支 持 性 能 计 数 器

// 将 每 次 更 新 的 毫 秒 数 转 换 为 每 次 更 新 的 性 能 计 数 器 单 位。
update_ticks_pc = UPDATE_TICKS_MS * freq / 1000;

// 初 始 化 计 数 器。
QueryPerformanceCounter((LARGE_INTEGER*)&start);

  // 主 消 息 循 环 从 这 里 开 始
    -- while (GetMessage(&msg, NULL, 0, 0))
    {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    QueryPerformanceCounter((LARGE_INTEGER*)&end);
  // 内 部 的 循 环 保 证 如 果 需 要 的 话,
  // “ 世 界” 的 更 新 次 数 多 于 一 次。
    while ((end - start) >= update_ticks_pc)
      {
      UpdateWorld();
      start += update_ticks_pc;
      }
    } // 消 息 循 环 结 束
  return msg.wParam;

   ---- 注 意, 如 果 硬 件 不 支 持 性 能 计 数 器
   ,QueryPerformanceFrequency 将 返 回 FALSE。 这 种 情 况 通 常 不 会 发
   生, 如 果 真 的 发 生 了, 只 有 使 用 timeGetTime 的 程 序 来 替 代。
   
  有 关 竞 争
  
   ---- 有 时 Windows 看 起 来 好 象 用 一 只 手 给 出, 但 同 时 又 用 另
   一 只 手 把 它 拿 走。 用 它 的 右 手 提 供 了 时 间 测 量 达 1 毫 秒
   的 能 力, 但 同 时 左 手 又 过 来, 在 最 需 要 的 时 候 把 CPU 时 间
   抓 走 了, 于 是 游 戏 中 的“ 捣 蛋 鬼”(sprites) 就 会“ 死” 在 那
   里, 蹒 跚 地 走 过 屏 幕, 就 像 许 多 个 穿 着 木 底 鞋 的 芭 蕾 舞
   女 演 员 一 样。
   
   ---- 下 面 我 们 要 更 形 象 地 看 一 下 该 问 题。 启 动 Moby Dick,
   然 后 运 行 多 个 Time Waster 的 实 例。 单 击 Moby Dick 窗 口, 重 新
   激 活 该 游 戏, 然 后 观 察 云 的 不 稳 定 的 移 动 情 况。 如 果
   Time Waster 真 的 任 意 消 耗 CPU 的 时 间, 甚 至 船 的 动 画 也 会 被
   破 坏。 甚 至 在 所 有 的 Time Waster 的 实 例 都 已 关 闭 之 后, 它
   们 仍 能 使 系 统 的 工 作 陷 入 沼 泽。 这 是 因 为 所 有 分 配 到 硬
   盘 上 的 虚 拟 内 存 都 必 须 被 释 放。Windows 有 可 能 正 在 做 一 些
   交 换 文 件 的 清 除 工 作。
   
   ---- 尽 管 对 一 般 玩 家 来 说, 不 需 要 在 后 台 运 行 消 耗 处 理
   器 时 间 的 任 务, 游 戏 仍 然 要 与 网 络 动 作 和 Windows 日 常 事
   务 分 享 时 间。 程 序 员 不 能 向 在 DOS 下 工 作 时 那 样 独 占 CPU,
   充 其 量 最 大 的 期 望 就 是 给 游 戏 分 配 一 个 比 较 大 块 的 时 间
   “ 馅 饼”。
   
  调 整 优 先 级
  
   ---- Windows 并 不 需 要 给 每 一 个 正 在 处 理 的 任 务 分 配 相 等
   的 CPU 时 间 数。 根 据 应 用 程 序 的 综 合 的 优 先 类 别(priority
   class), 以 及 在 这 些 应 用 程 序 内 部 的 每 个 线 程 的 线 程 优
   先 级(thread priority),Windows 把 时 间“ 馅 饼” 分 割 开 来。 这 两
   种 优 先 级 都 是 由 应 用 程 序 的 开 发 者 决 定 的。
   
   ---- 然 而, 在 某 些 特 定 的 时 候, 此 时 Windows 也 会 提 升 该 线
   程 的 优 先 级。 这 种 现 象 可 以 在 Moby Dick 中 非 常 形 象 地 观 察
   到, 方 法 是 当 云 彩 的 线 程 遇 到 麻 烦 而 不 能 得 到 足 够 的
   CPU 时 间 时, 打 开 一 个 菜 单 或 对 话 框, 此 时, 云 彩 的 移 动
   会 立 刻 变 得 平 滑 起 来。Windows 动 态 地 提 升 在 应 用 程 序 中 的
   所 有 线 程 的 优 先 级, 这 是 因 为 它 在 猜 测 是 否 用 户 想 要 做
   点 什 么 事 情, 并 在 期 待 快 速 的 响 应。
   
   ---- Moby Dick Windows 的 View 菜 单 的 Settings 命 令 允 许 程 序 员
   改 变 该 应 用 程 序 的 优 先 类 别, 程 序 员 还 可 以 设 置 主 线 程
   和 移 动 云 彩 通 过 屏 幕 的 线 程 的 优 先 级。 警 告: 某 些 优 先
   级 的 组 合 可 能 导 致 Windows 无 法 正 常 工 作, 只 能 通 过 使 用
   CTRL+ALT+DEL 来 关 闭 Moby Dick。
   
   ---- 改 变 优 先 级 类 别 和 线 程 优 先 级 是 非 常 危 险 的。 特 别
   要 注 意 的 是“ 一 个 具 有 高 于 11 的 优 先 级 的 线 程 会 与 操 作
   系 统 的 正 常 操 作 相 冲 突。” 这 就 意 味 着 如 果 使 用 高 于
   HIGH_PRIORITY_CLASS 和 THREAD_PRIORITY_LOWEST 或 者
   NORMAL_PRIORITY_CLASS 和 THREAD_PRIORITY_HIGHEST 的 组 合, 程 序 就 不
   能 安 全 工 作。
   
   ---- 读 者 可 以 在 实 验 Moby Dick Windows 的 优 先 级 的 同 时, 在
   后 台 运 行 一 个 或 多 个 Time Waster 的 实 例, 同 时 计 量(gauge) 出
   其 对 游 戏 和 对 系 统 的 效 果。 最 后, 可 能 会 得 出 这 个 结 论,
   在 程 序 的 整 个 生 命 期 中, 重 新 设 置 优 先 级 所 带 来 的 问 题
   要 比 解 决 的 问 题 还 多。 但 是, 暂 时 地 提 升 某 些 时 间 紧 迫
   任 务 的 优 先 级, 还 是 有 用 的。
   
     _________________________________________________________________
                                      

--
※ 来源: 中国科大BBS站 [bbs.ustc.edu.cn]
--
※ 转载:.紫 丁 香 bbs.hit.edu.cn.[FROM: poster.hit.edu.c]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:209.530毫秒