資料來源:網路

 

主講人:(為顧及授課教師之隱私,故不在此公佈其姓名)

 

課程主旨:這門課所講述的是遊戲程式的開發技術與實務

 

講義精要:

 

1. 程式概論

 

● 遊戲的成型:

 

遊戲的成型主要分為表裡兩部分,表面指的是呈現在玩家面前的視覺、聽覺上的感官,裡指的是複雜的資料結構,一般遊戲的畫面成型概念如下:

 

◎ 元件:

 

或稱為 Block、地圖圖素、圖素,泛指在畫面上與主角間有遮罩關係者 (與人物有前後關係)

 

◎ 格線:

 

座標,紀錄各元件的位置

 

◎ 地圖:

 

§ 拼湊模式 (可重複運用)

 

§ Pre-Render 模式 (精緻美觀)

 

§ 90 度角,畫面的感官為正上視圖

 

§ 3D 45 度角,格線為 45 度角傾斜

 

§ 3D,角度任意

 

地圖屬性:

 

○ 可通過與否

 

○ 機關事件

 

◎ 資料結構:

 

支撐遊戲性,上述每一向皆須由企劃輸入,早期 DOS 時代,許多的資料大多由 PE2 等文書編輯器輸入,程式須與企劃人員約定輸入格式,最後程式必須撰寫編碼程式將之匯入遊戲,如:

 

ID 100

 

NAME 張三

 

HP 200

 

MP 100

 

此種方法耗時費力,經常因為企劃人員的筆誤,造成後續遊戲開發的困擾,如 ID 筆誤成 1D。Windows 95 後,程式大多利用 MFC 工具,開發制式的編輯器,取代 PE2 等文書編輯工具的工作,優點是方便企劃、程式在資料建立同時,便可剔除語法、資料重複的基本錯誤,加速遊戲開發速度;缺點是,編輯工具的開發,往往耗日費時,而且佔去遊戲開發時間的多數,對整個遊戲的進度毫無幫助。也有些人以折衷方法,利用 Windows 的 Word、Excel 可以圖形化的編輯器來輸入資料

 

● 與企劃、美術分工:

 

遊戲小組的成員,一般分為程式、企劃與美術人員,真正運作時,有製作人在統籌,調解三方的合作、意見糾紛與進度掌控,另外,比較獨立作業的有音樂音效製作,但就開發而言,程式、企劃與美術三方缺一不可

 

◎ 企劃與程式:

 

企劃發想各個遊戲的驚奇點子,往往是天馬行空的創意,在遊戲開發初期,企劃最重要的工作,是與程式規劃遊戲系統,企劃定出要求,程式就現有技術與時間提供可不可行與替代方案給企劃,就實際開發的例子,往往受限於程式現有技術與時間,許多好的創意便窒礙難行。一但定案,程式便協助企劃訂定各種表單的格式 (或製作編輯工具),讓企劃依循一定的模式,完成一個遊戲的資料輸入,最後,重點是完成主程式的開發,確定雙方同意的創意無誤的在電腦上執行

 

◎ 程式與美術:

 

美術內部的分工,一般分為原畫設計與實作人員。美術通常比較不參與系統的討論,大多是在遊戲定案後,給予企劃與程式美術上的專業諮詢,譬如遊戲圖形採取何種格式、色盤如何安排、風格走向、製作測試圖形,讓程式試做無誤等等,工作量十分的大,一般與程式的互動,小到圖形格式、命名原則的約定,大到遊戲拼圖程式的規劃,這個編輯器也是唯一美術必須參與的,因為圖形製作美術最清楚元件設計時該擺在哪

 

◎ 美術與企劃:

 

兩者的關係互動性很大,企劃無時無刻不關心美術的圖形風格進度,拼好的地圖與企劃的發想是否有落差

 

● C / C++ 概論:

 

C++ 是 C 的延伸,它允許程式設計者撰寫物件導向式的程式,是由 Bjarne Stroustrup 設計,原來的 C 語言於 1978 年由 Brain W.Kernighan 和 Denis M.Ritchie 在 AT&T Bell 實驗室研發而出,C++ 當作物件導向程式語言的一個主要優點是,產生類別物件庫,方便其他專案程式使用、擴充、修改

 

● 其他概念:

 

◎ FPS (Frames Per Second):

 

每秒顯示張數,人的眼睛對快速移動的物體會有視覺暫留的現象,這個數值在電腦上最低大約需要 15 ~ 20 FPS,在這個範圍以下的畫面更新,會讓我們有延遲的感覺,發生這樣的問題時,通常最大的原因在繪圖圖素過多,繪圖的技術不良,或者所選用的程式語言不適所致

 

◎ Pixel:

 

圖素,螢幕上單一點,一般市面上的遊戲 Pixel 規格有分 256 色、16 Bit 色 (Hi-Color) 與 32 Bit 色 (True-Color) 等

 

◎ 遊戲解析度:

 

遊戲畫面的長寬點數,通常以 XXX * XXX 表示,一般常見的解析度為 640 * 480、800 * 600、1024 * 768,畫面解析度的增加,意味著處理時間的加長、FPS 的下降

 

◎ 透明色 (Color-Key):

 

選定一顏色 (或數色),凡圖片上任何一點顏色與之相同,皆會變成透明,此種方法會讓圖形邊緣有銳利的鋸齒出現

 

◎ 透明程度 (Alpha):

 

每一 Pixel 皆設定其透明度,0 表示完全透明,255 表示完全不透明,半透明的顏色會與背景顏色混色,公式如下:

 

Color = Alpha * (Source Color) + (255 - Alpha) * (Destination Color)

 

此種技術由於有混色的效果,邊緣鋸齒狀的現象會消失

 

◎ 抗失真:

 

利用補色的技術,讓鋸齒不那麼明顯的方法

 

◎ Real_Time (即時):

 

指遊戲的進行,不會音玩者任何動作而停頓,節奏明快,多數用在格鬥如鐵拳、快打系列,網路遊戲如 UO、天堂、金庸群俠等

 

◎ Low-Polygon (低面數技術):

 

利用很少的面數形成一物件,形成遊戲畫面,此種技術用於即時 3D 的遊戲,優點在可以很快的 Render 出畫面,缺點在圖形精細度不足,需搭配 2D 材質來修飾,如太空戰士 7 以後系列等

 

◎ High-Polygon (高面數技術):

 

與 Low-Polygon 相反,用數以萬計以上的面數完成單一元件,此種方法為一般美術作圖方式,優點是美觀,缺點是耗時與無法應用於即時 3D 遊戲,通常在遊戲的應用為片頭片尾全螢幕動畫,或者 Pre-Render 出各個精美圖形後,由程式將之放入遊戲畫面中,此類遊戲佔大部分的遊戲市場,如軒轅劍、新蜀山劍俠等

 

◎ Direct X:

 

Microsoft 開發,專為程式發展遊戲專用,其中常用分為 DirectDraw (畫面處理)、DirectSound (音樂音效處理)、Direct3D (3D 功能處理)、DirectInput (操作介面處理)、DirectPlay (網路功能處理)

 

◎ OpenGL:

 

專門處理 3D 遊戲的引擎

 

◎ 網路遊戲:

 

目前分兩大類,一種是點對點的遊戲,最多到 16 人同時連線對打,像世紀帝國、Diablo 等,一種是 Client-Server 架構的千人連線遊戲,像 UO、天堂、金庸群俠傳等等

 

2. 編輯器

 

● 地圖編輯器

 

要件:

 

◎ 地圖解析,企劃必須設定地圖最大與最小的範圍和最高層數,讓程式在規劃成事實 有所依據,美術必須提出功能需求,如 Undo / Layer、測試功能等

 

◎ block 最大圖形與命名規則,以拼湊圖方式的 block 一般都是以 file 的整數倍數 為最大圖形,命名用意在與程式溝通,方便程式能順利將美術圖檔 import 進入編 輯器內,例如 tree0000.bmp,一般而言,block 有兩個要素,一是編號,一是會動 與不動,會動者,又需要設定其總共有幾張動態,如火焰等 (早期的遊戲,多半規定 會動的全部統一張數,如火焰 8 張,飄揚的旗子必須也是 8 張),另外,由於 block 亦需設定貼圖點,此點又稱貼圖基準點

 

