關於2048小遊戲專案的一些心得
阿新 • • 發佈:2019-02-08
這是我學習完標C和Unix C以後做的一個小遊戲,只能支援字元終端介面。在此將其貼出來,希望能夠跟大家分享一些心得。以下是我當時寫的一個專案文件
==========================2048 by Phoenix遊戲開發手冊==========================
1.遊戲在終端中的顯示:遊戲按鍵設定:
w
a s d
遊戲規則:
使用者通過按鍵移動方格中的數字,相鄰格子中的數字如果相同,則可以通過移動
相應的方向來實現相加並將這兩個數字合併,率先達到2048則獲得勝利。如果方格已
經被充滿,既無法相加也無法移動,則遊戲失敗
***************************************************************************
2.技術支援:
作業系統:Ubuntu
開發工具:vim,gcc,makefile
***************************************************************************
3.遊戲中包含的檔案:
標頭檔案:color.h,myheadfile.h
儲存檔案:SaveMap.bin,SaveScore.bin,HistoryHightscore.bin
介面建立:MapPrinter.c
邏輯判定:JudgeLogic.c
事件響應:GameLogic.c
檔案操作:FileOperate.c
主檔案(程式入口):2048.c
***************************************************************************
3.遊戲的邏輯功能實現(將相應功能模組獨立出來並形成相應的檔案):
a.遊戲邏輯:GameLogic.c(已實現)
所包含的功能(函式)有move_top,move_down,move_lef,move_right,insert_num,
run_map;
move_*函式實現當用戶使用按鍵的時候,相應的移動效果的實現
insert_num函式用於在移動後的空位中插入隨機的2或4
run_map函式用於獲得使用者的操作並作出相應的邏輯實現
b.列印地圖:MapPrinter.c(已實現)
所包含的功能(函式)有map_print,init_map,print,print_color;
map_print用於打印出地圖
init_map用於在遊戲開始時且使用者上次遊戲並未儲存的情況下,初始化地圖
print用於列印地圖的“-----”
print_color用於打印出不同顏色的數字
c.判定邏輯:JudgeLogic.c(已實現)
所包含的功能(函式)有success_judge,fail_judge;
success_judge如果滿足勝利條件則在螢幕上列印"You Win!!",並終止遊戲
fail_judge如果滿足失敗判定條件,則終止遊戲,並在螢幕上列印“You Lose!!”
d.檔案操作:FileOperate.c(已實現)
所包含的功能(函式)有open,save_hightscore,save_game;
open:開啟檔案SaveMap.txt,檢視使用者是否有儲存上次遊戲的結果,如果有則將地
圖載入進來,否則直接初始化地圖。
save_hightscore:當檢測到使用者的成績高於歷史最高分時,將成績儲存下來
save_game:使用者輸入q的時候,將地圖寫入到SaveMap.bin檔案中
e.程式入口:2048.c
所包含的功能(函式)有:main
實現功能:為程式的執行提供一個入口
****************************************************************************
4.函式原型設定:
GameLogic.c:
void move_top(int (*)[],int,int*,int*,int*);
void move_down(int (*)[],int,int*,int*,int*);
void move_left(int (*)[],int,int*,int*,int*);
void move_right(int (*)[],int,int*,int*,int*);
void run_map(int (*)[],int,int*,int*,int*,int*);
void insert_num(int (*)[],int);
MapPrinter.c:
static void print(int);
static void print_color(int (*)[],int,int);
void init_map(int (*)[],int);
void map_printer(int (*)[],int,int*,int*);
JudgeLogic.c:
int success_judge(int (*)[],int);
int fail_judge(int (*)[],int,int,int);
FileOperate.c:
int open(int (*)[],int,int*,int*);
int save_hightscore(int);
int save_game(int (*)[],int,int);
2048.c
int main(void);
****************************************************************************
以下是相應的檔案實現程式碼:
標頭檔案:
color.h(定義了Linux中終端顏色顯示欄位)
myheadfile.h(聲明瞭整個2048遊戲中的函式及其共享巨集變數)#ifndef __COLOR__H #define __COLOR__H //定義字型顏色 #define NONE "\033[m" //取消列印的顏色,列印完數字後就要取消掉顏色列印 #define RED_2 "\033[0;40;31m" //黑底紅色 2 #define GREEN_4 "\033[0;40;32m" //黑底綠色 4 #define DARKYELLOW_8 "\033[0;40;33m" //黑底土黃 8 #define BLUE_16 "\033[0;40;36m" //黑底藍色 16 #define PURPLE_32 "\033[0;40;35m" //黑底紫紅 32 #define LIGHT_ORANGE_64 "\033[1;40;31m" //黑底淺橘 64 #define ORANGE_128 "\033[1;40;32m" //黑底橘紅 128 #define YELLOW_256 "\033[1;40;33m" //黑底黃色 256 #define LIGHT_PURPLE_512 "\033[1;40;34m" //黑底淺紫 512 #define PINK_1024 "\033[1;40;35m" //黑底粉字 1024 #define CAYN_2048 "\033[1;40;36m" //黑底青色 2048 #define BACKGROUND "\033[1;40m" //黑色背景 #endif //__COLOR__H
#ifndef __MYHEADFILE__H #define __MYHEADFILE__H //共享的巨集變數定義 #include <stdlib.h> #include <stdio.h> #include <time.h> #define SIZE 4 #define FILENAME map.txt #define FILESCORE score.bin //函式宣告 //GameLogic.c void move_top(int (*)[],int,int*,int*,int*); void move_down(int (*)[],int,int*,int*,int*); void move_left(int (*)[],int,int*,int*,int*); void move_right(int (*)[],int,int*,int*,int*); void insert_num(int (*)[],int); void run_map(int (*)[],int,int*,int*,int*,int*); //FileOperate.c int open(int (*)[],int,int*,int*); int save_hightscore(int); int save_game(int (*)[],int,int); //MapPrinter.c void init_map(int (*)[],int); void map_printer(int (*)[],int,int*,int*); //JudgeLogic.c int success_judge(int (*)[],int); int fail_judge(int (*)[],int,int,int); #endif
C檔案:
2048,c(遊戲入口檔案)
#include "myheadfile.h"
int main(void){
srand(time(0));
int arr[SIZE][SIZE] = {};
int IsMove,IsPlus,score = 0,hightscore = 0;
if(open(arr,4,&score,&hightscore)){
map_printer(arr,4,&score,&hightscore);
}
else{
init_map(arr,4);
map_printer(arr,4,&score,&hightscore);
}
run_map(arr,4,&score,&hightscore,&IsMove,&IsPlus);
return 0;
}
MapPrinter.c(負責列印遊戲介面)
#include "color.h"
#include "myheadfile.h"
static void print(int size){
int i;
for(i = 0;i < size;i++){
printf("-----");
}
printf("\n");
}
void init_map(int (*arr)[SIZE],int size){
int randone = rand()%size;
int randtwo = rand()%size;
int randthree = rand()%size;
int randfour = rand()%size;
arr[randone][randtwo]=2;
arr[randthree][randfour]=4;
}
static void print_color(int (*arr)[SIZE],int size,int row,int col){
switch(arr[row][col]){
case 0:
printf("|"BACKGROUND" "NONE);
break;
case 2:
printf("|"RED_2"2 "NONE);
break;
case 4:
printf("|"GREEN_4"4 "NONE);
break;
case 8:
printf("|"DARKYELLOW_8"8 "NONE);
break;
case 16:
printf("|"BLUE_16"16 "NONE);
break;
case 32:
printf("|"PURPLE_32"32 "NONE);
break;
case 64:
printf("|"LIGHT_ORANGE_64"64 "NONE);
break;
case 128:
printf("|"ORANGE_128"128 "NONE);
break;
case 256:
printf("|"YELLOW_256"256 "NONE);
break;
case 512:
printf("|"LIGHT_PURPLE_512"512 "NONE);
break;
case 1024:
printf("|"PINK_1024"1024"NONE);
break;
case 2048:
printf("|"CAYN_2048"2048"NONE);
break;
default:
break;
}
}
void map_printer(int (*arr)[SIZE],int size,int *score,int *hightscore){
int i,j;
system("clear");
printf("2048 by Phoenix\n");
printf("Score:%d\tHightScore:%d\n",*score,*hightscore);
print(size);
for(i = 0;i < size;i++){
for(j = 0;j < size;j++){
print_color(arr,size,i,j);
}
printf("|\n");
print(size);
}
}
GameLogic.c(實現遊戲的相應操作)
#include "myheadfile.h"
//向上移動
void move_top(int (*arr)[SIZE],int size,int* score,int* IsPlus,int *IsMove){
int i,j,k;
for(i = 0;i < size;i++){
k=0;
while(arr[k][i]&&k<size){ //找到同列不同行中值為0的數並得到行號
k++;
}
for(j = k+1;j < size;j++){
if(arr[j][i]!=0){
arr[k][i] = arr[j][i];
arr[j][i] = 0;
k++;
*IsMove = 2;
}
}
for(k=0;k<size-1;k++){
if(arr[k][i] == arr[k+1][i]&&arr[k][i]!=0){
arr[k][i] *= 2;
*score += arr[k][i];
arr[k+1][i] = 0;
*IsPlus = 1;
*IsMove = 1;
}
}
}
for(i = 0;i < size;i++){
k=0;
while(arr[k][i]&&k<size){
k++;
}
for(j = k+1;j < size;j++){
if(arr[j][i]!=0){
arr[k][i] = arr[j][i];
arr[j][i] = 0;
k++;
*IsMove = 1;
}
}
}
}
//向下移動
void move_down(int (*arr)[SIZE],int size,int* score,int* IsPlus,int* IsMove){
int i,j,k;
for(i = 0;i < size;i++){
k=size-1;
while(arr[k][i]&&k>=0){ //找到同列不同行中值為0的數並得到行號
k--;
}
for(j = k-1;j >= 0;j--){
if(arr[j][i]!=0){
arr[k][i] = arr[j][i];
arr[j][i] = 0;
k--;
*IsMove = 1;
}
}
for(k=size-1;k>0;k--){
if(arr[k][i] == arr[k-1][i]&&arr[k][i]!=0){
arr[k][i] *= 2;
*score += arr[k][i];
arr[k-1][i] = 0;
*IsPlus = 1;
*IsMove = 1;
}
}
}
for(i = 0;i < size;i++){
k=size-1;
while(arr[k][i]&&k>=0){
k--;
}
for(j = k-1;j >= 0;j--){
if(arr[j][i]!=0){
arr[k][i] = arr[j][i];
arr[j][i] = 0;
k--;
*IsMove = 1;
}
}
}
}
//向左移動
void move_left(int (*arr)[SIZE],int size,int* score,int* IsPlus,int* IsMove){
int i,j,k;
for(i = 0;i < size;i++){
k = 0;
while(arr[i][k]&&k<size){ //找到同列不同行中值為0的數並得到行號
k++;
}
for(j = k+1;j < size;j++){
if(arr[i][j]!=0){
arr[i][k] = arr[i][j];
arr[i][j] = 0;
k++;
*IsMove = 1;
}
}
for(k=0;k<size-1;k++){
if(arr[i][k] == arr[i][k+1]&&arr[i][k]!=0){
arr[i][k] *= 2;
*score += arr[i][k];
arr[i][k+1] = 0;
*IsPlus = 1;
*IsMove = 1;
}
}
}
for(i = 0;i < size;i++){
k=0;
while(arr[i][k]&&k<size){
k++;
}
for(j = k+1;j < size;j++){
if(arr[i][j]!=0){
arr[i][k] = arr[i][j];
arr[i][j] = 0;
k++;
*IsMove = 1;
}
}
}
}
//向右移動先移動,再判斷相加,最後再移動一次
void move_right(int (*arr)[SIZE],int size,int* score,int* IsPlus,int* IsMove){
int i,j,k;
for(i = 0;i < size;i++){
k=size-1;
while(arr[i][k]&&k>=0){ //找到同列不同行中值為0的數並得到行號
k--;
}
//向右移動
for(j = k-1;j >= 0;j--){
if(arr[i][j]!=0){
arr[i][k] = arr[i][j];
arr[i][j] = 0;
k--;
*IsMove = 1;
}
}
/****************************************************************/
//判斷相加
for(k=size-1;k>0;k--){
if(arr[i][k] == arr[i][k-1]&&arr[i][k]!=0){
arr[i][k] *= 2;
*score += arr[i][k];
arr[i][k-1] = 0;
*IsPlus = 1;
*IsMove = 1;
}
}
/******************************************************************/
}
//移動
for(i = 0;i < size;i++){
k=size-1;
while(arr[i][k]&&k>=0){
k--;
}
for(j = k-1;j >= 0;j--){
if(arr[i][j]!=0){
arr[i][k] = arr[i][j];
arr[i][j] = 0;
k--;
*IsMove = 1;
}
}
}
}
/************************************************************************/
void insert_num(int (*arr)[SIZE],int size){
int randX,randY,randnum;
randnum = rand()%2; //randnum的值為0或1,0表示在,1表示4
do{
randX=rand()%size;
randY=rand()%size;
if(arr[randX][randY] == 0){
if(randnum == 0){
arr[randX][randY] = 2;
break;
}
else{
arr[randX][randY] = 4;
break;
}
}
}while(1);
}
void run_map(int (*arr)[SIZE],int size,int *score,int *hightscore,int *IsMove,int *IsPlus){
char input;
int i,j;
int save = 0;
while(1){
*IsMove = 0;
*IsPlus = 0;
scanf("%c",&input);
scanf("%*[^\n]");
scanf("%*c");
switch(input){
case 'w':
move_top(arr,4,score,IsPlus,IsMove);
break;
case 's':
move_down(arr,4,score,IsPlus,IsMove);
break;
case 'a':
move_left(arr,4,score,IsPlus,IsMove);
break;
case 'd':
move_right(arr,4,score,IsPlus,IsMove);
break;
case 'q':
printf("Saving……,Please wait!!\n");
save = save_game(arr,size,*score);
sleep(1);
if(save){
printf("The game has been saved\n");
exit(0);
}
else{
printf("Save failed!!\n");
}
break;
default:
printf("Input error!!\n");
break;
}
if(*IsMove){
insert_num(arr,4);
}
map_printer(arr,4,score,hightscore);
if(success_judge(arr,4)){
printf("You Win!!\n");
for(i=0;i<size;i++){
for(j=0;j<size;j++){
arr[i][j] = 0;
}
}
save_hightscore(*score); //儲存最高分數
*score = 0;
save_game(arr,size,*score);
exit(0);
}
if(fail_judge(arr,4,*IsMove,*IsPlus)){
printf("You Lose!!\n");
for(i=0;i<size;i++){
for(j=0;j<size;j++){
arr[i][j] = 0;
}
}
save_hightscore(*score); //儲存最高分
*score = 0;
save_game(arr,size,*score);
exit(0);
}
}
}
JudgeLogic.c(實現對遊戲輸贏的判定)#include "myheadfile.h"
int success_judge(int (*arr)[SIZE],int size){
int i,j,success = 0;
for(i = 0;i < size;i++){
for(j = 0;j < size;j++){
if(arr[i][j] == 2048){
success = 1;
}
}
}
return success;
}
int fail_judge(int (*arr)[SIZE],int size,int IsMove,int IsPlus){
int i,j,tmp = 0,fail = 0,col = 0,row = 0;
//迴圈判斷陣列中是否還有空位,有則tmp=1,無則tmp=0
for(i = 0;i < size;i++)
for(j = 0;j < size;j++){
if(!arr[i][j])
tmp = 1;
}
for(i = 0;i < size;i++)
for(j = 0;j < size-1;j++){
if(arr[i][j] == arr[i][j+1] && arr[i][j] != 0){
col = 1;
}
if(arr[j][i] == arr[j+1][i] && arr[j][i] != 0){
row = 1;
}
}
if(!IsMove && !IsPlus && !tmp && !col && !row){
fail = 1;
}
return fail;
}
FileOperate.c(通過檔案來儲存相應的玩家資訊及歷史最高分)
#include "myheadfile.h"
int open(int (*arr)[SIZE],int size,int *score,int *hightscore){
FILE *p_file = fopen("SaveMap.bin","r");
int i,j,failed = 0;
if(NULL == p_file){
printf("File open error!!\n");
return 0;
}
else{
for(i = 0;i < size;i++){
failed = fread(arr[i],sizeof(int),size,p_file);
}
for(i = 0;i < size;i++){
for(j = 0;j < size-1;j++){
if(arr[i][j] == arr[i][j-1] && !arr[i][j]){
failed = 0;
}
else{
failed = 1;
}
}
}
fclose(p_file);
p_file = fopen("SaveScore.bin","r");
if(NULL == p_file){
printf("ERROR:Can not read SaveScore.bin\n");
return 0;
}
else{
fread(score,sizeof(int),1,p_file);
fclose(p_file);
p_file = fopen("HistoryHightscore.bin","r");
if(NULL == p_file){
printf("ERROR:Can not read HistoryHightscore.bin\n");
return 0;
}
else{
fread(hightscore,sizeof(int),1,p_file);
printf("11111111111111111:%d\n",*hightscore);
fclose(p_file);
p_file = NULL;
}
}
return failed;
}
}
int save_hightscore(int score){
FILE *p_file = fopen("HistoryHightscore.bin","r+");
int hightscore;
if(NULL == p_file){
printf("ERROR:Can not open HistoryHightscore.c!!\n");
return 0;
}
else{
fread(&hightscore,sizeof(int),1,p_file);
if(score > hightscore){
rewind(p_file);
fwrite(&score,sizeof(int),1,p_file);
}
fclose(p_file);
p_file = NULL;
return 1;
}
}
int save_game(int (*arr)[SIZE],int size,int score){
FILE *p_file = fopen("SaveMap.bin","w");
int i;
if(NULL == p_file){
printf("ERROR:Can not open SaveMap.bin!!\n");
return 0;
}
else{
for(i = 0;i < size;i++){
fwrite(arr[i],sizeof(int),size,p_file);
}
fclose(p_file);
p_file = fopen("SaveScore.bin","w");
if(NULL == p_file){
printf("ERROR:Can not open SaveScore.bin!!\n");
return 0;
}
else{
fwrite(&score,sizeof(int),1,p_file);
fclose(p_file);
p_file = NULL;
return 1;
}
}
}
最終的程式執行結果: