製作字符遊戲貪吃蛇的學習記錄
c語言簡潔強大,我們可以利用它寫出許多程序,甚至製作一些小遊戲,比如貪吃蛇。筆者在第一次用c語言製作貪吃蛇遊戲時,頭都大了,因爲完全不懂該從何處下手開始寫代碼。後來借鑑了前輩的經驗才明白,需要用到自頂向下、逐步求精的方法,將一個貪吃蛇遊戲細分爲幾個小問題:打印地圖、蛇的移動、gameover的判定、食物的產生。然後再慢慢化簡各個小問題中的難題。在這裏給大家記錄下筆者的學習記錄,若能給大家一點幫助則是萬幸。
1.打印地圖
”),4表示食物("$")。這樣做的好處就是每次打印新地圖時可以用同一個函數,同時也便於後續的一些判定。以下是地圖的構造。
int map[22][42] = { 0 };//地圖全部初始化爲空地,最好把map設爲全局變量
for (int i = 0; i < 22; i++)
{
for (int j = 0; j < 42; j++)
if (i == 0 || i == 21 || j == 0 || j == 41)
map[i][j] = 3;
}//設置牆壁
map[1][5] = 1;//設置蛇頭
map[1][1] = 2; map[1][2] = 2; map[1][3] = 2; map[1][4] = 2;//設置蛇身
void new_map()
{
for (int i = 0; i < 22; i++)
{
for (int j = 0; j < 42; j++)
{
if (map[i][j] == 0)
printf(" ");//空地
if (map[i][j] == 1)
printf("H");//蛇頭
if (map[i][j] == 2)
printf("X");//蛇身
if (map[i][j] == 3)
printf("*");//牆壁
if (map[i][j] == 4)
printf("$");//食物
}
printf("\n");
}
2.蛇的移動
蛇如何移動是整個遊戲中最關鍵的一部分,若是一開始就考慮整條蛇的移動是很困難的,於是筆者先從最簡單的頭部的移動開始寫代碼:用x、y表示頭的橫縱座標,讀入相應的方向字符後對座標進行相應的加減就好了,如下
while (1)//死循環,之後可加入gameover的判定
{
map[y][x] = 0;//舊蛇頭歸零
scanf_s("%d", &m);
switch (m)
{
case 'w': y -= 1; break;
case 's': y += 1; break;
case 'a': x -= 1; break;
case 'd': x += 1; break;
default:
break;
}//移動
map[y][x] = 1;//新蛇頭
}
接着再考慮蛇身的移動,這裏許多前輩使用了數據構造的辦法,無奈在下學得不精,不得不放棄使用另一種方式來實現蛇的身體構造。但是一些思想還是通用的,蛇身的移動其實就是蛇頭的位置變成蛇身,然後蛇的尾巴消失。這樣一來就豁然開朗了,筆者只需要利用一個二維數據存儲每個蛇身的座標位置,再分別用兩個指針指向蛇的脖子(頭的下一節)和蛇尾,每次移動就讀入舊蛇頭即新蛇脖的座標,蛇尾座標歸零後再指向下一個蛇尾即可。代碼形式如下int head = 4, tail = 1,body[801][2] = { 0 };//筆者構建的地圖爲20×40,考慮蛇身最大長度所以設定了801
body[1][0] = 1; body[1][1] = 1; body[2][1] = 1; body[2][0] = 2; body[3][1] = 1; body[3][0] = 3; body[4][1] = 1; body[4][0] = 4;//初始蛇身的座標,0代表x軸,1代表y軸
while (1)
{
map[y][x] = 2;//舊蛇頭替換爲蛇身
head = (head + 1) % 801;//指向新蛇脖,準備讀入數據,%801爲了讓數組循環
body[head][0] = x; body[head][1] = y;//讀入新蛇脖(頭的下一節)
scanf_s("%d", &m);
switch (m)
{
case 'w': y -= 1; break;
case 's': y += 1; break;
case 'a': x -= 1; break;
case 'd': x += 1; break;
default:
break;
}
map[y][x] = 1;
map[body[tail][1]][body[tail][0]] = 0;//去掉蛇尾
tail = (tail + 1) % 801;//指向新蛇尾
new_map();//打印蛇移動後的新地圖
}
3.gameover的判定
int _life(int a)
{
if (a == 4)
return 2;//遇到食物,返回一個特殊值,便於後續判定。同理如果想增加遊戲道具也可以用這種方法
else
{
if (a == 0)
return 1;elsereturn 0;}}
需要注意的是_life()函數的判斷要在新蛇頭生成前,即map[y][x] = 1;的上一條。4.食物的生成
void _food()
{
int x = rand() % 40 + 1, y = rand() % 20 + 1;
while (food == 0)
{
if (map[y][x] == 0)//找一個空地放食物
{
map[y][x] = 4;
food = 1;//全局變量,監控食物是否存在
}
x = rand() % 40 + 1; y = rand() % 20 + 1;
}
}
在上面的_life()函數裏吃掉食物時會返回爲2,這就便於我們進行食物被吃、和蛇身變長的判定if (life != 2){//life = _life(map[y][x])
map[body[tail][1]][body[tail][0]] = 0;
tail = (tail + 1) % 801;//沒吃到食物,蛇尾消失
}
else food = 0;//吃到食物就跳過尾巴消失,則蛇自然變長,同時食物變量歸零
最後只需要把上面的內容整合下,並做些調整就可以製作出最簡單的貪吃蛇遊戲了,下面是完整的代碼#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include <Windows.h>
#include <conio.h>
int map[22][42] = { 0 }, food = 0;
int _life(int a);
void move();
void _food();
void new_map();
int main()
{
for (int i = 0; i < 22; i++)
{
for (int j = 0; j < 42; j++)
if (i == 0 || i == 21 || j == 0 || j == 41)
map[i][j] = 3;
}
map[1][5] = 1;
map[1][1] = 2; map[1][2] = 2; map[1][3] = 2; map[1][4] = 2;
move();
return 0;
}
int _life(int a)
{
if (a == 4)
return 2;
else
{
if (a == 0)
return 1;
else
return 0;
}
}
void move()
{
int head = 4, tail = 1, x = 5, y = 1, body[801][2] = { 0 }, life = 1;
body[1][0] = 1; body[1][1] = 1; body[2][1] = 1; body[2][0] = 2; body[3][1] = 1; body[3][0] = 3; body[4][1] = 1; body[4][0] = 4;
char m = 'd', m1 = 'd';//設定初始的移動方向
while (life)
{
_food();
map[y][x] = 2;
head = (head + 1) % 801;
body[head][0] = x; body[head][1] = y;
for (; _kbhit();) m = _getch();//用到<conio.h>庫,讓蛇在沒有輸入指令時保持原來的方向移動
switch (m)
{
case 'w':if (m1 == 's'){ m = m1; y += 1; break; }
else{ y -= 1; break; }
case 's':if (m1 == 'w'){ m = m1; y -= 1; break; }
else{ y += 1; break; }
case 'a':if (m1 == 'd'){ m = m1; x += 1; break; }
else{ x -= 1; break; }
case 'd':if (m1 == 's'){ m = m1; x -= 1; break; }
else{ x += 1; break; }
default:m = m1;
break;
}
m1 = m;//m1爲了不讓蛇回頭
life = _life(map[y][x]);
map[y][x] = 1;
if (life != 2){
map[body[tail][1]][body[tail][0]] = 0;
tail = (tail + 1) % 801;
}
else food = 0;
Sleep(300);//用到 <Windows.h>屏幕刷新的間隔,可以控制蛇的速度
system("cls");//刷新屏幕,讓遊戲看起來像在一個地圖裏運行的
new_map();
}
}
void _food()
{
int x = rand() % 40 + 1, y = rand() % 20 + 1;
while (food == 0)
{
if (map[y][x] == 0)
{
map[y][x] = 4;
food = 1;
}
x = rand() % 40 + 1; y = rand() % 20 + 1;
}
}
void new_map()
{
for (int i = 0; i < 22; i++)
{
for (int j = 0; j < 42; j++)
{
if (map[i][j] == 0)
printf(" ");
if (map[i][j] == 1)
printf("H");
if (map[i][j] == 2)
printf("X");
if (map[i][j] == 3)
printf("*");
if (map[i][j] == 4)
printf("$");
}
printf("\n");
}
}