1. 程式人生 > 程式設計 >Linux下用C語言實現推箱子游戲

Linux下用C語言實現推箱子游戲

前面有Linux的常用命令和vim文字編輯器還沒有介紹,之後我會補上的。
今天來介紹如何用C語言寫一個簡單的小遊戲,叫做“小老鼠推箱子”。雖然遊戲的編寫過程不復雜,但是我覺得能夠從中找到自己對於程式設計的不足和完善自己的程式設計思維是最重要的。遊戲程式碼不多,所以直接寫在一個c檔案中。本人小白,有不足之處還望指教

遊戲介紹

下圖是遊戲開始介面,$是小老鼠,#是牆,O是終點,當小老鼠把所有箱子推進終點就代表遊戲通過。

Linux下用C語言實現推箱子游戲

遊戲思維

遊戲地圖用一個二維陣列去儲存。陣列中不同的值代表不同的物件(老鼠、牆、路等)。當小老鼠在移動時,陣列中的值也會隨之改變,但是為了在遊戲無法進行下去時可以從新開始遊戲,所以需要另一個數組去保留地圖的初始狀態。也就是說需要兩個二維陣列,一個始終不改變,一個用來記錄實時的狀態。

小老鼠在移動時會遇到兩種情況,即可移動和不可移動。在編寫程式碼時,只需要將可移動的情況寫完成,那麼就不需要去判斷不可移動的狀態。可移動的情況有下面兩種:

1、小老鼠面前什麼也沒有(面前是路或者是終點)。
2、小老鼠前面是箱子,此時分為兩種情況(箱子前面是路或者箱子前面是終點)。
這時候,應當思考的是如何根據二維陣列的值去判斷上述情況,以及當小老鼠移動和推動箱子時,二維陣列的值是如何變化的。

#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,0},{0,1},{1,3,2,4,0}
},boards[7][8]={};
//記錄小老鼠在移動中的位置
int row = 0;
int col = 0;
int cnt = 0;//箱子個數,用來判斷遊戲是否結束。

用0~4表示地圖上面有的小老鼠等物體,值得提的是對於箱子和小老鼠來說,他們移動時都可能會在終點上,那麼他們移入終點和移開終點時的情況需要分別去判斷,為了能夠避免過多的判斷,當它們移入終點時,終點所在位置的值就等於它們的值加上終點的值。同理,當它們移開終點時,終點所在位置的值就等於終點的值減去它們的值。這樣就不用去判斷小老鼠和箱子在不在終點上了。所以,小老鼠在終點上的值是7。

程式碼詳解

這裡先從main()函式開始思考。當遊戲開始時,需要先將地圖初始化,即把用來記錄實時狀態的地圖初始化成遊戲開始時的地圖(就是前面提的的兩個地圖),所以這裡需要init()函式。初始化後就是開始遊戲,遊戲過程中需要獲取方向鍵,這些在start()函式中完成。之後,在這些函式中再去思考是否需要去寫其他函式補足功能。

//初始化地圖
void init()
{
 for(int i=0; i<7*8; i++)
 { //這裡是為了當你按下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++) //統計遊戲是否結束,當箱子都在終點上時就結束了
 {
 for(int j=0; j<8; j++)
 {
  if(5==boards[i][j])
  {
  cnt++;
  }
  if(3==cnt)
  {
  printf("通關!\n");
  exit(0);//通關退出遊戲
  }
 }
 }
 cnt=0; //如果遊戲沒有結束,下次還是需要從0統計
}

//開始遊戲
void start()
{
 while(1)
 {
 print();//遊戲開始時需要列印地圖,每次從鍵盤上獲得方向移動後也需要列印地圖
 int dir=get_keyboard(); //從鍵盤獲取方向
 system("clear"); //下次列印地圖時,先將螢幕清空
 switch(dir) //每次都需要判斷小老鼠前面和前面的前面的座標的狀態,這樣可以在move()
   //函式中統一各個方向的寫法
 {
  case KEY_UP: move(row-1,col,row-2,col);break;
  case KEY_DOWN: move(row+1,row+2,col);break;
  case KEY_LEFT: move(row,col-1,row,col-2);break;
  case KEY_RIGHT: move(row,col+1,col+2);break;
  case KEY_ENTER:
   init();break; //按enter則從新開始遊戲
  case KEY_q:
   exit(0);//如果按q則退出遊戲
 }
 }
}

int main()
{
 //遊戲初始化
 init();
 system("clear"); //系統命令,用來清空螢幕

 //開始遊戲
 start();

 return 0;
}

好了,這就是今天的內容。這裡需要的用來獲取鍵盤的標頭檔案直接貼上在下面。

#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;

//此函式能立即從鍵盤不回顯的接收資料
static int get_keyboard(void)
{
 //接收系統呼叫的執行結果
 int ret = 0;
 //儲存終端裝置的配置資訊
 struct termios old;
 //通過系統呼叫獲取終端的配置資訊
 ret=tcgetattr(STDIN_FILENO,&old);
 if(0 > ret)
 {
 perror("tcgetattr");
 return -1;
 }
 //初始化新的終端配置資訊
 struct termios new = old;
 //取消回顯並立即獲取
 new.c_lflag &= ~(ICANON|ECHO);
 //設定新的終端配置資訊
 ret= tcsetattr(STDIN_FILENO,TCSANOW,&new);
 if(0 > ret)
 {
 perror("tcsetattr");
 return -2;
 }
 //在新的模式下從終端獲取資料
 int key_value = 0;
 do
 {
 key_value += getchar();
 //由於和系統對FILE結構體的實現各不相同
 //linux系統 while(stdin->_IO_read_end - stdin->_IO_read_ptr);
 //OS系統 while(stdin->_r);
 }while(stdin->_IO_read_end - stdin->_IO_read_ptr);
 //還原終端的配置資訊
 ret = tcsetattr(STDIN_FILENO,&old);
 if(0 > ret)
 {
 perror("tcsetattr");
 return -3;
 }
 //返回獲取到的資料
 return key_value;
}
#endif//GETCH_H

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。