線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:1607
推到 Plurk!
推到 Facebook!

[分享] windows 游戲設計時的單任務與多任務處理

 
axsoft
版主


發表:681
回覆:1056
積分:969
註冊:2002-03-13

發送簡訊給我
#1 引用回覆 回覆 發表時間:2002-07-23 09:55:15 IP:61.218.xxx.xxx 未訂閱
windows 游戲設計時的單任務與多任務處理    資料來源: 標點工作室  http://makegame.myetang.com/jswd/013.htm    單 任 務 處 理    Windows 最 傑 出 的 功 能 之 一 是 能 夠 同 時 運 行 多 個 程 序, 但 有 時 也 會 讓 人 感 到 頭 疼, 特 別 是 對 於 那 些 習 慣 於 完 全 控 制 計 算 機 甚 至 時 鐘 頻 率、 非 常 自 信 的 游 戲 程 序 員( 當 然, 我 們 的 確 在 乎 那 些 沒 禮 貌 的、 在 退 出 時 不 恢 復 正 確 的 系 統 時 間 的 游 戲。 但 是 幸 好, 現 在 我 們 可 以 忘 掉 這 些 了)。    ---- 在 多 任 務 環 境 下, 游 戲 程 序 員 需 要 注 意 三 個 大 的 負 效 應:    當 游 戲 失 去 焦 點 而 進 入 後 台 後, 其 執 行 不 得 不 被 掛 起( 可 以 在 Moby Dick Windows 中 使 用“ 中 止 的” 變 量 觀 察 它 是 如 何 工 作 的)。 如 果 是 一 個 實 時 游 戲, 程 序 員 當 然 希 望 它 被 懸 掛。 但 在 回 合 制 游 戲 中, 當 玩 家 去 做 其 它 事 情 時, 程 序 員 可 能 不 希 望 計 算 機 一 方 作 任 何 動 作, 但 希 望 後 台 的 人 工 智 能(AI) 運 算 依 舊 執 行。    其 它 的 任 務 佔 用 CPU 時 間, 結 果 造 成 我 們 不 能 一 直 控 制 游 戲 中 事 情 發 生 時 的 速 度。 我 們 將 在 後 面 討 論 這 個 痛 苦 的 問 題。    每 當 游 戲 回 到 前 台, 程 序 員 不 得 不 重 畫 游 戲 窗 口。Windows 並 不 負 責 記 憶 它 所 覆 蓋 或 隱 藏 的 窗 口 的 內 容﹔ 它 所 能 做 的 最 多 是 通 知 一 個 窗 口 需 要 重 畫 其 客 戶 區 域。 這 在 有 關 Windows 的 文 章 中 都 有 論 述( 參 見 WM_PAINT 的 內 容), 我 們 在 這 裡 就 不 討 論 了。 事 實 上,Moby Dick Windows 並 不 恢 復 其 自 己 的 窗 口﹔ 我 們 將 在 講 到 DirectDraw 下 的 雙 緩 衝 時 看 它 是 如 何 實 現 的。    程 序 中 的 多 任 務    ---- 盡 管 Moby Dick DOS 在 使 用 中 斷 處 理 程 序 時 展 示 了 內 部 多 任 務( 或 者 說 多 線 程) 的 一 種 原 始 形 式, 但 是 該 程 序 仍 然 沒 有 突 破 DOS 的 單 主 題 特 性, 即 在 一 個 時 間 只 做 一 件 事 情。 有 些 DOS 程 序 的 確 作 到 了 真 正 的 多 線 程, 但 是 那 需 要 非 常 巨 大 的 編 程 工 作。Windows 95 SDK 使 這 項 工 作 簡 單 了 許 多, 把 線 程 放 進 每 一 個 游 戲 開 發 者 的“ 錦 囊” 之 中( 如 果 讀 者 還 不 熟 悉 這 個 概 念, 那 麼 簡 單 說 明 一 下, 一 個 線 程 就 是 程 序 的 一 部 分, 它 執 行 時 獨 立 於 其 它 的 部 分, 並 且 不 需 要 與 其 它 部 分 同 步。 線 程 不 是 由 中 斷 來 驅 動 的﹔ 它 們 只 是 在 每 一 次 Windows 給 它 們 CPU 時 間 時 繼 續 其 執 行。)    ---- 在 下 列 情 況 下, 可 能 要 考 慮 實 現 獨 立 的 線 程:    允 許 後 台 AI。 就 算 是 用 戶 正 忙 於 來 回 移 動(moving pieces around)、 打 開 對 話 框 等 事 情, 計 算 機 也 能 夠 考 慮 其 下 一 步。 處 理 這 類 線 程 非 常 方 便, 因 為 它 不 需 要 與 其 它 的 事 情 同 步。    預 先 加 載 數 據。 例 如, 當 玩 家 正 努 力 向 下 一 級 奮 鬥 時, 使 一 個 線 程 負 責 讀 取 文 件 並 準 備 好 游 戲“ 世 界”。    給 予 時 間 緊 迫(time-critical) 的 任 務 優 先 權。 我 們 將 在 後 面 會 到 這 個 主 題。    游 戲 循 環    ---- 游 戲 循 環 的 概 念 在 各 編 程 環 境 下 都 比 較 相 似。 第 一 步, 需 要 獲 取 輸 入: 可 以 是 輪 詢 它, 等 待 它, 或 者 在 它“ 運 行” 時 通 過 中 斷 或 一 個 消 息 隊 列 攔 截 它。 第 二 步, 處 理 該 輸 入, 並 且 把 它 變 成 一 個 在 游 戲 中 有 實 際 意 義 的 動 作, 如: 使 飛 機 傾 斜 飛 行 或 小 卒 向 前 走 一 步。 然 後, 把 結 果 顯 示 出 來。 當 然, 在 這 個 主 題 中 也 要 求 精 雕 細 刻 和“ 變 奏 曲”, 包 括 計 算 AI 的 移 動、 把 控 制 權 從 一 個 玩 家 移 交 給 另 一 個 玩 家、 檢 查 勝 負 等 等。    ---- 然 而, 在 Windows 和 DOS 下 實 現 循 環 的 機 制 迥 然 不 同。 每 個 Windows 程 序 都 建 立 於 一 個 消 息 循 環 之 上。 盡 管 一 個 游 戲 循 環 可 以 建 立 在 消 息 循 環 之 上, 但 是 這 兩 者 仍 有 本 質 的 差 別。    Moby Dick DOS 的 循 環    ---- Moby Dick DOS 演 示 了 一 種 簡 單 的 游 戲 循 環, 在 這 裡 我 們 所 做 的 工 作 是: (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;" } } //結束更新 } //結束游戲內部循環 (while !gamedone) Moby Dick Windows的循環 從表面看來,好像沒有多大的差別: do { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message="=" WM_QUIT) break; //唯一的退出循環的出口。 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; } } //如果有人移動了 } //如果屏幕已更新 } //結束循環 while (TRUE);    ---- 前 面 已 經 提 到 過, 這 裡 沒 有 檢 查 是 否 運 行 超 時, 請 忽 略 它, 筆 者 在 Windows 版 中 未 實 現 它 是 為 了 避 免 令 人 煩 惱 的 中 斷。 中 斷 並 退 出 無 限 循 環 的 機 制 有 一 點 兒 而 且 並 不 重 要。 我 們 把 精 力 集 中 在 消 息 循 環 本 身, 所 以 把 其 它 的 無 關 代 碼 都 刪 掉, 只 留 下 最 基 本 的 部 分:    do    {    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))    {    if (msg.message == WM_QUIT) break;    TranslateMessage(&msg);    DispatchMessage(&msg);    }    else DoSomething();    }    while (TRUE)    ---- 這 是 一 個 非 常 典 型 的 消 息 循 環。 唯 一 有 點 特 殊 的 地 方 就 是 它 使 用 的 是 PeekMessage 而 不 是 GetMessage。    GetMessage 與 PeekMessage 的 比 較    ---- 為 什 麼 要 用 PeekMessage 呢 ﹖ 原 因 很 簡 單,GetMessage 等 待 一 個 消 息( 就 像 _getch), 而 PeekMessage 不 是 這 樣( 就 像_kbhit)。 請 考 慮 下 面 的 循 環:    while (GetMessage(&msg, NULL, 0, 0))    { // 我 們 並 不 進 入 括 號 內 部, 直 到 有 一 個 消 息    TranslateMessage(&msg);    DispatchMessage(&msg);    DoSomething()    }    // 當 GetMessage 返 回 NULL 時, 退 出 該 程 序    return msg.wParam;    ---- 在 這 裡,DoSomething 不 會 完 成, 除 非 一 個 消 息-- 或 許 多 消 息-- 被 放 入 隊 列 中 並 被 處 理。 如 果 DoSomething 恰 好 產 生 一 個 消 息, 例 如, 如 果 它 更 新 了 屏 幕 並 且 因 此 而 產 生 了 一 個 WM_PAINT 消 息, 那 麼 好 了, 水 泵 注 水 後 將 開 始 啟 動 了。 要 使 DoSomething 可 靠 地 完 成 其 工 作, 這 並 不 是 一 個 好 方 法, 它 使 代 碼 有 點 混 淆, 但 它 工 作 的 還 不 錯。    ---- 相 比 之 下,PeekMessage 則 無 論 是 否 有 消 息 在 等 待, 只 要 檢 查 一 下 消 息 隊 列 就 完 成 其 操 作(yields the floor)。 在 我 們 的 例 子 中, 我 們 實 際 上 是 使 用 PeekMessage 來 處 理 消 息 的( 通 過 分 發 它 所 找 到 的 每 一 個 消 息 並 使 用 PM_REMOVE 參 數 從 隊 列 中 清 除 它)。 同 下 面 同 樣 有 效 的 代 碼 相 比, 它 要 更 加 直 接:    if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))    {    if (!GetMessage(&msg, NULL, 0, 0)) break;    TranslateMessage(&msg);    DispatchMessage(&msg);    }    else DoSomething();    ---- 在 這 裡 有 非 常 重 要 的 一 點 要 說 明, 我 們 的 偽 代 碼 DoSomething 是 獨 立 於 消 息 的﹔ 無 論 隊 列 中 送 出 的 什 麼 消 息, 甚 至 無 論 有 沒 有 消 息 在 那 兒, 它 都 將 執 行。 在 Moby Dick 中, 我 們 將 屏 幕 更 新 和 勝 利 條 件 的 檢 查 放 在 這 裡, 因 為 在 這 裡 檢 查 一 個 或 多 個 消 息 被 響 應 後 是 否 需 要 更 新 屏 幕 或 是 否 達 到 勝 利 條 件 很 方 便。    ---- 那 麼, 消 息 循 環 就 是 游 戲 循 環 嗎 ﹖ 從 抽 象 的 角 度, 是 的, 因 為 它 是 大 的 齒 輪, 帶 動 那 些 小 的 齒 輪。 但 是, 盡 管 把 一 些 函 數 調 用 放 在 此 處 可 能 比 較 方 便,Windows 編 程 規 則 卻 要 求 任 何 響 應 一 個 消 息 的 動 作 都 應 該 放 在 消 息 響 應 程 序 中( 就 是 說, 放 在 窗 口 過 程 中)。 在 一 個 實 時 游 戲 中, 絕 大 多 數 的 動 作 發 生 在 一 個 或 多 個 WM_TIMER 消 息 響 應 程 序 中。 回 合 制 游 戲 則 常 常 在 輸 入 消 息 的 響 應 函 數 中 做 大 量 的 工 作。    ---- 事 實 上, 程 序 員 經 常 會 發 現 在 主 消 息 循 環 內, 除 了 標 準 的 翻 譯 和 分 發 消 息 任 務 之 外 什 麼 事 情 也 沒 做。 如 果 這 樣, 就 可 以 回 過 頭 來 使 用 GetMessage, 因 為 除 了 響 應 消 息 之 外, 什 麼 事 也 不 需 要 發 生。    ---- 總 結 一 下, 實 現 循 環 有 兩 個 關 鍵 點:    Windows 消 息 循 環 與 游 戲 循 環 不 相 同。 游 戲 循 環 依 然 存 在( 至 少 在 概 念 上 如 此), 但 是 同 DOS 下 的 情 況 相 比, 它 的 部 件 在 代 碼 中 更 加 分 散。    如 果 想 要 在 消 息 循 環 內 執 行 任 何 獨 立 於 時 鐘 消 息 和 輸 入 消 息 的 代 碼, 請 使 用 PeekMessage 而 不 是 GetMessage。     時間就是金錢---[ 發問前請先找找舊文章]
系統時間:2024-04-20 18:13:54
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!