摘要:從一個新的角度看待編程教學,從游戲的角度學習C語言。雖然C語言不是面向對象語言,但通過一個小游戲的設計過程學習用函數思考問題,在過程性語言中更多加入面向對象思路,那么當你真正的去學習一門面向對象語言時,就會有C語言的函數學習基礎去對比。這會使學習面向對象編程更容易理解和更有樂趣。
關鍵詞:編程;C語言;游戲編程;面向對象編程思路
中圖分類號:TP311文獻標識碼:A文章編號:1009-3044(2008)11-20303-05
1 引言
程序設計是一門實踐性很強的課程,只有理論沒有實踐是不行的,而且理論水平也要在分析對比不同實踐的基礎上才能積累和提高。如果教師認為只要講解了語法就算教授了該門語言的程序設計,連理論的邊恐怕都沒沾上。在學生學習程序設計的時候,接觸到的書本例題及課后習題多是就題解題,主要是演習一下語法。并且當前的程序設計教學多以教授語法為主,沒有引導學生如何合理優美地使用語言。同時程序實現中的很多技巧被公認為是標準的實現方法,但學生很少在教學過程中接觸到。而最重要的一點,學生往往沒有學習到程序設計的一些原理,沒有判斷程序實現優劣的正確標準,這會妨礙他們今后不斷提高程序設計水平。
我們在教學C語言程序設計時,更容易遇到上述的問題。學生基本上是學習控制臺應用程序就題解題演習算法,而對C的實際應用卻一無所知。那么本文就想通過以Turbo C為游戲編程平臺,C作為編程語言,引導學生做一個生活中相關的項目。從游戲編程的角度學習C語言。雖然C語言不是面向對象語言,但通過一個小游戲的設計過程學習用函數思考問題,在過程性語言中更多加入面向對象思路,那么當你真正的去學習一門面向對象語言時,就會有C語言的函數學習基礎去對比。同時在游戲對象結構定義的時候融入更多屬性,事件函數指針和鏈表指針,在子畫面處理和界面對象處理中更多采用了事件驅動的思路,從而也可以為今后轉向C++游戲編程鋪平道路。
2 游戲編程的幾大要素
一個成功的游戲通常需要極高的美工水平、逼真的動畫效果、精巧的構思、簡便的操作。其中動畫和用戶操作是需要在編程中下苦功夫的。
游戲編程要注意五大要素:動畫的效果一定要流暢,沒有散動和盡量逼真,最好能夠符合“物理”和“生物學”的運動規律;且游戲要易于操作,并且能夠快速響應,必要的時候能夠設計出適合游戲的輸入設備和驅動程序;再則提高圖像,動畫,聲音和操作的同步性和混合性;用于實現游戲的數據結構一定要經過規劃,盡量簡便,高效,以及適用面和擴充性強。最后引入面向對象的思路將對理清程序員思路,簡化程序設計以及程序擴充等問題帶來相當大的幫助。
這些編程要素事實上涉及了包括直接寫屏,中斷,多任務,內存技術,動畫技術,顯示技術和優化算法等各種編程技術;涉及到了內存,PC喇叭,聲卡,鍵盤,鼠標,手柄,顯卡等各種技術和優化算法等各種編程技術;也涉及了包括C語言程序設計,C++語言程序設計,匯編語言,數據結構,數據庫,計算機硬件,接口技術,算法,高等數學,多媒體技術,人工智能等計算機專業課目;此外,還涉及到動畫技術,三維游戲編程,加密,解密技術和病毒與病毒防治編程等一些當前流行的計算機專業技術。
3 游戲的設計步驟
軟件工程課告訴我們任何一個軟件的實現都有必要的幾個步驟:先要對具體的內容規劃,融入自己的創意;其次用簡潔高效的數據結構來實現你的創意;再將你的數據結構用程序語言來表達出來;最后對程序的系統可用性,模塊功能進行測試,檢驗程序中錯誤和邊緣問題,同時修改程序不符人意的部分,使游戲更加好玩,更便于操作。
游戲設計事實上也是軟件的設計,然而它又有自身的一些特點。比如,游戲強調有極其新穎的創意,要對游戲的每個細節都有準確,詳盡的描述和表現等。游戲編程的步驟可以歸納為如下幾點。
(1)創意階段:對創意進行完善,設計出游戲的完整過程并畫出流程圖,并將游戲分解為若干層次;對每個層次進行詳盡的描述,包括其中的人和物以及每個層次的實現目標;對每個層次設定規則,包括人和物的移動,操作者的權利,游戲的獎勵和懲罰以及周邊的一切環境,音效和幫助。
(2)規劃階段:使用的圖形模式。比如BIOS中斷10H的13H模式,即200*320像素256色;確定圖形,動畫的復雜程度。比如用二維實現還是三維。考慮使用游戲函數庫中的哪些函數來實現游戲創意中的那些要求。
(3)周邊準備:制作人物,環境的圖像文件,聲音文件以及建立地點,發展層次,人,物品,對話和獎勵懲罰的數據庫文件。
(4)細部實現:圖像實現函數,圖像動畫函數,聲音播放函數,輸入裝置驅動和功能函數以及存盤,讀盤函數。
(5)模塊實現:數據結構系統,圖像,動畫系統,輸入、輸出系統,人工智能系統,游戲循環系統,用戶界面系統以及聲音系統
(6)整體完善:游戲功能和完整性的擴展;游戲的文件大小的縮減。
這只是一個大的方向性原則,具體的內部設計步驟根據不同的游戲的情況和個人風格進行增減或調整。
4 游戲的流程
別看游戲有那么復雜的情節和玩法,從開始到發展再到結束的流程中,歸根結底就只有四樣東西:
動畫:根據運算結果對屏幕中變化的對象進行重畫,從而實現動畫效果;
響應:對于來自鍵盤,鼠標等輸入設備和計算機內部如時間等中斷進行響應;
運算:根據各類具體響應通過計算和重新賦值來改變程序內變量數值;
循環:反復進行動畫,響應和運算的操作來實現游戲的真正進程。
實現一個游戲的基本流程如圖1所示。
下面以游戲俄羅斯方塊為例,動畫流程分為以下幾個步驟:
(1)游戲開始,初始設置(這個過程設置了所有的變量初始值);
(2)進行一個方塊下落或者消去的動畫(這個過程只負責重畫屏幕);
(3)然后查看是否有用戶發出的旋轉,下落,左右移動或退出游戲的指令(這個過程接收信息,同時存入輸入變量);
(4)此后如果有用戶指令根據其指令計算出塊的下一個形狀,位置,同時也按照游戲本身設置的下落速度進行疊加計算并且判斷是否到達底部,如果到達底部再判斷是否可以消去和是否死亡(這個過程改變各種變量的值);
(5)計算完畢后,根據結果進行下一次下落或消去的動畫(回到步驟2),如此循環往復直到游戲結束(這個過程就是一個While語句之類的循環)。
對應上面5個步驟,再以一個簡單的C語言偽程序為例:
# include…
#define…
Void main(void)
{
int a,b,c;//步驟1:相當于設置初始值
a=1;
b=2;
c=3;
while(a!=’q’)
{
printf(“%d”,c); // 步驟2:相當于重畫屏幕
a=getch(); //步驟3:相當于響應輸入設備
c=a*b; //步驟4:相當于重新運算變量值
}//步驟5:相當于如果a不等于q就繼續循環步驟2--4
}
我們將上面給出的偽程序套用之后再一步一步改寫,一個游戲程序就出來了。
# include
# include
# include
# include
# include
# include
/* 七種拼塊的形狀數據 */
char fk[7][4][2]={
1,1,1,2,1,3,1,4,
1,1,1,2,1,3,2,3,
2,1,2,2,1,3,2,3,
1,1,1,2,2,2,2,3,
2,1,2,2,1,3,2,3,
1,1,2,1,1,2,2,2,
1,1,1,2,1,3,2,2};
/* 存放組成拼塊的四個小方塊X和Y坐標 */
int kx[4],ky[4];
/* 桶 */
int tong[13][25];
/* 等待按鍵并延時的函數 */
char key(int s)
{
clock_t t1,t2;
char c;
t1=clock();
do
t2=clock();
while (((t2-t1)
if (kbhit())
c=getch();
else
c=0;
returnc;
}
/* 顯示或清除方塊的函數 */
void kuai(int x,int y,int c)
{
char cc[3];
if(c==1)
strcpy(cc,\"[]\");
else
strcpy(cc,\"\");
gotoxy(x*2,y);
puts(cc);
gotoxy(79,24);
}
/* 畫出拼塊 */
void hua()
{
int i;
for(i=0;i<4;i++)
kuai(kx[i],ky[i],1);
}
/* 清除拼塊的函數 */
void ca()
{
int i;
for(i=0;i<4;i++)
kuai(kx[i],ky[i],0);
}
/* 拼塊下落一行 */
int xialuo()
{
int t,i;
ca();
t=1;
for(i=0;i<4;i++){
if(ky[i]==24) {t=0; break;};
if(tong[kx[i]][ky[i]+1]==1) {t=0; break;};
}
if(t==1)
for(i=0;i<4;i++)
ky[i]=ky[i]+1;
else
for(i=0;i<4;i++)
tong[kx[i]][ky[i]]=1;
hua();
return t;
}
/* 拼塊左右移動函數 */
void yidong(int p)
{
int t,i;
ca();
t=1;
i=0;
do{
if((kx[i]==1)(p==-1)) {t=0; break;};
if((kx[i]==12)(p==1)) {t=0; break;};
if (tong[kx[i]+p][ky[i]]==1) {t=0; break;};
i++;
} while (i<4);
if(t==1)
for(i=0;i<4;i++)
kx[i]=kx[i]+p;
hua();
}
/* 旋轉拼塊的函數 */
void zhuan()
{
int i,t,x,y;
int kx1[4],ky1[4];
ca();
x=kx[1]; y=ky[1];
for(i=0;i<4;i++) {
kx1[i]=x+y-ky[i]; ky1[i]=y-x+kx[i];
}
t=1;
for(i=0;i<4;i++){
if((kx1[i]<1)||(kx1[i]>12)||(ky1[i]<1)||(ky1[i]>24)) {t=0; break;};
if (tong[kx1[i]][ky1[i]]==1) {t=0; break;};
}
if(t==1)
for(i=0;i<4;i++){
kx[i]=kx1[i]; ky[i]=ky1[i];
};
hua();
}
/* 計算一行中的方塊數 */
int fangkuaishu(int h)
{
int i,p=0;
for(i=1;i<=12;i++)
p=p+tong[i][h];
if(p==12)
for(i=1000;i<=4000;i+=80){
sound(i);
delay(10);
}
nosound();
return p;
}
/* 桶中方塊除去一行,在此行上面的方塊下移一行 */
void yihang(int h)
{
int k,j,q;
for(k=h;k>0;k--){
q=0;
for(j=1;j<=12;j++){
if(tong[j][k]!=tong[j][k-1]){
kuai(j,k,tong[j][k-1]);
tong[j][k]=tong[j][k-1];
}
q=q+tong[j][k];
}
if(q==0) break;
}
}
/* 檢查有無完成的行,并處理之 */
void jiancha()
{
int i,n;
i=24;
do{
n=fangkuaishu(i);
if(n==12) yihang(i); else i--;
} while((i>0)(n>0));
}
/* 開始函數,初始化,畫出空桶*/
void kaishi()
{
int i,j;
for(i=1;i<=24;i++)
for(j=1;j<=12;j++)
tong[j][i]=0;
clrscr();
for(i=0;i<24;i++)
puts(\"| |\");
printf(\"`----------'\");
randomize();
}
/* 主程序 */
main()
{
int i,j,k,s;
int m,t;
char kk;
kaishi();/* 屏幕初始化,顯示空桶 */
for(;;){/* 開始一個循環,在此循環中不斷檢測按鍵并處理之 */
m=rand()%7; /* 隨機產生一種拼塊 */
for(i=0;i<4;i++){/* 計算拼塊在桶中的坐標 */
kx[i]=fk[m][i][0]+5;
ky[i]=fk[m][i][1];
}
t=1;
for(i=0;i<4;i++) /* 檢查拼塊是否能放入桶中,不能放則退出游戲 */
if (tong[kx[i]][ky[i]]==1)
exit(0);
hua();/* 畫出拼塊 */
s=0; /* S是一個標志,指示是否按了空格鍵 */
for(;;){/* 此循環中處理一個拼塊的下落過程 */
if(s==0) kk=key(9); else kk=key(0); /* 按過了空格鍵則不延時 */
if((kk>='a')(kk<='z')) kk=kk-32;/* 將小寫字母轉換為大寫 */
switch (kk){/* 根據按鍵作相應處理 */
case ' ' : s=1;break;/* 按空格, 快速下落 */
case 'K': zhuan();break; /* 按K,旋轉拼塊 */
case 'J': yidong(-1); break; /* 按J,左移拼塊 */
case 'L': yidong(1); break; /* 按L,右移拼塊 */
case 'S': while(kbhit()==0);break; /* 按S,暫停游戲 */
case 'E': exit(0); break; /* 按E,結束游戲 */
default : t=xialuo(); /* 沒按上面的鍵,或未按任何鍵,拼塊下落一格 */
}
if(t==0) break; /* 拼塊落到桶底或不能再下落退出循環 */
}
jiancha();/* 檢查是否有完成的行,并處理之 */
}
}
面向對象編程思路的融入是游戲編程流程的又一重點。再來看一下程序設計,原先的流程可以被理解成結構圖如圖2。
這和前面的游戲流程并不矛盾,只是換個角度看問題。如下表1是對象思路中的每個環節和游戲流程的四大部分進行對應關系。
一個是流程明晰,一個是流行的對象化思路,那么到底我們按照哪種思路來編寫程序
(上接第307頁)
呢?如果我們以TC為平臺,那么思路就在流程化語言的基礎上加入面向對象思路,這樣的好處是能讓流程化語言也擁有一定的面向對象思路容易擴充,維護的優點。
5 結束語
本文章就給了大家一套寫游戲最簡單的模板。無論你要寫怎樣的游戲,都可將它分為動畫,響應,運算和循環四部分,外加一個初始設置。教師在教授學生基礎語法知識同時可以引導學生做些類似游戲的,學生比較感興趣的小程序,逐步培養算法思維,培養用程序設計語言思考和表達的能力。最后在學完一種語言的語法知識以后,讓學生自己選題完成一個項目,項目不論大小,關鍵是尋找最佳的解決方案,用最好的設計和代碼實現。在這一過程中,程序設計水平就逐漸提升了。
參考文獻:
[1] Tom Meigs,著,陳貴敏,杜敬利,韓琪,譯.頂級游戲設計[M].北京:電子工業出版社,2004:1-57.
[2] Andre LaMothe著, 李祥瑞,陳武譯. 3D游戲編程大師技巧[M].北京:人民郵電出版社,2005:1-139.
[3] 浦濱.C游戲編程從入門到精通[M].北京:北京希望電子出版社,2002:1-397.
[4] Marianne Krawczyk,著,姚曉光,孫泱譯.游戲開發核心技術[M].北京:機械工業出版社,2007:15-32.
注:本文中所涉及到的圖表、注解、公式等內容請以PDF格式閱讀原文