◎ 地圖屬性,企劃人員必須規劃出地圖屬性的類別與類別上限,地圖屬性作業方式有兩種, 一種直接放置於 block 上,拼圖的同時就決定了此座標可否通過,一種是在拼完之後, 再在座標上設定此處屬性

 

◎ 遊戲資訊,一張地圖至少需有一唯一 ID 與名字,以供遊戲時期玩家與程式所需

 

● 事件編輯器

 

◎ 旗標,事件影響遊戲流程,與主程式間的連結乃透過程式語言上的旗標 (FLAG) 達成, 旗標對企劃而言乃是一介於 0 ~ X (X 上限由程式企劃合定) 的數字,我們可利用此數 字改變遊戲流程,例如,FLAG1 成立,玩者決定先殺魔王,FLAG3 為主角成功殺死魔王了; 或者藉由 FLAG 控制遊戲狀態,如 FLAG123 成立時沒有隨機戰鬥等,每個 FLAG 企劃皆須告訴程式,有否特殊的功能

 

◎ 事件種類,以操作不同滑鼠觸發事件與玩者腳踏事件,滑鼠觸發事件又分遠端遙控型與近身 操作型態兩類,分述如下:

 

§ 滑鼠觸發: 指遊戲中玩者利用滑鼠點選而引發的事件,如寶箱等

 

○ 遠端遙控:指主角不用移動到事件旁邊,可見即可操作,對程式而言方便撰寫,比較常見於 國產遊戲

 

○ 近身操作:程式必須撰寫一段尋路程式,將主角位置移動到事件旁邊,到達後才可操作事件

 

§ 主角腳踏: 必須主角座標位置與事件重疊而引發此類事件,例如地圖切換點,特殊機關等

 

● 劇情編輯器

 

此處做法各家不一,但是大多數的做法是提供企劃人員眾多的指令集,讓企劃人員去運用組合成想像中的劇情環境,例如,主角發現母親被奸人所害身亡,悲痛萬分,音樂改變、主角走位到母親遺體處,跪下痛哭,此處企劃與美術密切配合,繪出主角、母親各個相關動作

 

劇情編輯器與企劃、主程式的關聯方式,乃透過旗標處理,一個旗標最多掛一個劇情

 

劇情編輯器,指令繁多,指令相關的物件繁多,舉凡人、物品、事件控制、音樂音效等

 

● 物品編輯器

 

物品分為兩類,一是過關用物品或稱劇情物品,一是冒險用物品,冒險用物品一般而言沒有屬性,帶在背包中,用在正確的事件點時,可開啟新的劇情,冒險物品多半跟戰鬥有關,如補血藥、補魔法藥、衣甲、武器等,此類物品企劃必須規劃出相關屬性的細部項目,例如對 MP、HP 等等的影響,注意一事,屬性分為現值與上限兩方的影響,再做資料設定同時必須一併考慮規劃進去

 

● 人物編輯器

 

編寫遊戲中所有的人物資料,包括人物對話、買賣資料、問答等等,另外常見的人物閒逛模式也在此設定

 

● 戰鬥編輯器

 

包括怪物與戰場編輯,戰鬥系統關係遊戲的耐玩性,企劃在規劃時期必須對市面上相關類型的遊戲市場多做了解與調查,避免閉門造車的窘境發生

 

企劃對系統的規劃,主要在設定戰鬥流程、公式、怪物資料輸入,以及平衡性的調整

 

3. 常用的檔案處理函數

 

● DeleteFile:刪除檔案

 

● GetFilePath:取得檔案完整路徑與檔名

 

● GetFileTitle:取得檔案名稱 (不含副檔名)

 

● GetFileName:取得檔案名成 (含副檔名)

 

● FindFile:開始搜尋檔案

 

● GetNextFile:持續搜尋檔案 (之前先呼叫 GetFile)

 

