Aug 04 2008

物件導向設計新思維:深入Policy-Based Class Design新大陸

自前一篇「物件導向設計新思維:探索Policy-Based Class Design新視界」發佈以來,已經過了很長的一段時間。在這篇千呼萬喚始出來的續集裡,將會進一步深入探索 Policy-Based Class Design 背後的設計概念與理論,以及在實作層面上經常會遭遇到的問題;最後,以一個遊戲系統中常見的音源引擎 (Audio Engine) 類別設計為範例,做為本文的片尾曲目。

記得曾經有人問過比爾蓋茲,如果有天他被迫一人獨自在荒島上生活,身旁只有一樣物品陪伴的話,最想要什麼東西?他的回答是:「一部電腦與編譯器。」許多程式設計者心裡所嚮往的美好世界,就是那種無拘無束、無邊無際的自由;好像只要手指還能夠活動、頭腦還能夠寫程式,天底下就沒有辦不到事情!只是,有時候這種無限度的自由,反而會對程式系統造成負面的影響。

《C++ 設計新思維》(Modern C++ Design) 書中的第一章,就對於程式系統的設計就開宗明義地闡述:

理想上,一個良好設計應該在編譯期強制表現出大部分 constraint(約束條件、規範)。

身為一位程式設計者,或多或少都有把自己的程式碼交給其他程式設計者使用的經驗,相對於「程式撰寫者」來說,所謂的「程式使用者」,就是那些使用你所撰寫的程式碼的人。就像是遊戲程式中經常使用的 DirectX、OpenGL 或者 .NET Framework 以及遊戲函式庫與引擎等等,我們都是身為「程式使用者」的身份。

繼續閱讀 << "物件導向設計新思維:深入Policy-Based Class Design新大陸"


Jul 05 2008

初探Nintendo DS程式開發與設計(四):做個拼貼藝術大師

由之前一系列的三篇文章中,已經一步步地瞭解了如何在 NDS 平台上使用 Framebuffer 背景模式、Bitmap 背景模式,以及 Sprite 物件功能,接著本篇將再次深入與遊戲製作息息相關的背景顯示功能,介紹在真實的遊戲程式應用中,更加實用的圖塊背景 (Tiled Background) 模式

前情提要

由於硬體架構上的先天限制,在 NDS 平台中,沒有如同個人電腦平台上的全彩 (32 bits) 或高彩 (24 bits) 顯示模式可以使用;在這個小小的掌上世界中,所有的繪圖物件只能夠使用 16 bits、8 bits 或 4 bits 的色彩顯示模式。而如前篇文章的內容所提,Sprite 物件能夠使用 16 色 (4 bits) 與 256 色 (8 bits) 兩種模式,對於背景圖件來說亦然如此。在 Sprite 的功能中,可以選擇使用 OBJCOLOR_16 或 OBJCOLOR_256 這兩項色彩模式,而在背景圖件中,也有相對應於 16 色與 256 色模式的 BG_COLOR_16 與 BG_COLOR_256 定義。

瞭解了色彩模式的種類之後,就可以開始進入圖塊 (Tile) 的世界了。很久很久以前,在那個電腦記憶體仍然十分珍貴而稀少的年代裡,多數的家用遊樂器主機,都只配備著非常有限的記憶體容量。而對於掌上型主機來說,記憶體的限制則更為顯著。因此,我們無法使用多張高解析度的點陣圖 (Bitmap) 做為遊戲中的背景圖片,所以在 NDS 上存在著一種特別的繪圖顯示模式,能夠以一組相同大小的圖塊,拼貼組合成為完整的遊戲背景圖片。因此,遊戲開發者就可以利用類似磁磚拼貼的方式,由有限數量的小型圖塊,組合出變化多端的大型圖片。

繼續閱讀 << "初探Nintendo DS程式開發與設計(四):做個拼貼藝術大師"


Jun 15 2008

初探Nintendo DS程式開發與設計(三):二維世界小精靈

延續前篇文章中所介紹的 Bitmap 背景圖件顯示功能後,本篇開始進入 2D 圖件 (Sprite) 的二維世界。所謂的 Sprite,原意是指「調皮搗蛋的小精靈」,但是在電腦繪圖與遊戲程式的領域中,Sprite 一詞則被用來當作二維繪圖物件的代名詞:

Sprite is the term used to describe a 2D graphics with a number of related functions.

nds-dev-sprite從前,在那個只有 X 軸與 Y 軸的平面世界中,Sprite 物件就是一切事物的根基,能夠用來表示角色人物、怪物敵人以及武器裝備等許多不同的遊戲物件。Sprite 可以只是一張單純的靜態圖片,也可以由數張圖片組合成為動畫的形式。簡單來講,Sprite 不過是一張可以移動與旋轉的 2D 貼圖罷了。

