這個是想來溫習一下好久以前學的C語言(不過file、 指標、結構 這些比較後面的章節都不太熟悉,以前學的其他東西自以為還行但實際上在寫時忘掉了很多,藉由寫個小程式來練習,程式方面的設計也只是做一步想一步)。P.S.本人極度缺乏各種基層知識...可能有不少地方會說錯。
原本是打算用1111陣列來當圍牆,空白陣列當作可移動空間。來做一個貪食蛇小遊戲,不過後來發現我不懂該怎麼控制程式的刷新printf時間 (人物走路速度),所以我就改變成以按鍵操控動作,按一下動一下的吃金幣小遊戲,不然看不清人物在哪...看不清畫面的原因據說是system("cls")洗頻洗得不夠快。p.s.我是用system("cls")來洗掉屏幕的。
____________________________________________________________________________
我的小遊戲是設計一小塊地方,皆以函示來區分動作.
P.S. 我的地圖char wall[][]字元陣列是 全域變數,一開始寫時指標不熟,先用全域變數替代。
第一步、設計地圖與人物圖示
我是打算利用字元陣列來顯示,1代表圍牆,空白代表可走路段.
我先設定一個9*9字元陣列再利用for迴圈與if設定邊界與可走路段.
void p_on(int *lv){ int i,j,ran; extern char wall[9][9]; for(i=0;i<=8;i++) /*遊戲內值初始化 */ { for(j=0;j<=8;j++) { if(*lv==0&&i==4&&j==4) wall[i][j]=12; /*人物初始位置設定*/ else if (i==0||i==8||j==0||j==8) wall[i][j]='1'; /*設定圍牆*/ else if(wall[i][j]!=12) wall[i][j]=' '; } } }_____________________________________________________________________________
第二步、設定一個副程式印出地圖
void printmap(void){ int i,j; coordinate(0,0); //這邊定座標 由於畫面皆相同 打算直接蓋掉 printf("吃金幣小遊戲\n"); for(i=0;i<6;i++){ coordinate(30,5+i); printf("%5s:%7s\n",key_set[i].key_name,key_set[i].key_prev); }; for(i=0;i<=8;i++) { coordinate(5,5+i); for(j=0;j<=8;j++) printf("%c",wall[i][j]); printf("\n"); } }__________________________________________________________________________
第三步、抓取按建值 getche() 與設定初始按鍵
在網路查到讀按鍵值而不用按enter的有getch( ),getche( )
getche( )會顯示 按鍵值
這個地方卡住我很久,我的讀值一直讀不好,後來才知道 方向鍵 和一些特殊案件會送出兩個數值
要getch() 兩次,或 聽說有個_ungetch() 的功能只是不安全......送出兩個值似乎和 鍵盤掃描碼有關
但是我詳細的不懂,只知道 方向鍵的上按一次 用getch() 兩次會是 224 72
下面的全域變數宣告 是 當初還不習慣用指標,先用全域變數這樣比較簡單
下面那些全域變數內的陣列 是設定初始按鍵值 與 動作名稱 和按鍵名稱,結構體是用來放這些資料...
只是我不把初始值直接寫在結構體內,是因為
1.寫在結構體會太多{},我很容易漏掉..}or{ 2.我要拿來當初始值用的
另外按鍵初始值 設定三個陣列是 動作名稱,按鍵讀取值,按鍵顯示名稱
struct link{ char key_name[5]; int key_load; char key_prev[7]; }; /* 全域變數*/ struct link key_set[6]; char wall[9][9]; /*按鍵 初始值*/ //以下按鍵初始值 設定三個陣列是 動作名稱,按鍵讀取值,按鍵顯示名稱 //例如: 上 119 w 。"上"是動作名字,"119"是按下鍵盤後getch的讀值, //"w"是按下鍵盤後那個鈕的名稱 char name[6][5]={{"上"},{"下"},{"左"},{"右"},{"重新"},{"離開"}}; /*動作名稱*/ int load[6]={72,80,75,77,32,27}; /*按鍵讀取值*/ char prev[6][7]={{24},{25},{27},{26},{"空白鍵"},{"ESC"}}; /*按鍵顯示名稱*/ /*按鍵 初始值*/ struct link key_set[6]; /* 全域變數*/ void board_set(void){ int i=0; for(i=0;i<6;i++) { strcpy(key_set[i].key_name,name[i]); strcpy(key_set[i].key_prev,prev[i]); key_set[i].key_load=load[i]; } }_____________________________________________________________________________
第四步、控制按鍵的修改
這個部分遇到的困難是 getch()讀取的值我設定為數值,但數值轉字元我不曉得,其實就是下面將大寫轉為小寫那部分的概念,只是那時懵懵懂懂念頭沒通達.
另一部分是 字串 為眾多 字元 組合,並且最後面多一個'\0'
void get_proc(void){ char ans,transl[1][7]; /*中文字2個字元配上最後一個字元'\0'*/ int i,j,temp,check=0,count; while(check<2){ system("cls"); printf("吃金幣小遊戲 按建設定\n"); for(i=0;i<6;i++) printf("動作<%5s>對應按鍵<%7s>\n",key_set[i].key_name,key_set[i].key_prev); printf("是否重新設定按鍵?\n"); printf("是(Y/y)/否(N/n)/還原(O/o)\n"); /*鍵盤讀取值的大小寫轉換*/ ans=getch(); if(ans>='A'&&ans<='Z') ans='a'+(ans-'A'); if(ans=='y'){ /*如果是按 y 會有甚麼樣的行動*/ check=1; /*check 是 只要不是按到 N/n 就會不斷的跑修改程式*/ for(i=0;i<6;i++){ printf("動作<%5s>原始對應按鍵為<%7s>,預修改為 <請輸入所想設定按鍵>", key_set[i].key_name,key_set[i].key_prev); if((temp=getch())==224||temp==0) //temp==32||temp==27 32和27也是某些按鍵的值, temp=getch(); //不過我只用方向鍵和F1-F12 而已 key_set[i].key_load=temp; count=0; //count 是用來對照 有沒有預設名稱的按鍵 數值, //都沒有就以字元來計算. //這邊是為了 按鍵按下去之後讀取得是數值,而數值 無法和 按鍵名稱連結, //如同 空白鍵 按下後我們讀到的數值會是32 //然而 為了使我們能看到這個按鍵的名稱,我在初始值那邊尋找數值且對照相應的名字, //讀到 32 顯示值會是 空白鍵 for(j=0;j<6;j++) { if(key_set[i].key_load==load[j]) strcpy(key_set[i].key_prev,prev[j]); else count+=1; } if(count==6){ transl[0][0]=temp-65+'A'; transl[0][1]='\0'; strcpy(key_set[i].key_prev,transl[0]); } printf("%s\n",key_set[i].key_prev); } printf("已設定完,請按壓任意鍵離開"); getch(); system("cls"); } else if(ans=='n'){ //如果按 n,則讓check=2,會使迴圈跳出 check=2; system("cls"); } else if(ans=='o'){ //按o代表還原按鍵設定 check=1; board_set(); } else //按其他任何鍵 繼續迴圈 check=0; } }_____________________________________________________________________________
第五步、移動鍵 的使用 與 地圖上的顯示
利用 副程式 與 指標 和XOR位元運算子 來使人物進行移動的動作
以前不知道在哪看到的,XOR 可以讓轉換不用再定義一個中間值.
1010^0110=1100 1100^1010=0110 0110^1100=1010
/*人物行為與按鍵的關係*/ void control(int *temp,int *i,int *j,int *goldcount){ //按動作鍵 上 之後的動作 if(*temp==key_set[0].key_load&&wall[*i-1][*j]!='1'){ //換位程式,人物字元位置會變成wall[i-1][j] wall[*i][*j]=wall[*i][*j]^wall[*i-1][*j]; wall[*i-1][*j]=wall[*i][*j]^wall[*i-1][*j]; wall[*i][*j]=wall[*i][*j]^wall[*i-1][*j]; //wall[i][j]原本字元0會變成空白 wall[*i][*j]=' '; //(x,y)座標值的變化,這邊往上只動了y方向數值變化,因此只移動了i=i-1 *i=*i-1; }其餘三個方向也是差不多的設置方法
_____________________________________________________________________________
第六步、設定遊戲開始
我還是把他設定為一個副程式來運作,不過其實這個應該直接放主程式就好.
void gamestart(void){ int i,jx=4,iy=4,tempo=0,count=0,back=0; printf("吃金幣小遊戲\n"); for(i=0;i<6;i++) printf("%5s:%7s\n", key_set[i].key_name,key_set[i].key_prev); printf("按下<重新>即開始,<離開>即結束"); while(back<1&&count<3){ tempo=getch(); if(tempo==0||tempo==224) tempo=getch(); /*預防有人設定離開在 有斷碼的按鍵*/ if(tempo==key_set[5].key_load) back=back+1; //此清除畫面為 清除"當前跳離遊戲,請按<重新>開始遊戲" else if(tempo==key_set[4].key_load){ system("cls"); /*遊戲開始的各項數值初始值*/ jx=iy=4; p_on(); while(tempo!=key_set[5].key_load){ control(&tempo,&jx,&iy); //system("cls"); nextmap_v(&jx,&iy); printmap(); printf("\n目前座標[%d, %d]\n",jx,iy); tempo=getch(); if(tempo==0||tempo==224) tempo=getch(); } printf("當前跳離遊戲,請按<重新>開始遊戲"); } else{ //system("cls"); printmap(); printf("\n座標[%d, %d]\n",jx,iy); printf("按下<重新>即開始,<離開>即結束,亂按3次即離開,目前已按%d次",count+1); count=count+1; } } }_____________________________________________________________________________
到這邊人物可以行走之後,才開始修改各種副程式增加
1.其他圍牆(1)與金幣(0)
2.吃完金幣後的第二~n層
3.吃多少金幣會對應到相應得等級
(不過上面我的副程式已經是在最後近乎完成版本時才想到寫個網頁,來介紹...
不然其實本來會有很多printf之類的東西來驗證 有沒有寫錯)
_____________________________________________________________________________
最後程式 長這樣,主程式拿來副程式,不過因為很多東西都分意區塊一區塊,所以有很多東西都在整合好後重複到.
#include <stdio.h> #include <stdlib.h> #include <conio.h> /* 函式 getch() */ #include <string.h> /* 函式 strcpy() */ /*副程式區域*/ void p_on(int*); /*圍牆參數設定 與亂數圍牆生成 */ void printmap(); /*列印出整個地圖*/ void board_set(void); /*按鍵初始設定與初始功能設置*/ void get_proc(void); /*控制按鍵的修改*/ void control(int*,int*,int*,int*); /*人物行為與按鍵的關係*/ void gamestart(void); /*遊戲開始*/ /*檢查金幣是否吃完進入下一層與顯示等級用與人物圖案*/ void nextmap_v(int*,int*,int*,int*,int*); /*副程式區域*/ struct link{ char key_name[5]; int key_load; char key_prev[7]; }; struct link key_set[6]; char wall[9][9]; /*按鍵 初始值*/ //以下按鍵初始值 設定三個陣列是 動作名稱,按鍵讀取值,按鍵顯示名稱 //例如: 上 119 w 上是動作名字,119是按下鍵盤後getch的讀值, //w是按下的鍵的名稱 //動作名稱 char name[6][5]={{"上"},{"下"},{"左"},{"右"},{"重新"},{"離開"}}; //按鍵讀取值 int load[6]={72,80,75,77,32,27}; //按鍵顯示名稱 char prev[6][7]={{24},{25},{27},{26},{"空白鍵"},{"ESC"}}; //按鍵 初始值 int main() { board_set(); get_proc(); gamestart(); system("pause"); system("cls"); return 0; }_____________________________________________________________________________
遊戲畫面(我不知道怎麼控制視窗大小所以沒弄)
C程式碼與小遊戲 連結:
https://drive.google.com/drive/folders/16PYiYvS-OqXuO4-FNq2s94PonqsLh6nR?usp=sharing
剛進小遊戲時跑出的按鍵設定 |
準備開始遊戲 |
開始吃金幣 |
_____________________________________________________________________________
不過整個寫完後才發現 有一些缺點
1.因為各個部分都是做一步想一步,2-3天內才做完,昨天與今天的想法會有衝突(有時會茅塞頓開),所以有很多地方重複定義到.
2.檢查 地圖上是否還有金幣 其實可以在生成地圖時使用記數把金幣數量定明,可以不用再寫一個迴圈檢查.
3.在亂數產生地圖,可能可以在產生地圖檢查是否有無法導通的路段,不過我光是額外增加的檢查機制就有點寫不出來...寫出來感覺同個點最多會檢查到3次,要是檢查1次的會有造成死路的窘境...不知道該怎麼解決。
4.畫面刷新有查到system("cls")以外的做法,只是涉及到一些沒用過的寫法,還沒參透.
5.不知道該怎麼控制小遊戲的畫面大小.
沒有留言:
張貼留言