● IsDirectory:檢查搜尋到的檔案是否為資料夾 (FILE_ATTRIBUTE_DIRECTORY)

 

● IsDots:檢查目錄或母目錄 (. 或 ..)

 

4. TGA Header Definition

 

typedef struct _TgaHeader //一共有 18 個 Byte

 

{

 

BYTE nIDLen;

 

BYTE nColorMapType;

 

BYTE nImageType; //編碼模式

 

WORD nPaIMapStart;

 

WORD nPaIMapLen;

 

BYTE nPaIBitDepth;

 

WORD nXOffset;

 

WORD nYOffset;

 

WORD nDx;

 

WORD nDy;

 

BYTE nPixelDepth; //像素色彩深度

 

BYTE nImageDescript;

 

}SxTgaHeader;

 

5. TGA 編碼模式解讀

 

switch(nImageType)

 

{

 

case 0: //No Image (全黑影像)

 

ZeroMemory(m_pImage, m_nDx * m_nDy *m_nByte); //清空記憶體

 

break;

 

case 1: //ColorMap Image (不壓縮調色盤模式)

 

case 2: //True Color Image, No Encode (不壓縮全彩模式)

 

memcpy(m_pImage, pValid, m_nByte);

 

break;

 

case 3: //Mono Color

 

if(m_nByte)

 

{

 

memcpy(m_pImage, pValid, m_nDx * m_nDy * m_nByte);

 

}

 

else

 

{

 

ZeroMemory(m_pImage, m_nDx * m_nDy **(WORD*)(bHead + 16) / 8);

 

}

 

break;

 

case 9: //ColorMap Image, Encode (壓縮調色盤模式)

 

Decode(pValid, m_pImage, m_nByte);

 

break;

 

case 10:

 

case 11:

 

Decode(pValid, m_pImage, m_nByte);

 

break;

 

}

 

6. 載入 Bitmap 影像方式 (摘要)

 

CDIBitmap::CDIBitmap(int dx, int dy)

 

{

 

m_pInfo = NULL;

 

m_pSurface = NULL;

 

m_pRGB = NULL;

 

m_nDx = dx;

 

m_nDy = dy;

 

int nSize = sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);

 

m_pInfo = (LPBITMAPINFO) new BYTE[nSize];

 

memset(m_pInfo, 0, nSize);

 

 

 

BITMAPINFOHEADER * pInfo = (BITMAPINFOHEADER*) m_pInfo;

 

pInfo->biSize = sizeof(BITMAPINFOHEADER);

 

pInfo->biWidth = m_nDx;

 

pInfo->biHeight = (-1) * m_nDy; //Load 進來時是倒圖,所以將其反向

 

pInfo->biPlanes = 1; //其值永遠為 1

 

pInfo->biBitCount = 32; //32 內包含 Alpha

 

pInfo->biCompression = BI_RGB;

 

 

 

m_pSurface = new SxDxColor[m_nDx * m_nDy];

 

FillColor(RGB(0x7F, 0x7F, 0x7F));

 

}

 

7. 讀寫檔方式 (摘要)

 

class CSmartMemFile:public CMemFile

 

{

 

public:

 

inline const BYTE *AccessBuffer();

 

void Attach(BYTE *lpBuffer, UINT nBufferSize, BOOL nAutoErase = TRUE);

 

CSmartMemFile();

 

CSmartMemFile(BYTE *lpBuffer, UINT nSize, BOOL nAutoErase = TRUE);

 

virtual ~CSmartMemFile();

 

BOOL IsEOF();

 

template <class DT> inline CSmartMemFile & operator >> (DT &nGet)

 

{

 

Read(&nGet, sizeof(DT));

 

return(*this);

 

}

 

CSmartMemFile & operator >> (LPCTSTR nGet); //Get String

 

CSmartMemFile & operator >> (CString &nGet); //Get String

 

 

 

template <class DT> inline CSmartMemFile & operator << (DT &nPut)

 

{

 

Write(&nPut, sizeof(DT));

 

return(*this);

 

}

 

CSmartMemFile & operator << (LPCTSTR nPut); //Put String

 

inline CSmartMemFile & operator << (CString &nPut)

 

{

 

operator << ((LPCTSTR)nPut);

 

return(*this);

 

}

 

};

 