以 DirectX 為例,在 D3D 中已經包含了 Sprite 的物件功能,能夠使用 LPD3DXSPRITE 與 D3DXSprite() 輕易地創建出 Sprite 物件。即使目前許多平台上的遊戲,都已經趨向全 3D 型態的呈現方式,但是如果能夠應用得宜的話,傳統的 Sprite 物件繪圖模式仍然有很多的可能性存在。例如著名的線上遊戲《仙境傳說》,就是以 2D Sprite 人物,搭配 3D 型態場景的絕佳實例。

如果之前有接觸過 GBA 程式開發與設計知識的讀者,應該能夠很容易發現 NDS 平台與 GBA 平台的硬體架構與核心功能,其實擁有許多的相同之處。任天堂公司保留了原本設計良好的 GBA 架構,然後更進一步延伸,加入了 3D 繪圖、觸控操作、雙處理器引擎等等進階的硬體架構與功能。但是在 2D 繪圖的處理層面,仍然沿襲 GBA 平台的各種設定以及使用方法。而 Sprite 的許多定義與操作行為,也都是由 GBA 時代流傳下來的概念,因此在 NDS 平台上,Sprite 同樣佔了舉足輕重的關鍵地位。

繼續閱讀 << "初探Nintendo DS程式開發與設計(三):二維世界小精靈"


May 26 2008

向食人魔怪說嗨:與OGRE的第一類接觸

OGRE最近花費了大把的時間,努力地學習如何與 OGRE 這隻聲名遠播的巨獸成為好麻吉。至目前為止,總算是能夠稱得上稍微和它混熟了一點。關於 OGRE,這裡提出幾點我的學習筆記、目前觀察到的個性特質,以及與它相處的心得感想。

首先,對於稍具經驗的程式設計者來說,見到 OGRE 的第一眼印象,應該莫過於各種設計模式 (Design Pattern) 的廣泛使用了。在 OGRE 的架構設計與系統實作層面中,使用了 Abstract FactoryFactory MethodSingletonIteratorObserver 以及 Manager 等等許多著名的設計模式。說實在的,我還是頭一次看到這麼多不同種類的設計模式,能夠同時應用在一個遊戲繪圖引擎之中,正好能夠與以前所學的理論知識相互印證,學習起來非常爽快也很感動,真是令我大開眼界、大呼過癮!

而其中,最顯而易見的設計模式就是 SingletonManager 的結合使用。OGRE 利用這兩項模式的組合,製造出了一位一位各有擅場的「專業經理人」,使各個子系統所負的權責,都能夠劃分的非常清楚仔細。另外,利用 Observer 的概念,讓使用者能夠繼承 FrameListener 類別,自行定義進行繪圖程序前後的相關處理程序,也是一個非常優秀的設計模式應用實例。

回想自己當初剛進入業界時,在沒有任何人使用這些設計模式的情況下,額外看了許多書籍網站的資料,最後終於利用 Singleton 與 Manager 模式實作出幾項遊戲專案的子系統組件。結果深入認識了 OGRE 以後,才發現原來這些觀念已經如此廣泛地運用在遊戲引擎的設計架構之中。正所謂「他山之石,可以攻錯」,對於程式設計者來說,吸收新知以及學習別人的長處,的確是十分關鍵而不可或缺的能力啊。

繼續閱讀 << "向食人魔怪說嗨:與OGRE的第一類接觸"


May 08 2008

初探Nintendo DS程式開發與設計(二)

nds-dev-bitmap自從年代久遠的「初探Nintendo DS程式開發與設計(一)」問世以後,經過了漫長的兩個月等待,新的續篇終於不負眾望地(?)火熱出爐囉!這篇文章將進入 Nintendo DS 的內心世界,深入探索它在樸實外表下所隱藏的強大能力。然後,同樣地以一個簡單的範例介紹基礎的背景圖件顯示功能。

首先要瞭解的是 DS 的硬體架構。

DS 是一個擁有雙中央處理器 (CPU) 的系統,兩個處理器分別是 ARM 9 以及 ARM 7;ARM 9 是一個 32-bits 架構 66 MHhz 速度的處理器,而 ARM 7 是一個 32-bits 架構 33 MHhz 速度的處理器。DS 擁有 4 MB 的主記憶體容量,總計 656 KB 的顯示記憶體容量,以及數個大小不等的快取記憶體;詳細的內容配置,請參照這張 DS 的記憶體架構圖。除了核心架構以外,當然還有音效硬體、麥克風、觸控螢幕、主機按鍵、GBA 卡匣槽,以及 Wi-Fi 網路的支援能力。

