游戲中的多任務處理
發(fā)表時間:2024-02-17 來源:明輝站整理相關軟件相關文章人氣:
[摘要]---- Windows 最 杰 出 的 功 能 之 一 是 能 夠 同 時 運 行 多 個 程 序, 但 有 時 也 會 讓 人 感 到 頭 疼, 特 別 是 對 于 那 些 習 慣 于 完 全 控 制 計 算 機 甚 至 時 鐘 頻 率、 非 常 自 信 的 游 戲 程 序 員( 當 然, 我...
---- Windows 最 杰 出 的 功 能 之 一 是 能 夠 同 時 運 行 多 個 程 序, 但 有 時 也 會 讓 人 感 到 頭 疼, 特 別 是 對 于 那 些 習 慣 于 完 全 控 制 計 算 機 甚 至 時 鐘 頻 率、 非 常 自 信 的 游 戲 程 序 員( 當 然, 我 們 的 確 在 乎 那 些 沒 禮 貌 的、 在 退 出 時 不 恢 復 正 確 的 系 統(tǒng) 時 間 的 游 戲。 但 是 幸 好, 現(xiàn) 在 我 們 可 以 忘 掉 這 些 了)。
---- 在 多 任 務 環(huán) 境 下, 游 戲 程 序 員 需 要 注 意 三 個 大 的 負 效 應:
當 游 戲 失 去 焦 點 而 進 入 后 臺 后, 其 執(zhí) 行 不 得 不 被 掛 起( 可 以 在 Moby Dick Windows 中 使 用“ 中 止 的” 變 量 觀 察 它 是 如 何 工 作 的)。 如 果 是 一 個 實 時 游 戲, 程 序 員 當 然 希 望 它 被 懸 掛。 但 在 回 合 制 游 戲 中, 當 玩 家 去 做 其 它 事 情 時, 程 序 員 可 能 不 希 望 計 算 機 一 方 作 任 何 動 作, 但 希 望 后 臺 的 人 工 智 能(AI) 運 算 依 舊 執(zhí) 行。
其 它 的 任 務 占 用 CPU 時 間, 結 果 造 成 我 們 不 能 一 直 控 制 游 戲 中 事 情 發(fā) 生 時 的 速 度。 我 們 將 在 后 面 討 論 這 個 痛 苦 的 問 題。
每 當 游 戲 回 到 前 臺, 程 序 員 不 得 不 重 畫 游 戲 窗 口。Windows 并 不 負 責 記 憶 它 所 覆 蓋 或 隱 藏 的 窗 口 的 內 容; 它 所 能 做 的 最 多 是 通 知 一 個 窗 口 需 要 重 畫 其 客 戶 區(qū) 域。 這 在 有 關 Windows 的 文 章 中 都 有 論 述( 參 見 WM_PAINT 的 內 容), 我 們 在 這 里 就 不 討 論 了。 事 實 上,Moby Dick Windows 并 不 恢 復 其 自 己 的 窗 口; 我 們 將 在 講 到 DirectDraw 下 的 雙 緩 沖 時 看 它 是 如 何 實 現(xiàn) 的。
程 序 中 的 多 任 務
---- 盡 管 Moby Dick DOS 在 使 用 中 斷 處 理 程 序 時 展 示 了 內 部 多 任 務( 或 者 說 多 線 程) 的 一 種 原 始 形 式, 但 是 該 程 序 仍 然 沒 有 突 破 DOS 的 單 主 題 特 性, 即 在 一 個 時 間 只 做 一 件 事 情。 有 些 DOS 程 序 的 確 作 到 了 真 正 的 多 線 程, 但 是 那 需 要 非 常 巨 大 的 編 程 工 作。Windows 95 SDK 使 這 項 工 作 簡 單 了 許 多, 把 線 程 放 進 每 一 個 游 戲 開 發(fā) 者 的“ 錦 囊” 之 中( 如 果 讀 者 還 不 熟 悉 這 個 概 念, 那 么 簡 單 說 明 一 下, 一 個 線 程 就 是 程 序 的 一 部 分, 它 執(zhí) 行 時 獨 立 于 其 它 的 部 分, 并 且 不 需 要 與 其 它 部 分 同 步。 線 程 不 是 由 中 斷 來 驅 動 的; 它 們 只 是 在 每 一 次 Windows 給 它 們 CPU 時 間 時 繼 續(xù) 其 執(zhí) 行。)
---- 在 下 列 情 況 下, 可 能 要 考 慮 實 現(xiàn) 獨 立 的 線 程:
允 許 后 臺 AI。 就 算 是 用 戶 正 忙 于 來 回 移 動(moving pieces around)、 打 開 對 話 框 等 事 情, 計 算 機 也 能 夠 考 慮 其 下 一 步。 處 理 這 類 線 程 非 常 方 便, 因 為 它 不 需 要 與 其 它 的 事 情 同 步。
預 先 加 載 數(shù) 據(jù)。 例 如, 當 玩 家 正 努 力 向 下 一 級 奮 斗 時, 使 一 個 線 程 負 責 讀 取 文 件 并 準 備 好 游 戲“ 世 界”。
給 予 時 間 緊 迫(time-critical) 的 任 務 優(yōu) 先 權。 我 們 將 在 后 面 會 到 這 個 主 題。
游 戲 循 環(huán)
---- 游 戲 循 環(huán) 的 概 念 在 各 編 程 環(huán) 境 下 都 比 較 相 似。 第 一 步, 需 要 獲 取 輸 入: 可 以 是 輪 詢 它, 等 待 它, 或 者 在 它“ 運 行” 時 通 過 中 斷 或 一 個 消 息 隊 列 攔 截 它。 第 二 步, 處 理 該 輸 入, 并 且 把 它 變 成 一 個 在 游 戲 中 有 實 際 意 義 的 動 作, 如: 使 飛 機 傾 斜 飛 行 或 小 卒 向 前 走 一 步。 然 后, 把 結 果 顯 示 出 來。 當 然, 在 這 個 主 題 中 也 要 求 精 雕 細 刻 和“ 變 奏 曲”, 包 括 計 算 AI 的 移 動、 把 控 制 權 從 一 個 玩 家 移 交 給 另 一 個 玩 家、 檢 查 勝 負 等 等。
---- 然 而, 在 Windows 和 DOS 下 實 現(xiàn) 循 環(huán) 的 機 制 迥 然 不 同。 每 個 Windows 程 序 都 建 立 于 一 個 消 息 循 環(huán) 之 上。 盡 管 一 個 游 戲 循 環(huán) 可 以 建 立 在 消 息 循 環(huán) 之 上, 但 是 這 兩 者 仍 有 本 質 的 差 別。
Moby Dick DOS 的 循 環(huán)
---- Moby Dick DOS 演 示 了 一 種 簡 單 的 游 戲 循 環(huán), 在 這 里 我 們 所 做 的 工 作 是: (a) 檢 查 是 否 有 什 么 東 西 要 移 動, (b) 移 動 它, (c) 顯 示 結 果。
while (!gamedone)
{
//調用時間程序 -如果時間未到,則沒有任何響應。
AhabMoved = Move_Ahab();
//僅當 Ahab沒有移動時移動 Moby Dick。
//否則它們可能擦肩而過卻不能攔截。
if (!AhabMoved) Move_Moby();
//如果有任何一個移動,更新屏幕,
//并檢查是否有勝利和失敗。
if ((MobyX != OldMobyX) (MobyY != OldMobyY)
(AhabMoved))
{
UpdateScreen();
if ((MobyX == AhabX) && (MobyY == AhabY)
&& (painted[MobyX][MobyY]))
{
gamedone = 1;
cprintf("\a");
cprintf("You win!");
}
if (TimesUp <= 0) { cprintf("\a"); cprintf("Time's up!"); gamedone="1;" } if (raw_key="=" MAKE_ESC) { gamedone="1;" progdone="1;" } } //結束更新 } //結束游戲內部循環(huán) (while !gamedone) Moby Dick Windows的循環(huán) 從表面看來,好像沒有多大的差別: do { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message="=" WM_QUIT) break; //唯一的退出循環(huán)的出口。 TranslateMessage(&msg); DispatchMessage(&msg); } else { if ((MobyX !="OldMobyX)" (MobyY !="OldMobyY)" (AhabMoved)) { UpdateScreen(); if ((AhabX="=" MobyX) && (AhabY="=" MobyY) && (painted[AhabY][AhabX])) { Control="MessageBoxEx(hwnd," "You caught Moby! Play again?", "Call Me Ishmael", MB_ICONQUESTION MB_YESNO, 0); if (Control="=" IDYES) InitializeGame(); else break; } } //如果有人移動了 } //如果屏幕已更新 } //結束循環(huán) while (TRUE);
---- 前 面 已 經(jīng) 提 到 過, 這 里 沒 有 檢 查 是 否 運 行 超 時, 請 忽 略 它, 筆 者 在 Windows 版 中 未 實 現(xiàn) 它 是 為 了 避 免 令 人 煩 惱 的 中 斷。 中 斷 并 退 出 無 限 循 環(huán) 的 機 制 有 一 點 兒 而 且 并 不 重 要。 我 們 把 精 力 集 中 在 消 息 循 環(huán) 本 身, 所 以 把 其 它 的 無 關 代 碼 都 刪 掉, 只 留 下 最 基 本 的 部 分:
do
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else DoSomething();
}
while (TRUE)
---- 這 是 一 個 非 常 典 型 的 消 息 循 環(huán)。 唯 一 有 點 特 殊 的 地 方 就 是 它 使 用 的 是 PeekMessage 而 不 是 GetMessage。
GetMessage 與 PeekMessage 的 比 較
---- 為 什 么 要 用 PeekMessage 呢 ? 原 因 很 簡 單,GetMessage 等 待 一 個 消 息( 就 像 _getch), 而 PeekMessage 不 是 這 樣( 就 像_kbhit)。 請 考 慮 下 面 的 循 環(huán):
while (GetMessage(&msg, NULL, 0, 0))
{
// 我 們 并 不 進 入 括 號 內 部, 直 到 有 一 個 消 息
TranslateMessage(&msg);
DispatchMessage(&msg);
DoSomething()
}
// 當 GetMessage 返 回 NULL 時, 退 出 該 程 序
return msg.wParam;
---- 在 這 里,DoSomething 不 會 完 成, 除 非 一 個 消 息-- 或 許 多 消 息-- 被 放 入 隊 列 中 并 被 處 理。 如 果 DoSomething 恰 好 產(chǎn) 生 一 個 消 息, 例 如, 如 果 它 更 新 了 屏 幕 并 且 因 此 而 產(chǎn) 生 了 一 個 WM_PAINT 消 息, 那 么 好 了, 水 泵 注 水 后 將 開 始 啟 動 了。 要 使 DoSomething 可 靠 地 完 成 其 工 作, 這 并 不 是 一 個 好 方 法, 它 使 代 碼 有 點 混 淆, 但 它 工 作 的 還 不 錯。
---- 相 比 之 下,PeekMessage 則 無 論 是 否 有 消 息 在 等 待, 只 要 檢 查 一 下 消 息 隊 列 就 完 成 其 操 作(yields the floor)。 在 我 們 的 例 子 中, 我 們 實 際 上 是 使 用 PeekMessage 來 處 理 消 息 的( 通 過 分 發(fā) 它 所 找 到 的 每 一 個 消 息 并 使 用 PM_REMOVE 參 數(shù) 從 隊 列 中 清 除 它)。 同 下 面 同 樣 有 效 的 代 碼 相 比, 它 要 更 加 直 接:
if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
if (!GetMessage(&msg, NULL, 0, 0)) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else DoSomething();
---- 在 這 里 有 非 常 重 要 的 一 點 要 說 明, 我 們 的 偽 代 碼 DoSomething 是 獨 立 于 消 息 的; 無 論 隊 列 中 送 出 的 什 么 消 息, 甚 至 無 論 有 沒 有 消 息 在 那 兒, 它 都 將 執(zhí) 行。 在 Moby Dick 中, 我 們 將 屏 幕 更 新 和 勝 利 條 件 的 檢 查 放 在 這 里, 因 為 在 這 里 檢 查 一 個 或 多 個 消 息 被 響 應 后 是 否 需 要 更 新 屏 幕 或 是 否 達 到 勝 利 條 件 很 方 便。
---- 那 么, 消 息 循 環(huán) 就 是 游 戲 循 環(huán) 嗎 ? 從 抽 象 的 角 度, 是 的, 因 為 它 是 大 的 齒 輪, 帶 動 那 些 小 的 齒 輪。 但 是, 盡 管 把 一 些 函 數(shù) 調 用 放 在 此 處 可 能 比 較 方 便,Windows 編 程 規(guī) 則 卻 要 求 任 何 響 應 一 個 消 息 的 動 作 都 應 該 放 在 消 息 響 應 程 序 中( 就 是 說, 放 在 窗 口 過 程 中)。 在 一 個 實 時 游 戲 中, 絕 大 多 數(shù) 的 動 作 發(fā) 生 在 一 個 或 多 個 WM_TIMER 消 息 響 應 程 序 中。 回 合 制 游 戲 則 常 常 在 輸 入 消 息 的 響 應 函 數(shù) 中 做 大 量 的 工 作。