8. 產生亂數方式 (摘要)

 

class CRandom

 

{

 

public:

 

inline static int Random(DWORD __num)

 

{

 

return (rand() * __num) / (RAND_MAX + 1);

 

}

 

inline static BOOL IsRate(int nRate)

 

{

 

return(Random(100) < nRate);

 

}

 

inline static int ThrowDice(int nFace)

 

{

 

return(Random(nFace) + 1);

 

}

 

CRandom();

 

virtual ~CRandom();

 

}; //SRAND 重新產生新的 Seed, 避免週期性重複

 

9. 佇列處理 (摘要)

 

template <class DT> class CSmartQueue

 

{

 

protected:

 

CArray <DT, DT> m_pList;

 

int m_nReadPtr;

 

int m_nWritePtr;

 

public:

 

inline CSmartQueue(int nSize) //建構

 

{

 

SetSize(nSize);

 

}

 

inline CSmartQueue(){};

 

//設定佇列大小及移除所有資料

 

inline void SetSize(int nSize)

 

{

 

m_pList.SetSize(nSize);

 

m_nReadPtr = m_nWritePtr = 0;

 

}

 

inline ~CSmartQueue(){}

 

inline BOOL GetObject(DT &pData)

 

{

 

if(!IsEmpty())

 

{

 

pData = m_pList.GetAt(m_nReadPtr);

 

m_nReadPtr++;

 

m_nReadPtr %= m_pList.GetSize();

 

return(TRUE);

 

}

 

return(FALSE);

 

}

 

inline BOOL PutObject(DT pData)

 

{

 

if (!IsFull())

 

{

 

m_pList.SetAt(m_nWritePtr, pData);

 

m_nWritePtr++;

 

m_nWritePtr %= m_pList.GetSize();

 

return(TRUE);

 

}

 

return(FALSE);

 

}

 

inline BOOL operator >> (DT &pData) {return GetObject(pData);}

 

inline BOOL operator << (DT pData) {return PutObject(pData);}

 

inline BOOL IsEmpty()

 

{

 

return(m_nReadPtr == m_nWritePtr);

 

}

 

inline BOOL IsFull()

 

{

 

return((m_nWritePtr + 1) % m_pList.GetSize() == m_nReadPtr);

 

}

 

inline void RemoveAll()

 

{

 

m_nReadPtr = m_nWritePtr;

 

}

 

};

 

10. 堆疊處理 (摘要)

 

template <class DT> class CStack

 

{

 

public:

 

CArray <DT, DT> m_pList;

 

public:

 

CStack(){}

 

CStack(CStack <DT> &st) //Reverse

 

{

 

*this = st;

 

}

 

virtual ~CStack(){}

 

BOOL Push(DT &pData)

 

{

 

m_pList.Add(pData);

 

return(TRUE);

 

}

 

BOOL Pop(DT &pData)

 

{

 

int nSize = m_pList.GetSize();

 

if(nSize == 0) return (FALSE);

 

pData = m_pList.GetAt(nSize - 1);

 

m_pList.RemoveAt(nSize - 1);

 

return(TRUE);

 

}

 

BOOL IsEmpty()

 

{

 

return(m_pList.GetSize() == 0 ? TRUE : FALSE); //Stack Empty

 

}

 

void RemoveAll(){m_pList.RemoveAll();}

 

void operator = (CStack <DT> &dt)

 

{

 

RemoveAll();

 

m_pList.Copy(dt.m_pList);

 

}

 

void Reverse()

 

{

 

for (int count = 0; count < m_pList.GetSize() / 2; count++)

 

{

 

DT pData = m_pList.GetAt(count);

 

m_pList[count] = m_pList[m_pList.GetSize() - count - 1];

 

m_pList[m_pList.GetSize() - count - 1] = pData;

 

}

 

}

 

};

 

11. Timer 設計 (摘要)

 

void CTimer::Breath()

 

