Programming 版 (精华区)

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

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

                    从DOS 到Windows 的 游 戏 移 植(6)
                                      
                                   谭 翁
                                      
     _________________________________________________________________
                                      
   ---- 本 期 将 讨 论 鼠 标 方 面 的 问 题。Moby Dick DOS 实 际 上 没 有
   使 用 鼠 标 光 标, 并 且 切 换 到 图 形 模 式 时, 默 认 情 况 下 是
   鼠 标 光 标 是 不 可 见 的。 在 Windows 中, 情 形 刚 好 相 反。 光 标
   总 是 出 现 在 窗 口 中, 除 非 特 意 将 它 藏 起 来, 比 如 在 全 屏
   幕 的 应 用 程 序 中。
   
  自 定 义 光 标
  
   ---- 在DOS 下 实 现 一 个 光 标, 需 要 使 用DOS 中 断 33h 的 子 调 用
   09h, 将 一 个 位 图 分 配 给 光 标 作 为 其 图 像。 如 果 光 标 在 屏
   幕 的 不 同 点 上 改 变 了 形 状, 那 么 在 每 次 调 用 该 中 断 之 后
   , 必 须 保 存 光 标 的 位 置。 应 用 程 序 负 责 跟 踪 光 标 位 置 并
   相 应 地 改 变 光 标 形 状。
   
   ---- 在Windows 下, 实 现 自 定 义 光 标 变 得 十 分 容 易。 如 果 仍
   想 使 用 现 有 的DOS 游 戏 中 的 硬 代 码 图 像 数 据, 就 需 要 利 用
   CreateCursor 在 程 序 运 行 时 动 态 地 绘 制 一 个 光 标。 但 在 大 多
   数 情 况 下 采 用 的 方 法 是, 在 资 源 编 辑 器 中 画 出 图 像, 将
   资 源 文 件 链 接 到 应 用 程 序, 用 LoadCursor 使 图 像 在 运 行 时
   可 见。 当 然, 使 用 标 准 的 光 标 前 必 须 先 加 载 它。
   
   ---- 可 以 使 用 SetCursor 在 任 何 时 间 改 变 光 标 的 形 状, 其 唯
   一 的 输 入 参 数 是 新 光 标 的 句 柄。 只 要 光 标 已 经 被 加 载
   ,LoadCursor 将 仅 返 回 已 存 在 的 句 柄, 所 以 下 面 的 代 码 即 使
   在 循 环 中 执 行, 也 丝 毫 不 会 浪 费 系 统 资 源:
   
   ---- SetCursor(LoadCursor(MAKEINTRESOURCE(IDC_MYCURSOR)));
   
   ---- 想 要 使 用 自 定 义 光 标, 必 须 将 窗 口 默 认 光 标 设 成
   NULL。 有 关 SetCursor 请 参 阅 API 参 考。
   
   ---- 在SVGA 模 式 下 的 基 于DOS 的 游 戏 中, 大 多 数 卡 甚 至 连 最
   基 本 的“ 指 针” 状 光 标 也 必 须 在 软 件 中 实 现。 在 Windows 下
   , 这 当 然 是 不 必 要 的, 因 为 它 能 够 轻 松 地 适 应 图 形 驱 动
   器 所 支 持 的 任 何 显 示 模 式。
   
  鼠 标 逃 逸(Mouse Escapes) 时 怎 么 办 ?
  
   ---- 根 据 定 义, 基 于DOS 的 游 戏 是 全 屏 的, 所 以 当 鼠 标 游 走
   到 边 界 之 外 时, 程 序 员 不 必 担 心 会 发 生 什 么 问 题。 但 是,
   在 基 于 Windows 的 游 戏 中, 即 使 鼠 标 不 在 应 用 程 序 窗 口 中,
   也 有 必 要 跟 踪 鼠 标 光 标。
   
   ---- 一 般 情 况 下, 只 有 当 鼠 标 活 动 发 生 在 应 用 程 序 或 是
   对 话 框 窗 口 内 部 时,Windows 才 将 鼠 标 消 息 发 送 给 应 用 程 序
   或 者 对 话 框 窗 口。 而 且, 它 根 据 鼠 标 是 活 动 在 客 户 区 域
   还 是 非 客 户 区 域( 例 如, 在 菜 单 栏, 或 者 是 在 边 框 上),
   发 送 不 同 的 消 息。
   
   ---- 大 多 数 应 用 程 序, 包 括 简 单 的 指 点(point-and-click) 战
   略 游 戏, 都 只 是 围 绕 着 客 户 区 域 消 息。 然 而, 游 戏 可 能 需
   要 了 解 当 鼠 标 在 客 户 区 域 之 外 时, 向 上 到 达 了 什 么 位 置
   。 举 一 个 普 通 的 例 子---- 有 关 地 图 方 面 的 战 略 游 戏, 当 光
   标 在 客 户 区 域 之 外 移 动 时, 地 图 将 会 自 动 地 卷 动。 在
   Moby Dick 中, 非DirectInput 鼠 标 接 口 要 求 Ahab 移 向 光 标, 而 无
   论 光 标 是 否 在 游 戏 窗 口 内 部( 注 意: 这 只 有 在 没 有 定 义
   USE_DIRECTINPUT 时 编 译 该 程 序 才 有 效。)
   
   ---- 在 Windows 3.1 下,Moby Dick 应 用 程 序 可 能 已 经 使 用 了
   SetCapture 函 数 来“ 捕 获 鼠 标”, 也 就 是 说 将 所 有 的 鼠 标 消
   息 读 到 游 戏 中, 而 不 考 虑 光 标 所 在 的 位 置。 而 在 Windows
   95 下 这 是 不 可 能 的, 原 因 是 异 步 输 入 处 理, 它 是 Windows
   95 禁 止 任 何 一 个 应 用 程 序 独 占 系 统 的 一 种 方 法。 程 序 员
   仍 可 以 捕 获 鼠 标, 但 只 有 在 按 钮 释 放 之 后。 很 显 然, 这 不
   是 我 们 在 Moby Dick 中 所 想 要 的。
   
   ---- 还 有 两 种 更 为 复 杂 的 捕 获 鼠 标 的 方 法, 这 两 种 方 法
   都 要 求 安 装 一 个“ 钩 子”(hook), 用 于 拦 截 在 应 用 程 序 之
   外 的 Windows 消 息。 有 一 个 API 函 数 GetCursorPos, 无 论 光 标 在
   屏 幕 上 的 什 么 位 置, 它 都 将 返 回 光 标 的 当 前 位 置。
   
   ---- GetCursorPos 以 屏 幕 坐 标 返 回 光 标 的 位 置, 然 而 本 程 序
   中 的 图 形 函 数 是 以 窗 口 坐 标 为 基 础 的, 因 为 它 们 是 独 立
   于 窗 口 的 实 际 位 置 的, 这 就 需 要 ClientToScreen。 它 将 窗 口
   中 一 个 点 的 相 对 坐 标 转 换 为 绝 对 或 者 屏 幕 坐 标, 并 且 把
   窗 口 的 当 前 位 置 考 虑 进 来。
   
   ---- 下 面 部 分 代 码 显 示 了 如 何 决 定 与 光 标 位 置 相 比 较 的
   Ahab 的 矩 形 边 界 的 屏 幕 坐 标。
   
   ---- RECT AhabRect;
   
   ---- // 计 算 船 的 矩 形 网 格 的 窗 口 坐 标。
   
   ---- // 坐 标 将 由 船 的 行 和 列 的 单 元 格 数 乘 以 一 个 单 元 格
   的 像 素 而 得 出。