不同於 PC 平台上動輒成千上百的記憶體容量,整個 DS 的主記憶體只有 4 MB 的大小,而相當重要的顯示記憶體更是只有 656 KB 的容量;因此,在 DS 上對於任何資源的配置行為,都需要精打細算地評估考量。ARM 9 與 ARM 7 都能夠自由存取主記憶體,而主記憶體通常會用來存放 ARM 9 所使用的遊戲執行檔,以及與遊戲相關的實體檔案。至於 ARM 7 的執行檔,雖然也能夠放在主記憶體中,但是為了效能上的考量,devkitPro 預設的行為會將執行檔放在快取記憶體 IWRAM 之中。另外,DS 的觸控螢幕輸入功能,是交由 ARM 7 所處理的;在 libnds 的預設執行檔中,ARM 7 會在每次主迴圈更新時,讀取觸控螢幕的相關資料,並且將這些資料存入 ARM 9 也能夠讀取的地方。

繼續閱讀 << "初探Nintendo DS程式開發與設計(二)"


Apr 23 2008

物件導向設計新思維:探索Policy-Based Class Design新視界

Policy-Based Class Design 到底是什麼樣的東西?又能夠用來解決什麼樣的問題?本文將由一個在程式專案中常見的現實案例,帶領讀者進入 Policy-Based Class Design 的全新思維領域。

學習一項新的思維方法或實作技術,最容易的方法就是從需求面出發。所以在介紹 Policy-Based Design 之前,先來看看一個現實中經常會遇到的程式設計案例:假設在處理 Input/Output 功能的系統層,原來存在著 BinaryReader 與 BinaryWriter 兩個類別,分別用來讀取以及寫入二進位制的資料串流。

現在,在新的專案項目中,我們希望能夠利用這兩個既存的 IO 類別,但是又不希望將檔案的讀取與寫入動作,分成 BinaryReader 與 BinaryWriter 兩個不同的類別進行處理。因此,程式設計者需要設計一個新的 IO 管理者類別,將原有的讀取與寫入功能合併在一起,以達到集中操作以及檔案資源管理的功用。

而為了盡量減少對既存程式碼的修改,其中一種設計方法是使新的 IOManager 類別,同時繼承自 BinaryReader 與 BinaryWriter 類別,然後藉由虛擬函式的宣告,修改專案所需的行為程序:

class IOManager
    :
    public BinaryReader,
    public BinaryWriter
{
public:
    virtual void Read(); // 覆寫自 BinaryReader
    virtual void Write(); // 覆寫自 BinaryWriter
}

這樣設計的方法確實能夠在不修改原有類別的前提下,達到 IO 系統介面集中的目的。程式系統的撰寫者無須再同時照料 BinaryReader 與 BinaryWriter 兩個類別,只要一個 IOManager 類別就能夠處理 IO 層面的功能操作。

繼續閱讀 << "物件導向設計新思維:探索Policy-Based Class Design新視界"


Apr 12 2008

多核多緒多樂趣

「多核心處理器有前途嗎?」幾年前,或許只是單純的疑問句,而到了今時今日,無庸置疑地已經成為了肯定句。

有沒有注意到,曾經每年都在急速成長的 CPU 處理速度,已經停留在 2 至 3G MHz 很久的一段時間了呢?在個人電腦的硬體配備中,已經由追求 CPU 的速度提升,轉向追求核心數量的增加。主流的 CPU 規格也逐漸由雙核心進步到四核心、六核心,甚至是未來的八核心中央處理器。「多核心」聽起來很酷,而這個所謂的多核心處理器的意義何在?

「Programming for Parallelism」中,共有 8 段配上中文字幕的影片,由 Intel 公司的 James Reinders 由淺至深地簡介了多核心硬體發展的必要性,以及相關程式應用技術的入門概念。其中在第一段的影片內容中,提到關於提升處理器時鐘頻率的作法,有幾項很關鍵的問題點:

  • 繼續往上提升處理器的時鐘頻率有什麼問題?功率是我們最常聽見的說法。提高處理器的時鐘頻率,對於執行速度的提升會越來越不明顯;也許性能提升了 13%,但是功率的卻多耗損了 73%。所以如果我們繼續提升處理器的時鐘頻率,一來電費不划算,二來使用 CPU 煎牛排再也不是夢想。
  • 另一個問題在於記憶體。過去幾年間,CPU 很快樂的以倍速飛快地成長著,然而在現實世界的硬體市場中,記憶體的時鐘頻率已經遠遠地跟不上處理器時鐘頻率的提升。就算處理器的動作再快,也得從記憶體拿取資料,並且將計算完畢的資料恭恭敬敬地交回給記憶體才行,所以在記憶體速度跟不上的情況下,處理器運算速度再快也只能空轉等待。
  • 還有一個問題點在於可平行處理的指令集。能夠進行平行處理的指令集,已經被開發利用得差不多了,接下來即使不斷提升處理器的速度,也不能保證得到更好更快的執行效能,而只是徒增更多 CPU 閒置發呆的時間。