{

 

DWORD nTime = GetTickCount();

 

m_nGameClock += nTime - m_nStartGameTick;

 

SxTimeCallBack *pNode = m_pCallBack.GetRoot();

 

CGame *pGame = CGame::GetObject();

 

while(pNode)

 

{

 

if(!pNode->nStatus & FLAG_LOCK) && nTime >= pNode->nNextTimeOut)

 

{

 

pNode->nStatus |= FLAG_TIMEOUT;

 

if(!pNode->pCallBack)

 

{

 

pNode->nNextTimeOut = pNode->nFreq + nTime; //m_nNextTimeOut

 

SxMessage msg = {MSGDT_TIMEOUT, pNode->nID};

 

pGame->m_pMessage << msg;

 

}

 

else

 

{

 

pNode->pCallBack->Breath(pNode->nID, pNode->nFreq);

 

pNode->nNextTimeOut = pNode->nFreq + nTime;

 

//m_nNextTimeOut; //GetTickCount();

 

}

 

}

 

pNode = m_pCallBack.GetChild();

 

}

 

}

 

12. 地圖轉換 (摘要)

 

class CTimeSeed

 

{

 

public:

 

int m_nTSMode; //Type of change map

 

int m_nFlag;

 

public:

 

virtual void GetPosition(int &sx, int &sy, int &dx, int &dy);

 

virtual void Breath(int nID, DWORD &nFreq);

 

CTimeSeed(int nType = TIMESEED_TYPENORM);

 

virtual ~CTimeSeed();

 

}

 

13. 遊戲開發流程

 

14. 主程式程式流程

 

額外補充:

 

1. 編輯器需求大致分為以下幾種

 

● 地圖編輯器

 

● 人物編輯器

 

● 物品編輯器

 

● 戰鬥系統編輯器:包含戰場、怪物 (主角)

 

● 劇情編輯器

 

● 事件編輯器

 

2. Win32 SDK 主用於開發遊戲主程式、MFC 主用於開發介面程式

 

3. TGA 檔案格式是唯一支援 32 bit 的圖檔格式 (R:8 bit、G:8 bit、B:8 bit、Alpha:8 bit)

 

4. 讀檔寫檔相關函數:

 

● 讀檔:sscanf(buf,"%d %s") 或 sscanf(buf, "%d,%s")

 

● 寫檔:cstring() 或 sprintf

 

5. 碰撞偵測通常使用 Bounding Box 來偵測

 

6. 分時系統使用方式,一般因為 SetTimer 優先權較低,有時易被 Windows 略過,故一般使用 GetTickCount()

 

7. Alpha Channel 色表做法

 

● 16 bit:

 

R:0 ~ 32

 

G:0 ~ 63

 

B:0 ~ 32

 

共需 62 * 32 / 1024 = 2 k 記憶體

 

● 24 bit:

 

R:0 ~ 255

 

G:0 ~ 255

 

B:0 ~ 255

 

共需 256 * 256 / 1024 = 64 k 記憶體

 

8. 一般顯示卡對 16 bit 色彩編碼方式分為以下兩種

 

● R:G:B = 5:6:5

 

● R:G:B = 5:5:5

 

9. DIB (Device Independent Bitmap)

 

10. template 必須放在 Header File 中

 

11. 移動記憶體函數的差異

 

● MoveMemory:記憶體不會重疊

 

● MemCpy:記憶體會重疊

 

12. 定義一個 Debug 版本判斷式的方法

 

#ifdef_DEBUG

 

ASSERT (count < 10)

 

#endif

 

13. 更新速度的基本設定

 

● 螢幕更新速度:20 fps / sec (1 sec = 1,000 msec = 20 fps => 1fps = 50 ticks)

 

● 人物速度:20 fps / sec

 

14. 鍵盤滑鼠事件 Message 的捕捉可用 Hook 去監視

 

作業:

 

將所有中文字 Dump 到文字檔 → C++ Source Code And Exe File 

 

arrow
arrow
    全站熱搜

    sleepingwolf 發表在 痞客邦 留言(0) 人氣()