AhabRect.top = AhabY * SPRITE_HT;
AhabRect.bottom = AhabRect.top + SPRITE_HT;
AhabRect.left = AhabX * SPRITE_WD;
AhabRect.right = AhabRect.left + SPRITE_WD;

   ---- // 转 换 为 屏 幕 坐 标。 我 们 把 RECT 视 为 两 个 POINT ( 左 上
   和 右 下) 的 一 个 数 组。
   
   ---- ClientToScreen(hMainWindow, (POINT*) &AhabRect);
   ---- ClientToScreen(hMainWindow, (POINT*) &AhabRect.right);
   
   ---- 还 有 一 种 方 法 可 以 确 保 程 序 不 再 丢 失 对 鼠 标 的 控 制
   : 在 游 戏 启 动 时 将 窗 口 最 大 化, 并 且 不 提 供 恢 复 按 钮。
   当 一 个 应 用 程 序 处 于 前 台 时, 就 把 所 有 其 他 应 用 程 序 都
   “ 挤” 出 去, 这 虽 然 不 是 好 的 Windows 做 法, 但 确 实 很 有 效
   。
   
  鼠 标 单 击
  
   ---- 如 果 只 使 用 标 准 的 Windows API, 至 少 有 三 种 方 法 可 以
   在 应 用 程 序 窗 口 中 监 视 鼠 标 的 单 击:
    1. 鼠 标 消 息。 这 是 捕 获 每 一 次 单 击 的 最 可 靠 的 方 法。 它
       的 缺 点 是 快 速 的 单 击( 例 如 在 一 个 射 击 游 戏 中) 将 堆
       积 在 消 息 列 队 中, 不 能 被 及 时 处 理。 如 果 使 用
       GetCursorPos 轮 询 来 跟 踪 光 标 移 动 的 轨 迹, 也 许 会 遇 到
       麻 烦, 不 得 不 同 时 响 应 鼠 标 单 击 和 光 标 移 动。
    2. GetKeyState。 该 函 数 可 用 来 测 试 鼠 标 按 钮 的 状 态, 但 是
       与 键 的 情 况 一 样, 该 函 数 并 不 返 回 当 前 硬 件 的 实 际 状
       态。 它 告 诉 程 序 员 当 最 后 一 次 从 队 列 中 取 出 消 息 时,
       按 钮 处 在 什 么 状 态。 例 如, 如 果 处 理 的 最 后 一 个 消 息
       是WM_LBUTTONDOWN,GetKeyState(VK_LBUTTON) 就 会 返 回 一 个 负 值(
       最 高 位 是 1)。
       
   ---- GetKeyState 返 回 值 的 低 位 字 节 实 际 上 就 是 一 个“ 开 关”
   动 作, 即 每 按 下 一 次 按 钮( 或 键) 都 切 换 该 键 对 应 的 值。
   下 面 一 行 代 码( 在 游 戏 循 环 的 某 处 执 行), 允 许 用 户 用 右
   边 的 按 钮 打 开 或 者 关 闭 音 乐:
   
   ---- BOOL MusicOn = (GetKeyState(VK_RBUTTON) & 1);
   
   ---- GetKeyState 是 设 计 用 来 响 应 设 置 按 钮 状 态 的 消 息 的,
   不 能 用 类 似 下 面 的 语 句 来 等 待 鼠 标 单 击:
   
   ---- while (GetKeyState(VK_LBUTTON) >= 0); // *** 不 工 作 ! ***
   
   ---- 还 有, 象 下 面 这 样 轮 询 鼠 标 按 钮:
   
   ---- if (GetKeyState(VK_LBUTTON) < 1) DoSomething();
   
   ---- 将 捕 获 不 到 所 有 的 单 击, 因 为 从 鼠 标 最 后 一 次 被 轮
   询 之 后, 用 户 可 能 已 经 按 下 并 释 放 按 钮 了。
     * GetAsyncKeyState。 如 果 不 是 使 用 DirectInput 3.0 来 跟 踪 鼠 标
       单 击, 并 且 游 戏 循 环 不 是 以 消 息 队 列 为 基 础 的, 那 么
       使 用GetAsyncKeyState 可 能 是 最 好 的 方 法。 对 于 鼠 标, 它 的
       工 作 方 式 是 相 同 的, 使 用 虚 键 VK_LBUTTON、VK_RBUTTON 和(
       对 于 三 个 按 钮 的 鼠 标)VK_MBUTTON。 注 意 这 些 虚 键 被 映 射
       到 鼠 标 的 实 际 按 钮, 而 不 是 用 户 在 控 制 面 板 所 定 义 的
       第 一 和 第 二 按 钮。
       
     _________________________________________________________________
                                      

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