歡迎來(lái)到32課. 這課大概是在我所寫(xiě)作已來(lái)最大的一課. 超過(guò)1000 行代碼和約1540行的HTML. 這也是第一課用到我新的NeHeGL 基本代碼. 這課寫(xiě)了很長(zhǎng)時(shí)間, 但我想他是值得期待的. 一些知識(shí)點(diǎn)用到是: Alpha 混合, Alpha 測(cè)試, 讀取鼠標(biāo), 同時(shí)用到Ortho 和透視, 顯示客戶鼠標(biāo), 按深度排列物體, 動(dòng)畫(huà)幀從單張材質(zhì)圖 和更多要點(diǎn), 你將學(xué)到更多精選的內(nèi)容!
最初的版本是在屏幕上顯示三個(gè)物體,當(dāng)你單擊他們將改變顏色. 很有趣!?! 不怎樣! 象往常一樣, 我想給你們這些家伙留下一個(gè)超極好的課程. 我想使課程有趣, 豐富,當(dāng)然..美觀. 所以, 經(jīng)過(guò)幾個(gè)星期的編碼之后, 這課程完成了! 即使你不編碼,你仍會(huì)喜歡這課. 這是個(gè)完整的游戲. 游戲的目標(biāo)是射擊更多的靶子, 在你失去一定數(shù)的靶子后,你將不能再用鼠標(biāo)單擊物體.
我確信會(huì)有批評(píng),但我非常樂(lè)觀對(duì)這課! 我已在從深度里選擇和排序物體這個(gè)主題里找到快樂(lè)!
一些需要注意的代碼. 我僅僅會(huì)在lesson32.cpp里討論. 有一些不成熟的改動(dòng)在 NeHeGL 代碼里. 最重要的改動(dòng)是我加入鼠標(biāo)支持在 WindowProc(). 我也加入 int mouse_x, mouse_y 在存鼠標(biāo)運(yùn)動(dòng). 在 NeHeGL.h 以下兩條代碼被加入: extern int mouse_x; & extern int mouse_y;
課程用到的材質(zhì)是用 Adobe Photoshop 做的. 每個(gè) .TGA 文件是32位圖片有一個(gè)alpha 通道. 若你不確信自已能在一個(gè)圖片加入alpha通道, 找一本好書(shū),上網(wǎng),或讀 Adobe Photoshop幫助. 全部的過(guò)程非常相似,我做了透明圖在透明圖課程. 調(diào)入你物體在 Adobe Photoshop (或一些其它圖形處理程序,且支持alpha 通道). 用選擇顏色工具選你圖片的背景. 復(fù)制選區(qū). 新建一個(gè)圖. 粘貼生成新文件. 取消圖片選擇,你圖的背景應(yīng)是黑色. 使周?chē)前咨? 選全部圖復(fù)制. 回到最初的圖且建一個(gè)alpha 通道. 粘貼黑和白透明圖你就完成建立alpha通道.存圖片為32位t .TGA文件. 使確定保存透明背景是選中的,保存!
如以往我希望你喜歡這課程. 我感興趣你對(duì)他的想法. 若你有些問(wèn)題或你發(fā)現(xiàn)一些問(wèn)題,告訴我. 我匆忙的完成這課程 所以若你發(fā)現(xiàn)哪部分很難懂,給我發(fā)些郵件,然后我會(huì)用不同的方式或更詳細(xì)的解釋!
#include <windows.h>
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include "NeHeGL.h"
在第1課里, 我提倡關(guān)于適當(dāng)?shù)姆椒ㄟB接到 OpenGL 庫(kù). 在 Visual C++ 里點(diǎn)擊’項(xiàng)目’,設(shè)置,連接項(xiàng). 移下到 對(duì)象/庫(kù) 模塊 加入 OpenGL32.lib, GLu32.lib 和 GLaux.lib. 預(yù)編譯一個(gè)需要的庫(kù)的失敗將使編譯器找出所出的錯(cuò)誤. 有時(shí)你不想發(fā)生! 使事情更壞, 若你僅僅預(yù)編譯庫(kù)在debug 模式, 和有人試在release 模式建立你程序... 更多的錯(cuò)誤. 有許多人看代碼. 他們大多數(shù)是新程序員. 他們?nèi)〉侥愕拇a, 試著編譯. 他們得到錯(cuò)誤, 刪掉代碼,移走.
下而的代碼告訴編譯者去連接需要的庫(kù). 一點(diǎn)多些的字, 但少些以后的頭痛. 在這個(gè)課程, 我們將連接 OpenGL32 庫(kù),GLu32庫(kù) 和 WinMM庫(kù) (用來(lái)放音樂(lè)). 在這課程我們會(huì)調(diào)入 .TGA 文件,所以我們不用 GLaux庫(kù).
#pragma comment( lib, "opengl32.lib" ) // 在鏈接時(shí)連接Opengl32.lib庫(kù)
#pragma comment( lib, "glu32.lib" ) // 鏈接glu32.lib庫(kù)
#pragma comment( lib, "winmm.lib" ) // 鏈接winmm.lib庫(kù)
下而的3 行檢查若 CDS_FULLSCREEN 已被你的編譯器定義. 若還沒(méi)被定義, 我們給 CDS_FULLSCREEN 為 4. 馬上你完全部丟掉... 一些編譯器不給 CDS_FULLSCREEN 變量,將返回一個(gè)錯(cuò)誤,但是 CDS_FULLSCREEN 是有用的! 防止出錯(cuò)消息, 我們檢查若 CDS_FULLSCREEN 是否定義,若出錯(cuò), 我們定義他. 使每人生活更簡(jiǎn)單.
我們?cè)俣x DrawTargets函數(shù), 為窗口和按鍵設(shè)變量. 你若不懂定義,讀一遍MSDN術(shù)語(yǔ)表.保持清醒, 我不是教 C/C++, 買(mǎi)一本好書(shū)若你對(duì)非gl代碼要幫助!
#ifndef CDS_FULLSCREEN
#define CDS_FULLSCREEN 4
#endif
void DrawTargets();
GL_Window* g_window;
Keys* g_keys;
下面的代碼是用戶設(shè)置變量. base 是將用到的字體顯示列表的開(kāi)始列表值. roll 是將用到的移動(dòng)的大地和建立旋轉(zhuǎn)的云. level 應(yīng)是級(jí)別 (我們開(kāi)始是 1級(jí)). miss 保留失去了多少物體. 他還用來(lái)顯示用戶的士氣(不丟失意味著高士氣). kills 保留每級(jí)打到多少靶子. score 會(huì)保存運(yùn)行時(shí)打中的總數(shù), 同時(shí)用到結(jié)束比賽!
最后一行是讓我們通過(guò)結(jié)構(gòu)比較的函數(shù). 是等待qsort 最后參數(shù)到 type (const *void, const *void).
// 用戶定義的變量
GLuint base; // 字體顯示列表
GLfloat roll; // 旋轉(zhuǎn)的云
GLint level=1; // 現(xiàn)在的等級(jí)
GLint miss; // 丟失的數(shù)
GLint kills; // 打到的數(shù)
GLint score; // 當(dāng)前的分?jǐn)?shù)
bool game; // 游戲是否結(jié)束?
typedef int (*compfn)(const void*, const void*); // 定義用來(lái)比較的函數(shù)
現(xiàn)在為我們物體的結(jié)構(gòu). 這個(gè)結(jié)構(gòu)存了所有一個(gè)物體的信息. 旋轉(zhuǎn)的方向, 若被打中, 在屏幕的位置, 等等.
一個(gè)快速運(yùn)動(dòng)的變量... rot 我想讓物體旋轉(zhuǎn)特別的方向. hit 若物體沒(méi)被打中將是 FALSE . 若物體給打中或飛出, 變量將是 TRUE.
變量frame 是用來(lái)存我們爆炸動(dòng)畫(huà)的周期. 每一幀改變?cè)黾右粋€(gè)爆炸材質(zhì). 在這課有更多在不久.
保存單個(gè)物體的移動(dòng)方向, 我們用變量 dir. 一個(gè)dir 能有4 個(gè)值: 0 - 物體左移, 1 - 物體右移, 2 - 物體上移 和最后 3 - 物體下移
texid 能是從0到4的數(shù). 0 表示是藍(lán)面材質(zhì), 1 是水桶材質(zhì), 2 是靶子的材質(zhì) , 3 是 可樂(lè)的材質(zhì) 和 4 是 花瓶 材質(zhì). 最近在調(diào)入材質(zhì)的代碼, 你將看到先前5種材質(zhì)來(lái)自目標(biāo)圖片.
x 和 y 兩者都用來(lái)記屏模上物體的位置. x 表示物體在 x-軸, y 表示物體在 y-軸.
物體在z-軸上的旋轉(zhuǎn)是記在變量spin. 在以后的代碼, 我們將加或減spin基數(shù)在旅行的方向上.
最后, distance 保存我們物體到屏幕的距離. 距離是極端重要的變量, 我們將用他來(lái)計(jì)算屏幕的左右兩邊, 而且在對(duì)象關(guān)閉之前排序物體,畫(huà)出物體的距離.
struct objects {
GLuint rot; // 旋轉(zhuǎn) (0-不轉(zhuǎn), 1-順時(shí)針轉(zhuǎn), 2-逆時(shí)針轉(zhuǎn))
bool hit; // 物體碰撞?
GLuint frame; // 當(dāng)前爆炸效果的動(dòng)畫(huà)幀
GLuint dir; // 物體的方向 (0-左, 1-右, 2-上, 3-下)
GLuint texid; // 物體材質(zhì) ID
GLfloat x; // 物體 X 位置
GLfloat y; // 物體 Y 位置
GLfloat spin; // 物體旋轉(zhuǎn)
GLfloat distance; // 物體距離
};
解釋下面的代碼沒(méi)有真正的結(jié)果. 我們?cè)谶@課調(diào)入TGA圖代替bitmaps圖片. 下面的用來(lái)表示TGA圖片數(shù)據(jù)的結(jié)構(gòu)是盡可能好的 . 若你需要詳細(xì)的解釋下面的代碼,請(qǐng)讀關(guān)于調(diào)入TGA 文件的課程.
typedef struct // 新建一個(gè)結(jié)構(gòu)
{
GLubyte *imageData; // 圖片數(shù)據(jù) (最大 32 位)
GLuint bpp; // 圖片顏色深度 每象素
GLuint width; // 圖片寬度
GLuint height; // 圖片高度
GLuint texID; // 貼圖材質(zhì) ID 用來(lái)選擇一個(gè)材質(zhì)
} TextureImage; // 結(jié)構(gòu) 名稱
緊接下面的代碼為們10個(gè)材質(zhì)和個(gè)30物體留出空間. 若你打算在游戲里加更多物體,得增加這個(gè)變量到你想到的數(shù)
TextureImage textures[10]; // 定義10個(gè)材質(zhì)
objects object[30]; // 定義 30 個(gè)物體
我不想限制每個(gè)物體的大小. 我想瓶子(vase)比can高, 我想水桶bucket比瓶子寬. 去改變一切是簡(jiǎn)單的, 我建了一個(gè)結(jié)構(gòu)存物體的寬和高.
我然后在最后一行代碼中設(shè)每個(gè)物體的寬高. 得到這個(gè)coke cans的寬, 我將檢查size[3].w. 藍(lán)面是 0, 水桶是 1, 和靶子是 2, 等. 寬度表現(xiàn)在 w. 使有意義?
struct dimensions { // 物體維數(shù)
GLfloat w; // 物體寬
GLfloat h; // 物體高
};
// 每個(gè)物體的大小: 藍(lán)面, 水桶, 靶子, 可樂(lè), 瓶子
dimensions size[5] = { {1.0f,1.0f}, {1.0f,1.0f}, {1.0f,1.0f}, {0.5f,1.0f}, {0.75f,1.5f} };
下面是大段代碼是調(diào)入我們 TGA 圖片和轉(zhuǎn)換他為材質(zhì). 這是同我在第25課所用的一樣的代碼,你可回去看一看.
我用TGA 圖片的原因是他們是有alpha 通道的. 這個(gè)alpha 通道告訴 OpenGL 哪一部分圖是透明的,哪一部分是白底. alpha 通道是被建立在圖片處理程序, 并保存在.TGA圖片里面. OpenGL 調(diào)入圖片, 能用alpha 通道設(shè)置圖片中每個(gè)象素透明的數(shù)量.
bool LoadTGA(TextureImage *texture, char *filename) // 調(diào)入一個(gè)TGA 文件到內(nèi)存
{
GLubyte TGAheader[12]={0,0,2,0,0,0,0,0,0,0,0,0}; // (未)壓縮的 TGA 頭
GLubyte TGAcompare[12]; // 用來(lái)比較 TGA 頭
GLubyte header[6]; // 首先 6 個(gè)有用的字節(jié)
GLuint bytesPerPixel; // 每象素字節(jié)數(shù)在 TGA 文件使用
GLuint imageSize; // 用來(lái)圖片大小的存儲(chǔ)
GLuint temp; // 臨時(shí)變量
GLuint type=GL_RGBA; // 設(shè)置默認(rèn)的 GL 模式 為 RBGA
FILE *file = fopen(filename, "rb"); // 打開(kāi) TGA 文件
if( file==NULL || // 文件是否已存在 ?
fread(TGAcompare,1,sizeof(TGAcompare),file)!=sizeof(TGAcompare) || // 是否讀出12個(gè)字節(jié)?
memcmp(TGAheader,TGAcompare,sizeof(TGAheader))!=0 || // 文件頭是不是我們想要的 ?
fread(header,1,sizeof(header),file)!=sizeof(header)) // 若正確則讀下 6 個(gè) Bytes
{
if (file == NULL) // 文件是否已存在 ?
return FALSE; // 返回錯(cuò)誤
else // 否則
{
fclose(file); // 若有任何錯(cuò)誤, 關(guān)掉文件
return FALSE; // 返回錯(cuò)誤
}
}
texture->width = header[1] * 256 + header[0]; // 定義 TGA 寬
texture->height = header[3] * 256 + header[2]; // 定義 TGA 高
if( texture->width <=0 || // 若 寬<=0
texture->height <=0 || // 若 高<=0
(header[4]!=24 && header[4]!=32)) // 若 TGA 是 24 or 32 位?
{
fclose(file); // 若有任何錯(cuò)誤, 關(guān)掉文件
return FALSE; // 返回錯(cuò)誤
}
texture->bpp = header[4]; // 取 TGA 的位每象素 (24 或 32)
bytesPerPixel = texture->bpp/8; // 除以 8 得到字節(jié)每象素
imageSize = texture->width*texture->height*bytesPerPixel; // 計(jì)算 所需內(nèi)存為 TGA 數(shù)據(jù)
texture->imageData=(GLubyte *)malloc(imageSize); // 分配 內(nèi)存 為 TGA 數(shù)據(jù)
if( texture->imageData==NULL || // 這個(gè)內(nèi)存是否存在?
fread(texture->imageData, 1, imageSize, file)!=imageSize) // 圖片大小與保留內(nèi)存的大小想等 ?
{
if(texture->imageData!=NULL) // 圖片數(shù)據(jù)的調(diào)入
free(texture->imageData); // 若成功, 釋放圖象數(shù)據(jù)
fclose(file); // 關(guān)掉文件
return FALSE; // 返回錯(cuò)誤
}
for(GLuint i=0; i<int(imageSize); i+=bytesPerPixel) // 在圖象數(shù)據(jù)里循環(huán)
{ // 交換第1和第3 Bytes (’紅’red 和 ’藍(lán)’blue)
temp=texture->imageData[i]; // 臨時(shí)存儲(chǔ) 圖象的 ’i’
texture->imageData[i] = texture->imageData[i + 2]; // 設(shè) 第1 Byte 得到變量 第3 Byte
texture->imageData[i + 2] = temp; // 設(shè)第3 Byte 得到變量 ’temp’ (第1 Byte 變量)
}
fclose (file); // 關(guān)掉文件
// 建立一個(gè)貼圖材質(zhì)從以上數(shù)據(jù)
glGenTextures(1, &texture[0].texID); // 生成 OpenGL 材質(zhì) ID
glBindTexture(GL_TEXTURE_2D, texture[0].texID); // 綁定我們的材質(zhì)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // 線過(guò)濾器
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 線過(guò)濾器
if (texture[0].bpp==24) // 若 TGA 是24 位的
{
type=GL_RGB; // 設(shè) ’type’ 為 GL_RGB
}
glTexImage2D(GL_TEXTURE_2D, 0, type, texture[0].width, texture[0].height, 0, type, GL_UNSIGNED_BYTE, texture[0].imageData);
return true; // 材質(zhì)建立成功, 返回正確
}
2D 字體材質(zhì)代碼同我已在前一課用的是一樣的. 然而, 有一些小不同. 第一是你將看到僅僅唯一生成95 顯示列表. 若你看字體材質(zhì), 你會(huì)看到只有 95 字母計(jì)算空間在圖片頂,左. 第二個(gè)事是你將通知分在16.0f 為 cx 和 我們只分在8.0f 為cy. 我這樣做的結(jié)果是因?yàn)樽煮w材質(zhì)是256 象素寬, 但僅僅一伴就高(128 象素). 所以計(jì)算cx 我們分為16.0f 和計(jì)算分cy 為一半(8.0f).
若你不懂下面的代碼, 回去讀17課. 建立字體的代碼的詳細(xì)解釋在第17課里!
GLvoid BuildFont(GLvoid) // 建立我們字體顯示列表
{
base=glGenLists(95); // 建立 95 顯示列表
glBindTexture(GL_TEXTURE_2D, textures[9].texID); // 綁我們字體材質(zhì)
for (int loop=0; loop<95; loop++) // 循環(huán)在 95 列表
{
float cx=float(loop%16)/16.0f; // X 位置 在當(dāng)前字母
float cy=float(loop/16)/8.0f; // Y 位置 在當(dāng)前字母
glNewList(base+loop,GL_COMPILE); // 開(kāi)始建立一個(gè)列表
glBegin(GL_QUADS); // 用四邊形組成每個(gè)字母
glTexCoord2f(cx, 1.0f-cy-0.120f); glVertex2i(0,0); // 質(zhì)地 / 點(diǎn) 座標(biāo) (底 左)
glTexCoord2f(cx+0.0625f, 1.0f-cy-0.120f); glVertex2i(16,0); // 質(zhì)地 / 點(diǎn) 座標(biāo) (底 右)
glTexCoord2f(cx+0.0625f, 1.0f-cy); glVertex2i(16,16); // 質(zhì)地 / 點(diǎn) 座標(biāo) (頂 右)
glTexCoord2f(cx, 1.0f-cy); glVertex2i(0,16); // 質(zhì)地 / 點(diǎn) 座標(biāo) (頂 左)
glEnd(); // 完成建立我們的 四邊形 (字母)
glTranslated(10,0,0); // 移到字體的右邊
glEndList(); // 完成建軍立這個(gè)顯示列表
} // 循環(huán)直到所有 256 完成建立
}
輸出的代碼也在第17課, 但已修改為在屏幕輸出我們的分?jǐn)?shù), 等級(jí)和士氣(不斷改變的值).
GLvoid glPrint(GLint x, GLint y, const char *string, ...) // 輸出在屏慕的位置
{
char text[256]; // 保存在我們的字符串
va_list ap; // 到列表的指針
if (string == NULL) // 若文字為空
return; // 返回
va_start(ap, string); // 解析字符串
vsprintf(text, string, ap); // 轉(zhuǎn)換字符串
va_end(ap); // 結(jié)果的字符串
glBindTexture(GL_TEXTURE_2D, textures[9].texID); // 選擇我們字體材質(zhì)
glPushMatrix(); // 存觀看模式矩陣
glLoadIdentity(); // 設(shè)觀看模式矩陣
glTranslated(x,y,0); // 文字輸出位置 (0,0 - 底 左-Bottom Left)
glListBase(base-32); // 選擇字體設(shè)置
glCallLists(strlen(text), GL_UNSIGNED_BYTE, text); // 輸出顯示列表中的文字
glPopMatrix(); // 取出以前的模式矩陣
}
這些代碼調(diào)用排序程序. 它比較距離在兩個(gè)結(jié)構(gòu)并返回-1 若第一個(gè)結(jié)構(gòu)的距離小于第二個(gè) , 1 i若 第一個(gè)結(jié)構(gòu)的距離大于第二個(gè) 0 否則 (若 距離相等)
int Compare(struct objects *elem1, struct objects *elem2) // 比較 函數(shù)
{
if ( elem1->distance < elem2->distance) // 若 第一個(gè)結(jié)構(gòu)的距離小于第二個(gè)
return -1; // 返回 -1
else if (elem1->distance > elem2->distance) // 若 第一個(gè)結(jié)構(gòu)的距離大于第二個(gè)
return 1; // 返回1
else // 否則 (若 距離相等)
return 0; // 返回 0
}
InitObject() 代碼是來(lái)建立每個(gè)物體. 我們開(kāi)始設(shè) rot 為 1. 這使物體順時(shí)針旋轉(zhuǎn). 然后設(shè)爆炸效果動(dòng)畫(huà)幀為0(我們不想爆炸效果從中間開(kāi)始).我們下面設(shè) hit 為 FALSE, 意思是物體還沒(méi)被擊中或正開(kāi)如. 選一個(gè)物體材質(zhì), texid 用來(lái)給一個(gè)隨機(jī)的變量從 0 到 4. 0是blueface 材質(zhì) 和4 是 vase 材質(zhì). 這給我們5種隨機(jī)物體.
距離變量是在-0.0f to -40.0f (4000/100 is 40)的隨機(jī)數(shù) . 當(dāng)我們真實(shí)的畫(huà)物體,我們透過(guò)在屏幕上的另10 個(gè)單位. 所以當(dāng)物體在畫(huà)時(shí), 他們將畫(huà)從-10.0f to -50.0f 單位 在屏幕(不挨著, 也不離得太遠(yuǎn)). 我分隨機(jī)數(shù)為 100.0f 得到更精確的浮點(diǎn)數(shù)值.
在給完隨機(jī)的距離之后, 我們給物體一個(gè)隨機(jī)的 y . 我們不想物體低于 -1.5f, 否則他將低于大地, 且我們不想物體高于3.0f. 所以留在我們的區(qū)間的隨機(jī)數(shù)不能高于4.5f (-1.5f+4.5f=3.0f).
去計(jì)算 x 位置, 用一些狡猾的數(shù)學(xué). 用我們的距離減去15.0f . 除以2 減5*level. 再 減隨機(jī)數(shù)(0.0f 到5) 乘level. 減 5*level rndom(0.0f to 5*level) 這是最高級(jí).
選一個(gè)方向.
使事情簡(jiǎn)單明白x, 寫(xiě)一個(gè)快的例子. 距離是 -30.0f ,當(dāng)前級(jí)是 1:
object[num].x=((-30.0f-15.0f)/2.0f)-(5*1)-float(rand()%(5*1));
object[num].x=(-45.0f/2.0f)-5-float(rand()%5);
object[num].x=(-22.5f)-5-{lets say 3.0f};
object[num].x=(-22.5f)-5-{3.0f};
object[num].x=-27.5f-{3.0f};
object[num].x=-30.5f;
開(kāi)始在屏模上移 10 個(gè)單位 , 距離是 -30.0f. 其實(shí)是 -40.0f.用透視的代碼在 NeHeGL.cpp 文件.
GLvoid InitObject(int num) // 初始化一個(gè)物體
{
object[num].rot=1; // 順時(shí)針旋轉(zhuǎn)
object[num].frame=0; // 設(shè)爆炸效果動(dòng)畫(huà)幀為0
object[num].hit=FALSE; // 設(shè)點(diǎn)擊檢測(cè)為0
object[num].texid=rand()%5; // 設(shè)一個(gè)材質(zhì)
object[num].distance=-(float(rand()%4001)/100.0f); // 隨機(jī)距離
object[num].y=-1.5f+(float(rand()%451)/100.0f); // 隨機(jī) Y 位置
// 隨機(jī)開(kāi)始 X 位置 基于物體的距離 和隨機(jī)的延時(shí)量 (確定變量)
object[num].x=((object[num].distance-15.0f)/2.0f)-(5*level)-float(rand()%(5*level));
object[num].dir=(rand()%2); // 選一個(gè)隨機(jī)的方向
檢查方向
if (object[num].dir==0) // 若隨機(jī)的方向正確
{
object[num].rot=2; // 逆時(shí)針旋轉(zhuǎn)
object[num].x=-object[num].x; // 開(kāi)始在左邊 (否定 變量)
}
現(xiàn)在我們檢查texid 來(lái)找出所選的的物體. 若 texid 為0, 所選的物體是 Blueface . blueface 總是在大地上面旋轉(zhuǎn). 確定開(kāi)始時(shí)在地上的層, 我們?cè)O(shè) y 是 -2.0f.
if (object[num].texid==0) // 藍(lán)色天空表面
object[num].y=-2.0f; // 總是在大地上面旋轉(zhuǎn)
下面檢查若texid 是 1. 這樣, 電腦所選物體的是 Bucket. bucket不從左到右運(yùn)動(dòng), 它從天上掉下來(lái). 首先我們不得不設(shè) dir 是 3. 這告訴電腦我們的水桶bucket 是掉下來(lái)或向下運(yùn)動(dòng).
我們最初的代碼假定物體從左到右運(yùn)動(dòng). 因?yàn)閎ucket 是向下落的, 我們得不給它一個(gè)新的隨機(jī)的變量 x . 若不是這樣, bucket 會(huì)被看不到. 它將不在左邊落下就在屏幕外面. 我們給它一個(gè)新的隨機(jī)距離變量在屏幕上. 代替減去15, 我們僅僅減去 10. 這給我們一些幅度, 保持物體在屏幕??. 設(shè)我們的distance 是-30.0f, 從0.0f -40.0f的隨機(jī)變量. 為什么從 0.0f 到 40.0f? 不是從0.0f to -40.0f? 答案很簡(jiǎn)單. rand() 函數(shù)總返回正數(shù). 所以總是正數(shù). 另外,回到我們的故事. 我們有個(gè)正數(shù) 從0.0f 到 40.0f.我們加距離 最小 10.0f 除以 2. 舉個(gè)例子,設(shè)x變量為 15 ,距離是 -30.0f:
object[num].x=float(rand()%int(-30.0f-10.0f))+((-30.0f-10.0f)/2.0f);
object[num].x=float(rand()%int(-40.0f)+(-40.0f)/2.0f);
object[num].x=float(15 {assuming 15 was returned))+(-20.0f);
object[num].x=15.0f-20.0f;
object[num].x=-5.0f;
下面設(shè)y. 我們想水桶從天上下來(lái). 我人不想穿過(guò)云. 所以我們?cè)O(shè) y 為 4.5f. 剛在去的下面一點(diǎn).
if (object[num].texid==1) // 水桶(Bucket)
{
object[num].dir=3; // 下落
object[num].x=float(rand()%int(object[num].distance-10.0f))+((object[num].distance-10.0f)/2.0f);
object[num].y=4.5f; // 隨機(jī) X, 開(kāi)始在屏模上方
}
我們想靶子從地面突出到天上. 我們檢查物體為 (texid 是 2). 若是, 設(shè)方向(dir) 是 2 (上). 用精確的數(shù) x 位置.
我們不想target 開(kāi)始在地上. 設(shè) y 初值為-3.0f (在地下). 然后減一個(gè)值從0.0f 到 5 乘當(dāng)前 level. 靶子不是立即出現(xiàn). 在高級(jí)別是有延時(shí), 通過(guò)delay, 靶子將出現(xiàn)在一個(gè)在另一個(gè)以后, 給你很少時(shí)間打到他們.
if (object[num].texid==2) // 靶子
{
object[num].dir=2; // 開(kāi)始向上飛
object[num].x=float(rand()%int(object[num].distance-10.0f))+((object[num].distance-10.0f)/2.0f);
object[num].y=-3.0f-float(rand()%(5*level)); // 隨機(jī) X, 開(kāi)始在下面的大地 + 隨機(jī)變量
}
所有其它的物體從右到左旅行, 因而不必給任何變量付值來(lái)改變物體. 它們應(yīng)該剛好工作在所給的隨機(jī)變量.
現(xiàn)在來(lái)點(diǎn)有趣的材料! "為了alpha 混合技術(shù)正常的工作, 透明的原物必須不斷地排定在從后向前畫(huà)". 當(dāng)畫(huà)alpha 混合物體是, 在遠(yuǎn)處的物體是先畫(huà)的,這是非常重要的, 下面畫(huà)緊臨的上面的物體.
理由是簡(jiǎn)單的... Z 緩沖區(qū)防止 OpenGL 從已畫(huà)好的混合東西再畫(huà)象素. 這就是為什么會(huì)發(fā)生物體畫(huà)在透明混合之后而不再顯示出來(lái). 為什么你最后看到的是一個(gè)四邊形與物體重疊... 很不好看!
我們已知道每個(gè)物體的深度. 因而在初始化一個(gè)物體之后, 我們能通過(guò)把物體排序,而用qsort 函數(shù)(快速排序sort),來(lái)解決這個(gè)問(wèn)題 . 通過(guò)物體排序, 我們能確信第一個(gè)畫(huà)的是最遠(yuǎn)的物體. 這意味著當(dāng)我們畫(huà)物體時(shí), 起始于第一個(gè)物體, 物體通過(guò)用距離將被先畫(huà). 緊挨著那個(gè)物體(晚一會(huì)兒畫(huà)) 將看到先前的物體在他們的后面, 再將適度的混合!
這文中的這行線注釋是我在 MSDN 里發(fā)現(xiàn)這些代碼,在網(wǎng)上花時(shí)間查找之后找到的解答 . 他們工作的很好,允許各種的排序結(jié)構(gòu). qsort 傳送 4 個(gè)參數(shù). 第一個(gè)參數(shù)指向物體數(shù)組 (被排序的數(shù)組d). 第二個(gè)參數(shù)是我們想排序數(shù)組的個(gè)數(shù)... 當(dāng)然,我們想所有的排序的物體普遍的被顯示(各個(gè)level). 第三個(gè)參數(shù)規(guī)定物體結(jié)構(gòu)的大不, 第四個(gè)參數(shù)指向我們的 Compare() 函數(shù).
大概有更好的排序結(jié)構(gòu)的方法, 但是 qsort() 工作起來(lái)... 快速方便,簡(jiǎn)單易用!
這個(gè)是重要的知識(shí)點(diǎn), 若你們想用 glAlphaFunc() 和 glEnable(GL_ALPHA_TEST), 排序是沒(méi)必要的. 然而, 用Alpha 功能你被限制在完全透明或完全白底混合, 沒(méi)有中間值. 用 Blendfunc()排序用一些更多的工作,但他顧及半透明物體.
// 排序物體從距離:我們物體數(shù)組的開(kāi)始地址 *** MSDN 代碼修改為這個(gè) TUT ***// 各種的數(shù)按// 各自的要素的// 指針比較的函數(shù)
qsort((void *) &object, level, sizeof(struct objects), (compfn)Compare );
}
初始化的代碼總是一樣的. 首先的現(xiàn)兩行取得我們window 的消息和我們建盤(pán)消息. 然后我們用 srand() 建一個(gè)基于時(shí)間的多樣化的游戲. 之后我們調(diào)入 TGA 圖片并用LoadTGA()轉(zhuǎn)換到材質(zhì) . 先前的 5個(gè)圖片是將穿過(guò)屏幕的物體. Explode 是我們爆炸動(dòng)畫(huà), 大地和天空 彌補(bǔ)現(xiàn)場(chǎng)背景, crosshair是你在屏幕上看到表現(xiàn)鼠標(biāo)當(dāng)前位置的十字光標(biāo), 最后, 用來(lái)顯示分?jǐn)?shù),標(biāo)題和士氣值的字體的圖片. 若任何調(diào)入圖片的失誤,則到返回 FALSE 值, 并程序結(jié)束. 值得注意的是這些基本代碼不是返回整數(shù)型(INIT)的 FAILED 錯(cuò)誤消息.
BOOL Initialize (GL_Window* window, Keys* keys) // 任何 OpenGL 從這初始化
{
g_window = window;
g_keys = keys;
srand( (unsigned)time( NULL ) ); // 使隨機(jī)化事件
if ((!LoadTGA(&textures[0],"Data/BlueFace.tga")) || // 調(diào)入藍(lán)面材質(zhì)
(!LoadTGA(&textures[1],"Data/Bucket.tga")) || // 調(diào)入水桶材質(zhì)
(!LoadTGA(&textures[2],"Data/Target.tga")) || // 調(diào)入靶子材質(zhì)
(!LoadTGA(&textures[3],"Data/Coke.tga")) || // 調(diào)入 可樂(lè)材質(zhì)
(!LoadTGA(&textures[4],"Data/Vase.tga")) || // 調(diào)入 花瓶 材質(zhì)
(!LoadTGA(&textures[5],"Data/Explode.tga")) || // 調(diào)入 爆炸材質(zhì)
(!LoadTGA(&textures[6],"Data/Ground.tga")) || // 調(diào)入 地面 材質(zhì)
(!LoadTGA(&textures[7],"Data/Sky.tga")) || // 調(diào)入 天空 材質(zhì)
(!LoadTGA(&textures[8],"Data/Crosshair.tga")) || // 調(diào)入 十字光標(biāo) 材質(zhì)
(!LoadTGA(&textures[9],"Data/Font.tga"))) // 調(diào)入 字符 材質(zhì)
{
return FALSE; // 若調(diào)入失敗, 返回錯(cuò)誤
}
若所有圖片調(diào)入成功則輪到材質(zhì), 我們能繼續(xù)初始化. 字體材質(zhì)被調(diào)入, 因而保險(xiǎn)能建立我們的字體. 我們跳入BuildFont()來(lái)做這些.
然后我們?cè)O(shè)置OpenGL. 背景色為黑, alpha 也設(shè)為0.0f. 深度緩沖區(qū)設(shè)為激活小于或等于測(cè)試.
glBlendFunc() 是很重要的一行代碼. 我們?cè)O(shè)混合函數(shù)(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). 這些加上alpha變量的屏幕上的混合物體存在物體的材質(zhì). 在設(shè)置混合模式之后, 我們激活blending(混合). 然后我們打開(kāi) 2D 材質(zhì)貼圖, 最后,打開(kāi) GL_CULL_FACE. 這是去除每個(gè)物體的后面( 沒(méi)有一點(diǎn)浪費(fèi)在一些我們看不到的循環(huán) ). 畫(huà)一些四邊形逆時(shí)針卷動(dòng) ,因而精致而適當(dāng)?shù)拿嫫?
早先的教程我談?wù)撌褂胓lAlphaFunc()代替alpha 混合. 若你想用Alpha 函數(shù), 注釋出的兩行混合代碼和不注釋的兩行在glEnable(GL_BLEND)之下. 你也能注釋出qsort()函數(shù)在 InitObject() 部分里的代碼.
程序應(yīng)該運(yùn)行ok,但sky 材質(zhì)將不在這. 因?yàn)閟ky的材質(zhì)已是一個(gè)alpha 變量0.5f. 當(dāng)早在我說(shuō)關(guān)于Alpha函數(shù), 我提及它只工作在alpha 變量0 或 1. 若你想它出現(xiàn),你將不得不修改sky的材質(zhì)alpha 通道! 再則, 若你決定用Alpha 函數(shù)代替, 你不得排序物體.兩個(gè)方法都有好處! 再下而是從SGI 網(wǎng)站的快速引用:
"alpha 函數(shù)丟棄細(xì)節(jié),代替畫(huà)他們?cè)诮Y(jié)構(gòu)緩沖器里. 因此排序原來(lái)的物體不是必須的 (除了一些其它像混合alpha模式是打開(kāi)的). 不占優(yōu)勢(shì)的是象素必須完全白底或完全透明".
BuildFont(); // 建立我們的字體顯示列表
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // 黑色背景
glClearDepth(1.0f); // 安裝深度緩沖器
glDepthFunc(GL_LEQUAL); // 深度的類型測(cè)試
glEnable(GL_DEPTH_TEST); // 打開(kāi)深度測(cè)試
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 打開(kāi) Alpha 混合
glEnable(GL_BLEND); // 打開(kāi)混合
glAlphaFunc(GL_GREATER,0.1f); // 設(shè) Alpha 測(cè)試
glEnable(GL_ALPHA_TEST); // 打開(kāi) Alpha 測(cè)試
glEnable(GL_TEXTURE_2D); // 打開(kāi)材質(zhì)貼圖
glEnable(GL_CULL_FACE); // 去掉畫(huà)物體的背面
在程序的這段, 還沒(méi)有物體被定義,所以循環(huán)30個(gè)物體,每個(gè)物體都調(diào)InitObject().
for (int loop=0; loop<30; loop++) // 循環(huán)在 30 個(gè)物體Objects
InitObject(loop); // 初始化每個(gè)物體
return TRUE; // 返回正確 (設(shè)初值成功)
}
在初始化代碼里, 調(diào)入BuildFont() 建立95 的顯示列表. 所以這里在程序結(jié)束前刪掉95顯示列表
void Deinitialize (void) // 任何user 結(jié)束初始化從這
{
glDeleteLists(base,95); // 刪掉所有95 字體顯示列表
}
現(xiàn)在為急速原始物體... 是實(shí)際被選?形鍰宓拇?. 第一行為我們選擇物體的信息分配內(nèi)存. hits 是當(dāng)選擇時(shí)碰撞迅檢測(cè)的次數(shù).
void Selection(void) // 這是選擇正確
{
GLuint buffer[512]; // 設(shè)選擇緩沖
GLint hits; // 選擇物體的數(shù)
下面的代碼, 若游戲結(jié)束(FALSE).沒(méi)有選任何, 返回(exit). 若游戲還在運(yùn)行 (TRUE),用Playsound() 命令放射擊的聲間. 僅僅調(diào)Selection()的時(shí)間是在當(dāng)已鼠標(biāo)按下時(shí)和每次按下按鍵調(diào)用時(shí), 想放射擊的聲音. 聲音在放在 async 模式 ,所以放音樂(lè)是程序不會(huì)停.
if (game) // 游戲是否結(jié)束?
return; // 是,返回, 不在檢測(cè) Hits
PlaySound("data/shot.wav",NULL,SND_ASYNC); // 放音樂(lè) Gun Shot
設(shè)視點(diǎn). viewport[] 包括當(dāng)前 x, y, 當(dāng)前的視點(diǎn)(OpenGL Window)長(zhǎng)度,寬度.
glGetIntegerv(GL_VIEWPORT, viewport) 取當(dāng)前視點(diǎn)存在viewport[]. 最初,等于 OpenGL 窗口維數(shù). glSelectBuffer(512, buffer) 說(shuō) OpenGL 用這個(gè)內(nèi)存.
// 視點(diǎn)的大小. [0] 是 <x>, [1] 是 <y>, [2] 是 <length>, [3] 是 <width>
GLint viewport[4];
// 這是設(shè)視點(diǎn)的數(shù)組在屏幕窗口的位置
glGetIntegerv(GL_VIEWPORT, viewport);
glSelectBuffer(512, buffer); // 告訴 OpenGL 使我們的數(shù)組來(lái)選擇
存opengl的模式. 在這個(gè)模式什么也不畫(huà). 代替, 在選擇模式物體渲染信息存在緩存.
下面初實(shí)化name 堆棧,通過(guò)調(diào)入glInitNames() 和glPushName(0). I它重要的是標(biāo)記若程序不在選擇模式, 一個(gè)到glPushName()調(diào)用將忽略. 當(dāng)然在選擇的模試, 但這一些是是緊記的.
// 設(shè) OpenGL 選擇模式. 將不畫(huà)東西. 物體 ID’的廣度放在內(nèi)存
(void) glRenderMode(GL_SELECT);
glInitNames(); // 設(shè)名字堆棧
glPushName(0); // Push 0 (最少一個(gè)) 在棧上
之后, 不得不限制在光標(biāo)的下面畫(huà)圖. 為了做這些得用到投影矩陣. 然后把它推到堆棧中.重設(shè)矩陣則用到 glLoadIdentity().
用gluPickMatrix()限制的畫(huà). 第1個(gè)參數(shù)是當(dāng)前鼠標(biāo)的 x-座標(biāo), 第2個(gè)參數(shù)是當(dāng)前鼠標(biāo)的 y-座標(biāo), 然后寬和高的選區(qū). 最后當(dāng)前的 viewport[]. viewport[] 是指出視點(diǎn)的邊界. x 和_y 將在選區(qū)的中心.
glMatrixMode(GL_PROJECTION); // 選投影矩陣
glPushMatrix(); // 壓入投影矩陣
glLoadIdentity(); // 重設(shè)矩陣
// 這是建一個(gè)矩陣使鼠標(biāo)在屏幕縮放
gluPickMatrix((GLdouble) mouse_x, (GLdouble) (viewport[3]-mouse_y), 1.0f, 1.0f, viewport);
調(diào)入 gluPerspective() 應(yīng)用透視矩陣 ,被gluPickMatrix()選擇矩陣限制所畫(huà)區(qū)域 .
打開(kāi)modelview 矩陣,調(diào)用DrawTargets()畫(huà)我們的靶子. 畫(huà)靶子在DrawTargets() 而不在 Draw() 是因?yàn)閮H僅想選擇物體的碰撞檢測(cè)且, 不是天空,大地,光標(biāo).
之后, 打開(kāi)回到發(fā)射矩陣, 從堆棧中彈出矩陣. 之扣打開(kāi)回到modelview 矩陣.
最后一行,回到渲染模式 因而物體畫(huà)的很真實(shí)的在屏幕上. hits 將采集gluPickMatrix()所需要取渲染的物體數(shù) .
// 應(yīng)用透視矩陣
gluPerspective(45.0f, (GLfloat) (viewport[2]-viewport[0])/(GLfloat) (viewport[3]-viewport[1]), 0.1f, 100.0f);
glMatrixMode(GL_MODELVIEW); // 選擇模型變換矩陣
DrawTargets(); // 畫(huà)目標(biāo)
glMatrixMode(GL_PROJECTION); // 選擇投影變換矩陣
glPopMatrix(); // 取出投影矩陣
glMatrixMode(GL_MODELVIEW); // 選模式顯示矩陣
hits=glRenderMode(GL_RENDER); // 切換模式, 找出有多少
檢查若多于0 個(gè)hits 記錄. 若這樣, 設(shè)choose 為 第一個(gè)物體的名子. depth 取得它有多遠(yuǎn).
每個(gè)hit 分有4 個(gè)項(xiàng)目在內(nèi)存. 第一,在名子堆棧上打擊發(fā)生時(shí)的數(shù)字 .第二, 所選物體的最小z值. 第三,所選物體的最大 z 值, 最后,在同一時(shí)間里所選物體名子堆棧的內(nèi)容 (物體的名子). 在這一課,我們僅對(duì)最小z值和物體名子感興趣.
if (hits > 0) // 若有大于0個(gè) Hits
{
int choose = buffer[3]; // 選擇第一物體
int depth = buffer[1]; // 存它有多遠(yuǎn)
做循環(huán)所有hits 使沒(méi)有物體在第一個(gè)物體旁邊. 否則, 兩個(gè)物體會(huì)重疊, 一個(gè)物體碰到另一個(gè).當(dāng)你射擊時(shí), 重疊的物體會(huì)被誤選.
for (int loop = 1; loop < hits; loop++) // 循環(huán)所有檢測(cè)到的物體
{
// 對(duì)于其它的物體
if (buffer[loop*4+1] < GLuint(depth))
{
choose = buffer[loop*4+3]; // 選擇更近的物體
depth = buffer[loop*4+1]; // 保存它有多遠(yuǎn)
}
}
若物體被選.
if (!object[choose].hit) // 如果物體還沒(méi)有被擊中
{
object[choose].hit=TRUE; // 標(biāo)記物體象被擊中
score+=1; // 增加分?jǐn)?shù)
kills+=1; // 加被殺數(shù)
如下
if (kills>level*5) // 已有新的級(jí)?
{
miss=0; // 失掉數(shù)回0
kills=0; // 設(shè) Kills數(shù)為0
level+=1; // 加 Level
if (level>30) // 高過(guò) 30?
level=30; // 設(shè) Level 為 30 (你是 God 嗎?)
}
}
}
}
如下
void Update(DWORD milliseconds) // 這里用來(lái)更新
{
if (g_keys->keyDown[VK_ESCAPE]) // 按下 ESC?
{
TerminateApplication (g_window); // 推出程序
}
如下
if (g_keys->keyDown[' '] && game) // 按下空格鍵?
{
for (int loop=0; loop<30; loop++) // 循環(huán)所有的物體
InitObject(loop); // 初始化
game=FALSE; //設(shè)game為false
score=0; // 分?jǐn)?shù)為0
level=1; // 級(jí)別為1
kills=0; // 殺敵數(shù)為0
miss=0; // 漏過(guò)數(shù)為0
}
if (g_keys->keyDown[VK_F1]) // 按下f1?
{
ToggleFullscreen (g_window); // 換到全屏模式
}
roll-=milliseconds*0.00005f; // 云的旋轉(zhuǎn)
for (int loop=0; loop<level; loop++) // 循環(huán)所有的物體
{
下面的代碼按物體的運(yùn)動(dòng)方向更新所有的運(yùn)動(dòng)
if (object[loop].rot==1)
object[loop].spin-=0.2f*(float(loop+milliseconds)); // 若順時(shí)針,則順時(shí)針旋轉(zhuǎn)
if (object[loop].rot==2)
object[loop].spin+=0.2f*(float(loop+milliseconds)); // 若逆時(shí)針,則逆時(shí)針旋轉(zhuǎn)
if (object[loop].dir==1)
object[loop].x+=0.012f*float(milliseconds); // 向右移動(dòng)
if (object[loop].dir==0)
object[loop].x-=0.012f*float(milliseconds); // 向左移動(dòng)
if (object[loop].dir==2)
object[loop].y+=0.012f*float(milliseconds); // 向上移動(dòng)
if (object[loop].dir==3)
object[loop].y-=0.0025f*float(milliseconds); // 向下移動(dòng)
下面的代碼處理當(dāng)物體移動(dòng)到邊緣處,如果你沒(méi)有擊中它的結(jié)果
// 如果到達(dá)左邊界,你沒(méi)有擊中,則增加丟失的目標(biāo)數(shù)
if ((object[loop].x<(object[loop].distance-15.0f)/2.0f) && (object[loop].dir==0) && !object[loop].hit)
{
miss+=1;
object[loop].hit=TRUE;
}
// 如果到達(dá)右邊界,你沒(méi)有擊中,則增加丟失的目標(biāo)數(shù)
if ((object[loop].x>-(object[loop].distance-15.0f)/2.0f) && (object[loop].dir==1) && !object[loop].hit)
{
miss+=1;
object[loop].hit=TRUE;
}
// 如果到達(dá)下邊界,你沒(méi)有擊中,則增加丟失的目標(biāo)數(shù)
if ((object[loop].y<-2.0f) && (object[loop].dir==3) && !object[loop].hit)
{
miss+=1;
object[loop].hit=TRUE;
}
//如果到達(dá)左邊界,你沒(méi)有擊中,則方向變?yōu)橄蛳?br> if ((object[loop].y>4.5f) && (object[loop].dir==2))
object[loop].dir=3;
}
}
下面的代碼在屏幕上繪制一個(gè)圖像
void Object(float width,float height,GLuint texid) // 畫(huà)物體用需要的寬,高,材質(zhì)
{
glBindTexture(GL_TEXTURE_2D, textures[texid].texID); // 選合適的材質(zhì)
glBegin(GL_QUADS); // 開(kāi)始畫(huà)四邊形
glTexCoord2f(0.0f,0.0f); glVertex3f(-width,-height,0.0f);
glTexCoord2f(1.0f,0.0f); glVertex3f( width,-height,0.0f);
glTexCoord2f(1.0f,1.0f); glVertex3f( width, height,0.0f);
glTexCoord2f(0.0f,1.0f); glVertex3f(-width, height,0.0f);
glEnd();
}
下面的代碼繪制爆炸的效果
void Explosion(int num) // 畫(huà)爆炸動(dòng)畫(huà)的1幀
{
float ex = (float)((object[num].frame/4)%4)/4.0f; // 計(jì)算爆炸時(shí)生成的x的紋理坐標(biāo)
float ey = (float)((object[num].frame/4)/4)/4.0f; // 計(jì)算爆炸時(shí)生成的y的紋理坐標(biāo)
glBindTexture(GL_TEXTURE_2D, textures[5].texID); // 選擇爆炸的紋理
glBegin(GL_QUADS);
glTexCoord2f(ex ,1.0f-(ey )); glVertex3f(-1.0f,-1.0f,0.0f);
glTexCoord2f(ex+0.25f,1.0f-(ey )); glVertex3f( 1.0f,-1.0f,0.0f);
glTexCoord2f(ex+0.25f,1.0f-(ey+0.25f)); glVertex3f( 1.0f, 1.0f,0.0f);
glTexCoord2f(ex ,1.0f-(ey+0.25f)); glVertex3f(-1.0f, 1.0f,0.0f);
glEnd();
增加幀數(shù),如果大于63,則重置動(dòng)畫(huà)
object[num].frame+=1; // 加當(dāng)前的爆炸動(dòng)畫(huà)幀
if (object[num].frame>63) // 是否已完成所有的16幀?
{
InitObject(num); // 定義物體 (給新的變量)
}
}
畫(huà)靶子
void DrawTargets(void) // 畫(huà)靶子
{
glLoadIdentity();
glTranslatef(0.0f,0.0f,-10.0f); // 移入屏幕 20 個(gè)單位
for (int loop=0; loop<level; loop++) // 循環(huán)在 9 個(gè)物體
{
glLoadName(loop); // 給物體新名字
glPushMatrix(); // 存矩陣
glTranslatef(object[loop].x,object[loop].y,object[loop].distance); // 物體的位置 (x,y)
if (object[loop].hit) // 若物體已被點(diǎn)擊
{
Explosion(loop); // 畫(huà)爆炸動(dòng)畫(huà)
}
else
{
glRotatef(object[loop].spin,0.0f,0.0f,1.0f); // 旋轉(zhuǎn)物體
Object(size[object[loop].texid].w,size[object[loop].texid].h,object[loop].texid); // 畫(huà)物體
}
glPopMatrix(); // 彈出矩陣
}
}
下面的代碼繪制整個(gè)場(chǎng)景
void Draw(void) // 畫(huà)我們的現(xiàn)場(chǎng)
{
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度緩沖
glLoadIdentity(); // 重設(shè)矩陣
下面的代碼繪制飄動(dòng)的天空,它由四塊紋理組成,每一塊的移動(dòng)速度都不一樣,并把它們混合起來(lái)
glPushMatrix();
glBindTexture(GL_TEXTURE_2D, textures[7].texID); // 選天空的材質(zhì)
glBegin(GL_QUADS);
glTexCoord2f(1.0f,roll/1.5f+1.0f); glVertex3f( 28.0f,+7.0f,-50.0f);
glTexCoord2f(0.0f,roll/1.5f+1.0f); glVertex3f(-28.0f,+7.0f,-50.0f);
glTexCoord2f(0.0f,roll/1.5f+0.0f); glVertex3f(-28.0f,-3.0f,-50.0f);
glTexCoord2f(1.0f,roll/1.5f+0.0f); glVertex3f( 28.0f,-3.0f,-50.0f);
glTexCoord2f(1.5f,roll+1.0f); glVertex3f( 28.0f,+7.0f,-50.0f);
glTexCoord2f(0.5f,roll+1.0f); glVertex3f(-28.0f,+7.0f,-50.0f);
glTexCoord2f(0.5f,roll+0.0f); glVertex3f(-28.0f,-3.0f,-50.0f);
glTexCoord2f(1.5f,roll+0.0f); glVertex3f( 28.0f,-3.0f,-50.0f);
glTexCoord2f(1.0f,roll/1.5f+1.0f); glVertex3f( 28.0f,+7.0f,0.0f);
glTexCoord2f(0.0f,roll/1.5f+1.0f); glVertex3f(-28.0f,+7.0f,0.0f);
glTexCoord2f(0.0f,roll/1.5f+0.0f); glVertex3f(-28.0f,+7.0f,-50.0f);
glTexCoord2f(1.0f,roll/1.5f+0.0f); glVertex3f( 28.0f,+7.0f,-50.0f);
glTexCoord2f(1.5f,roll+1.0f); glVertex3f( 28.0f,+7.0f,0.0f);
glTexCoord2f(0.5f,roll+1.0f); glVertex3f(-28.0f,+7.0f,0.0f);
glTexCoord2f(0.5f,roll+0.0f); glVertex3f(-28.0f,+7.0f,-50.0f);
glTexCoord2f(1.5f,roll+0.0f); glVertex3f( 28.0f,+7.0f,-50.0f);
glEnd();
下面的代碼繪制地面
glBindTexture(GL_TEXTURE_2D, textures[6].texID); // 大地材質(zhì)
glBegin(GL_QUADS);
glTexCoord2f(7.0f,4.0f-roll); glVertex3f( 27.0f,-3.0f,-50.0f);
glTexCoord2f(0.0f,4.0f-roll); glVertex3f(-27.0f,-3.0f,-50.0f);
glTexCoord2f(0.0f,0.0f-roll); glVertex3f(-27.0f,-3.0f,0.0f);
glTexCoord2f(7.0f,0.0f-roll); glVertex3f( 27.0f,-3.0f,0.0f);
glEnd();
繪制我們的靶子
DrawTargets(); // 畫(huà)我們的靶子
glPopMatrix();
下面的代碼繪制我們的十字光標(biāo)
// 十字光標(biāo) (在光標(biāo)里)
RECT window; // 用來(lái)存窗口位置
GetClientRect (g_window->hWnd,&window); // 取窗口位置
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0,window.right,0,window.bottom,-1,1); // 設(shè)置為正投影
glMatrixMode(GL_MODELVIEW);
glTranslated(mouse_x,window.bottom-mouse_y,0.0f); // 移動(dòng)到當(dāng)前鼠標(biāo)位置
Object(16,16,8); // 畫(huà)十字光標(biāo)
下面的代碼用來(lái)顯示幫助文字
// 游戲狀態(tài) / 標(biāo)題名稱
glPrint(240,450,"NeHe Productions"); // 輸出 標(biāo)題名稱
glPrint(10,10,"Level: %i",level); // 輸出 等級(jí)
glPrint(250,10,"Score: %i",score); // 輸出 分?jǐn)?shù)
如果丟失10個(gè)物體,游戲結(jié)束
if (miss>9) // 我們已丟失 10 個(gè)物體?
{
miss=9; // 限制丟失是10個(gè)
game=TRUE; // 游戲結(jié)束
}
在下面的代碼里, 我們查看若game 是TRUE. 若 game 是TRUE, 我們輸出 ’GAME OVER’游戲結(jié)束的消息. 若game 是false, 我們輸出 玩家的士氣morale (到10溢出). 士氣morale是被設(shè)計(jì)用來(lái)從10減去玩家失誤的次數(shù)(miss) . 玩家失掉的越多, 士氣越低.
if (game) // 游戲是否結(jié)束?
glPrint(490,10,"GAME OVER"); // 結(jié)束消息
else
glPrint(490,10,"Morale: %i/10",10-miss); // 輸出剩余生命
最后做的事我們選投影矩陣, 恢復(fù)(取出) 我們的矩陣返回到前一個(gè)情形, 設(shè)矩陣模式為 modelview ,刷新緩沖區(qū) ,使所有物體被渲染.
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glFlush();
}
這課程是多次熬夜的成果, 許多的時(shí)間用來(lái)編碼和寫(xiě) HTML. 在這一課結(jié)束的時(shí)候你應(yīng)你會(huì)學(xué)會(huì)怎樣picking, sorting, alpha blending and alpha testing 工作. 制做點(diǎn)和軟件. 每一個(gè)游戲, 到精選的GUI’們.最好的未來(lái)是制做時(shí)你不用記錄物體. 你給一個(gè)名字和碰撞 . 這很簡(jiǎn)單! 用alpha 通道和alpha 測(cè)試你能使物體完全顯示, 或漏出一些. 結(jié)果是很好, 你不用擔(dān)心關(guān)于顯示物體的材質(zhì), 除非你不顯示他們! 同以往一樣, 我希望你喜歡這個(gè)課程,愿看到一些好的游戲或好的項(xiàng)目從這個(gè)課程誕生.如果你有什么問(wèn)題或找到錯(cuò)誤,讓我知道 ... 我僅是一個(gè)普通人 :)
我將花大量的時(shí)間加入東西像物理系統(tǒng), 更多圖, 更多聲音, 等. 雖然只是一個(gè)課程! 我不寫(xiě)不按車(chē)燈和車(chē)輪. 我寫(xiě)這個(gè)用盡量不混亂的方法教你 OpenGL . 我希望看到一些嚴(yán)謹(jǐn)?shù)男薷? 若你找一些cool的課程發(fā)給我一份. 若是好的修改我將放到下載頁(yè). 若有足夠充分的修改我會(huì)專注修改這個(gè)課程的版本! 我在這里給你一個(gè)起點(diǎn). 剩下的靠你了 :)
要點(diǎn): 這是很重要的,稱為glTexImage2D 你設(shè)為兩種格式國(guó)際 GL_RGBA. 否則 alpha blending 將不工作!