這個是想來溫習一下好久以前學的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.不知道該怎麼控制小遊戲的畫面大小.



沒有留言:
張貼留言