Linux下用C語言實現(xiàn)推箱子游戲
前面有Linux的常用命令和vim文本編輯器還沒有介紹,之后我會補上的。
今天來介紹如何用C語言寫一個簡單的小游戲,叫做“小老鼠推箱子”。雖然游戲的編寫過程不復(fù)雜,但是我覺得能夠從中找到自己對于編程的不足和完善自己的編程思維是最重要的。游戲代碼不多,所以直接寫在一個c文件中。本人小白,有不足之處還望指教
游戲介紹
下圖是游戲開始界面,$是小老鼠,#是墻,O是終點,當(dāng)小老鼠把所有箱子推進終點就代表游戲通過。

游戲思維
游戲地圖用一個二維數(shù)組去存儲。數(shù)組中不同的值代表不同的對象(老鼠、墻、路等)。當(dāng)小老鼠在移動時,數(shù)組中的值也會隨之改變,但是為了在游戲無法進行下去時可以從新開始游戲,所以需要另一個數(shù)組去保留地圖的初始狀態(tài)。也就是說需要兩個二維數(shù)組,一個始終不改變,一個用來記錄實時的狀態(tài)。
小老鼠在移動時會遇到兩種情況,即可移動和不可移動。在編寫代碼時,只需要將可移動的情況寫完成,那么就不需要去判斷不可移動的狀態(tài)??梢苿拥那闆r有下面兩種:
1、小老鼠面前什么也沒有(面前是路或者是終點)。
2、小老鼠前面是箱子,此時分為兩種情況(箱子前面是路或者箱子前面是終點)。
這時候,應(yīng)當(dāng)思考的是如何根據(jù)二維數(shù)組的值去判斷上述情況,以及當(dāng)小老鼠移動和推動箱子時,二維數(shù)組的值是如何變化的。
#include <stdio.h>
#include "get_keyboard.h"
//地圖7行8列 行[0-6] 列[0-7]
//0 路
//1 墻
//2 箱子
//3 終點
//4 小老鼠
//7 小老鼠站在終止上
//5 箱子到達終點上
int g_boards[7][8] =
{
{0,1,1,1,1,1,1,0},
{0,1,0,0,0,0,1,1},
{1,3,0,1,1,2,0,1},
{1,0,3,3,2,0,0,1},
{1,0,0,1,2,0,0,1},
{1,0,0,4,0,1,1,1},
{1,1,1,1,1,0,0,0}
}, boards[7][8]={};
//記錄小老鼠在移動中的位置
int row = 0;
int col = 0;
int cnt = 0;//箱子個數(shù),用來判斷游戲是否結(jié)束。
用0~4表示地圖上面有的小老鼠等物體,值得提的是對于箱子和小老鼠來說,他們移動時都可能會在終點上,那么他們移入終點和移開終點時的情況需要分別去判斷,為了能夠避免過多的判斷,當(dāng)它們移入終點時,終點所在位置的值就等于它們的值加上終點的值。同理,當(dāng)它們移開終點時,終點所在位置的值就等于終點的值減去它們的值。這樣就不用去判斷小老鼠和箱子在不在終點上了。所以,小老鼠在終點上的值是7。
代碼詳解
這里先從main()函數(shù)開始思考。當(dāng)游戲開始時,需要先將地圖初始化,即把用來記錄實時狀態(tài)的地圖初始化成游戲開始時的地圖(就是前面提的的兩個地圖),所以這里需要init()函數(shù)。初始化后就是開始游戲,游戲過程中需要獲取方向鍵,這些在start()函數(shù)中完成。之后,在這些函數(shù)中再去思考是否需要去寫其他函數(shù)補足功能。
//初始化地圖
void init()
{
for(int i=0; i<7*8; i++)
{ //這里是為了當(dāng)你按下enter鍵時,能夠重置游戲,重置游戲時需要重新初始化地圖
boards[i/8][i%8]=g_boards[i/8][i%8];
if(4==boards[i/8][i%8]) //這里還需要記錄老鼠的位置
{
row=i/8;
col=i%8;
}
}
}
//打印地圖
void print()
{
for(int i=0; i<7; i++)
{
for(int j=0; j<8; j++)
{
switch(boards[i][j])
{
case 0:
printf(" ");break; //空格代表路徑
case 1:
printf("#");break; //打印墻
case 2:
case 5:
printf("@");break; //打印箱子
case 3:
printf("O");break; //打印終點
case 4:
case 7:
printf("$");break; //打印小老鼠
}
}
printf("\n");
}
}
//推箱子
void move(int nrow, int ncol, int nnrow, int nncol)
{
if(0==boards[nrow][ncol] || 3==boards[nrow][ncol]) //如果小老鼠前面是路或者終點,說明可以移動
{
boards[nrow][ncol] +=4;//小老鼠進入這個位置了
boards[row][col] -=4;//小老鼠離開這個位置了
//移動過后小老鼠的位置也改變了
row = nrow;
col = ncol;
}
else if(2==boards[nrow][ncol] || 5==boards[nrow][ncol]) //如果小老鼠前面是箱子
{
if(0==boards[nnrow][nncol] || 3==boards[nnrow][nncol]) //如果箱子前面是路或者終點則可以移動
{
boards[nnrow][nncol] +=2;
boards[nrow][ncol] -=2-4;
//這里其實是boards[nrow][ncol]=boards[nrow][ncol]-2+4
//減去2是因為箱子移走了,加上4是因為小老鼠進入了
boards[row][col] -=4;
row=nrow;
col=ncol;
}
}
for(int i=0; i<7; i++) //統(tǒng)計游戲是否結(jié)束,當(dāng)箱子都在終點上時就結(jié)束了
{
for(int j=0; j<8; j++)
{
if(5==boards[i][j])
{
cnt++;
}
if(3==cnt)
{
printf("通關(guān)!\n");
exit(0);//通關(guān)退出游戲
}
}
}
cnt=0; //如果游戲沒有結(jié)束,下次還是需要從0統(tǒng)計
}
//開始游戲
void start()
{
while(1)
{
print();//游戲開始時需要打印地圖,每次從鍵盤上獲得方向移動后也需要打印地圖
int dir=get_keyboard(); //從鍵盤獲取方向
system("clear"); //下次打印地圖時,先將屏幕清空
switch(dir) //每次都需要判斷小老鼠前面和前面的前面的坐標(biāo)的狀態(tài),這樣可以在move()
//函數(shù)中統(tǒng)一各個方向的寫法
{
case KEY_UP: move(row-1, col, row-2, col);break;
case KEY_DOWN: move(row+1, col, row+2, col);break;
case KEY_LEFT: move(row, col-1, row, col-2);break;
case KEY_RIGHT: move(row, col+1, row, col+2);break;
case KEY_ENTER:
init();break; //按enter則從新開始游戲
case KEY_q:
exit(0);//如果按q則退出游戲
}
}
}
int main()
{
//游戲初始化
init();
system("clear"); //系統(tǒng)命令,用來清空屏幕
//開始游戲
start();
return 0;
}
好了,這就是今天的內(nèi)容。這里需要的用來獲取鍵盤的頭文件直接粘貼在下面。
#ifndef GETCH_H
#define GETCH_H
#include <stdio.h>
#include <termios.h>
#include <stdlib.h>
#include <unistd.h>
typedef enum KEYBOARD
{
KEY_UP = 183,
KEY_DOWN = 184,
KEY_RIGHT = 185,
KEY_LEFT = 186,
KEY_BACKSPACE = 127,
KEY_ENTER = 10,
KEY_0 = 48,
KEY_1 = 49,
KEY_2 = 50,
KEY_3 = 51,
KEY_4 = 52,
KEY_5 = 53,
KEY_6 = 54,
KEY_7 = 55,
KEY_8 = 56,
KEY_9 = 57,
KEY_A = 65,
KEY_B = 66,
KEY_C = 67,
KEY_D = 68,
KEY_E = 69,
KEY_F = 70,
KEY_G = 71,
KEY_H = 72,
KEY_I = 73,
KEY_J = 74,
KEY_K = 75,
KEY_L = 76,
KEY_M = 77,
KEY_N = 78,
KEY_O = 79,
KEY_P = 80,
KEY_Q = 81,
KEY_R = 82,
KEY_S = 83,
KEY_T = 84,
KEY_U = 85,
KEY_V = 86,
KEY_W = 87,
KEY_X = 88,
KEY_Y = 89,
KEY_Z = 90,
KEY_a = 97,
KEY_b = 98,
KEY_c = 99,
KEY_d = 100,
KEY_e = 101,
KEY_f = 102,
KEY_g = 103,
KEY_h = 104,
KEY_i = 105,
KEY_j = 106,
KEY_k = 107,
KEY_l = 108,
KEY_m = 109,
KEY_n = 110,
KEY_o = 111,
KEY_p = 112,
KEY_q = 113,
KEY_r = 114,
KEY_s = 115,
KEY_t = 116,
KEY_u = 117,
KEY_v = 118,
KEY_w = 119,
KEY_x = 120,
KEY_y = 121,
KEY_z = 122
}KEYBOARD;
//此函數(shù)能立即從鍵盤不回顯的接收數(shù)據(jù)
static int get_keyboard(void)
{
//接收系統(tǒng)調(diào)用的執(zhí)行結(jié)果
int ret = 0;
//存儲終端設(shè)備的配置信息
struct termios old;
//通過系統(tǒng)調(diào)用獲取終端的配置信息
ret=tcgetattr(STDIN_FILENO,&old);
if(0 > ret)
{
perror("tcgetattr");
return -1;
}
//初始化新的終端配置信息
struct termios new = old;
//取消回顯并立即獲取
new.c_lflag &= ~(ICANON|ECHO);
//設(shè)置新的終端配置信息
ret= tcsetattr(STDIN_FILENO,TCSANOW,&new);
if(0 > ret)
{
perror("tcsetattr");
return -2;
}
//在新的模式下從終端獲取數(shù)據(jù)
int key_value = 0;
do
{
key_value += getchar();
//由于和系統(tǒng)對FILE結(jié)構(gòu)體的實現(xiàn)各不相同
//linux系統(tǒng) while(stdin->_IO_read_end - stdin->_IO_read_ptr);
//OS系統(tǒng) while(stdin->_r);
}while(stdin->_IO_read_end - stdin->_IO_read_ptr);
//還原終端的配置信息
ret = tcsetattr(STDIN_FILENO,TCSANOW,&old);
if(0 > ret)
{
perror("tcsetattr");
return -3;
}
//返回獲取到的數(shù)據(jù)
return key_value;
}
#endif//GETCH_H
更多有趣的經(jīng)典小游戲?qū)崿F(xiàn)專題,分享給大家:
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C++ sdl實現(xiàn)渲染旋轉(zhuǎn)視頻的方法分享
一般情況下播放視頻時不需要旋轉(zhuǎn),但是如果是移動端錄制的視頻有時會出現(xiàn)rotate參數(shù),且視頻寬高也是互換的。所以本文為大家準(zhǔn)備了利用sdl實現(xiàn)渲染旋轉(zhuǎn)視頻的方法,需要的可以參考一下2022-12-12
C++?Cartographer源碼中關(guān)于MapBuilder的聲明與構(gòu)造
這篇文章主要介紹了C++?Cartographer源碼中關(guān)于MapBuilder的聲明與構(gòu)造,前面已經(jīng)談到了Cartographer中添加軌跡的方法和傳感器的數(shù)據(jù)流動走向。在添加軌跡的時候,除了添加位姿估計器還有采樣器,訂閱回調(diào)函數(shù)之外,最重要的是通過map_builder_bridge添加了一條軌跡2023-03-03
C++面向?qū)ο笳Z言自制多級菜單功能實現(xiàn)代碼
菜單類主要負(fù)責(zé)菜單的創(chuàng)建、修改、刪除,是包含菜單結(jié)構(gòu)組織和響應(yīng)函數(shù)的模型,用戶擁有充分的自主性,可根據(jù)需要自定義菜單顯示和響應(yīng)函數(shù),這篇文章主要介紹了C++面向?qū)ο笳Z言自制多級菜單,需要的朋友可以參考下2024-06-06