繼續閱讀 << "多核多緒多樂趣"


Mar 31 2008

表頭檔要不要?拿速度來換!

本文要提出的內容,是一個非常基礎的程式觀念:表頭檔 (Header Files) 與編譯依存關係的課題。雖然對於許多程式設計者來說,這個觀念技巧應該是相當基本而且必備的基礎知識,但在現實世界中,我卻時常看到許多人忽略了這個問題的重要性,因而造成專案開發流程中的重大災難。

來看看實際的範例。例如在專案中有一個 GameObject 類別,用來實作遊戲物件的相關操作行為:

// @ GameObject.h
#include < string >

class GameObject
{
public:
    int GetType();
    std::string& GetName();

private:
    int m_iType;
    std::string m_kName;
};

有了基礎的遊戲物件類別之後,自然會陸續建構並且實作出其他的類別。當 GameObject 需要使用或包含其他的類別時,可能會在類別中定義一個該類別的成員變數,以結合 GameObject 類別進行更複雜的操作行為。例如,可能會在 GameObject 類別中,加入一個 ComponentA 類別的物件定義遊戲物件的基本組件:

// @ GameObject.h
#include < string >
#include "ComponentA.h"

class GameObject
{
public:
    int GetType();
    std::string& GetName();
    ComponentA& GetComponentA();

private:
    int m_iType;
    std::string m_kName;
    ComponentA m_kCompA;
};

繼續閱讀 << "表頭檔要不要?拿速度來換!"


Mar 25 2008

Abstract Factory:物件家族的抽象工廠

Abstract Factory UML本篇文章將承續前篇「Factory Method:工廠化的物件生產方法」,介紹經常與 Factory Method 模式搭配使用的 Abstract Factory 設計模式,以及 Abstract Factory 模式於遊戲程式中的應用實例。

在複雜的軟體系統架構中,將原來雜亂無章的組件加以分門別類,然後從中取出性質相似與功能相近的物件,使其集中群聚,就能夠大幅地簡化程式系統組件的複雜度。而 Abstract Factory 模式,正是用來協助程式設計者將物件分門別類與集中管理的好方法。

Abstract Factory 的「工廠」,與 Factory Method 的「工廠」目的相同,同樣屬於「生成模式」的分類,都是用來產生出物件成品的製造者。對於設計模式的入門者來說,很容易將 Factory Method 模式與 Abstract Factory 模式兩者的使用目的以及使用時機搞混;雖然都是屬於工廠類型的作業模式,然而 Abstract Factory 與 Factory Method 的不同之處在於,抽象工廠模式是將一組性質相關或相依的物件,放在同一個工廠裡生產。例如對於食品工廠來說,能夠生產的包含飲料、零食與泡麵三項食品類的產品;而皮件工廠,則能夠生產出皮包、皮帶與皮鞋等皮製產品。也就是在一個工廠之內有數個不同的生產線,能夠同時進行不同成品的生產作業。在實際程式系統的應用中,可以說 Abstract Factory 經常是一堆 Factory Method 的組合

繼續閱讀 << "Abstract Factory:物件家族的抽象工廠"


Mar 21 2008

Google Summer of Code:貢獻,收穫,然後成長。

Google Summer of Code Map2008 年的 Google Summer of Code 活動正如火如荼地展開當中。

所謂的 Google Summer of Code (GSoC) 是由 Google 提供贊助經費,推動在學學生進行「開源專案」(Open Source Projects) 開發工作的活動計畫。這項計畫自 2005 年起每年舉辦,今年已經是 Google 第四度舉辦這樣兼具實質意義與精神意義的活動。

開源專案的發展,一直在全世界的軟體產業中佔有相當重要的地位。而在遊戲業界中,則以 John CarmackQuake 系列作品,做為原始碼開放精神的首選典範。如果沒有了這些專案開發者與貢獻者的無私奉獻,現在的我們就不會有免費的 LuaWordpressMySQLPHPOGRE 以及許許多多功能強大的軟體系統可用。

根據 GSoC 2008 FAQ 中開宗明義所闡述,發起這項活動計畫有下列五項目的:

  • 使更多的開源專案能夠被創造出來。
  • 鼓勵年輕的開發者,開始參與開源專案的開發。
  • 幫助開源專案找到並且帶來新的開發者與貢獻者。
  • 提供資訊科系的學生,一個在相關領域工作的機會(而不是去做漢堡)。
  • 給學生更多接觸真實軟體開發情境的機會。

繼續閱讀 << "Google Summer of Code:貢獻,收穫,然後成長。"