簡單的字符遊戲

製作字符遊戲貪吃蛇的學習記錄

        c語言簡潔強大,我們可以利用它寫出許多程序,甚至製作一些小遊戲,比如貪吃蛇。筆者在第一次用c語言製作貪吃蛇遊戲時,頭都大了,因爲完全不懂該從何處下手開始寫代碼。後來借鑑了前輩的經驗才明白,需要用到自頂向下、逐步求精的方法,將一個貪吃蛇遊戲細分爲幾個小問題:打印地圖、蛇的移動、gameover的判定、食物的產生。然後再慢慢化簡各個小問題中的難題。在這裏給大家記錄下筆者的學習記錄,若能給大家一點幫助則是萬幸。

1.打印地圖

        打印地圖我們首先要構造一個地圖,筆者的想法是構建一個整數型的二維數組用來表示地圖:0表示空地(“ ”),1表示蛇頭(“H”),2表示蛇身(“X”),3表示牆壁(“*
”),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的判定

        這個問題其實很簡單,我們只需要判斷蛇頭的新座標上的數字是不是爲0就好了(如果是食物只需要多加一個分支而已),寫一個函數_life(),給新蛇頭的位置,若該位置爲0就返回1,若爲蛇身或牆壁就返回0,讓循環結束(只需要把返回值的結果給while循環就好了),代碼如下
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");
	}
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章