1. 程式人生 > >C語言實現經典小遊戲貪吃蛇

C語言實現經典小遊戲貪吃蛇

純c語言寫的小遊戲,本人才疏學淺,程式碼有很多不足,僅供給初學者參考。

實現功能:

  • ↑ ↓ ← →分別用來控制上下左右四個方向
  • 空格暫停
  • esc退出遊戲
  • f1加速, f2 減速
  • 蛇的長度越長,速度越快(速度有上限)
  • 食物顏色隨機
  • 撞牆死/咬到自己死

不足之處:

  • 實現了蛇撞牆和撞到自己兩種死亡判斷,但是沒有實現遊戲勝利。
  • 蛇移動的時間和食物出現的時間固定,沒有實現遊戲難度的選擇。

執行結果展示:
在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

程式碼如下:

標頭檔案(snack.h)

#ifndef __SNACK_H__
#define __SNACK_H__

#define _CRT_SECURE_NO_WARNINGS 1

#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define  WALL "▓"
#define SNACK "●"
#define FOOD "★"

#define INIT_X 10
#define INIT_Y 20
#define INIT_LONG 4

enum Direction 
{
	UP = 1,	//上
	DOWN,	//下
	LEFT,	//左
	RIGHT	//右
};

typedef enum GameStatus	//遊戲狀態
{
	OK,				//正常
	NORMAL_END,		//正常結束
	KILL_BY_WALL,	//撞牆
	KILL_BY_SELF	//咬到自己
};

typedef struct SnackNode	//表示蛇身節點
{
	int x;
	int y;
	struct SnackNode* _pNext;
}SnackNode, *pSnackNode;

typedef struct Snack
{
	pSnackNode _psnack;//位置
	pSnackNode _pfood;//食物位置
	int _TotalScore; //得分
	int _AddScore;	//增加分數
	int _SleepTime;
	enum Direction _Dir;
	enum GameStatus _Status;
}Snack, *pSnack;


void setpos (int x, int y);//控制游標位置
int WelcomeToGame_1 ();//歡迎介面1
void WelcomeToGame_2 ();//歡迎介面2
void CreateMap ();//初始化地圖
void InitSnack (pSnack ps);//初始化蛇
void CreateFood (pSnack ps);//列印食物
void GameStart (pSnack ps);//遊戲開始
void SnackMove(pSnack ps);//蛇移動
void Killed_By_Wall(pSnack ps);//撞牆死
void Collide_Self(pSnack ps);咬到自己死
void AboutGame (pSnack ps);//遊戲說明
int color(int x);	//改變字型顏色
int GetLength (pSnack ps);//獲取蛇的長度
void Exit_Game(pSnack snack);//退出遊戲

#endif

主要函式(main.c)

#include "snack.h"

void setpos (int x, int y)
{
	COORD pos = {0};
	pos.X = x;
	pos.Y = y;
	SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), pos);
}

int WelcomeToGame_1 ()
{
	int i = 0;
	system ("mode con cols=100 lines=30");	//設定介面大小
	system ("title 貪吃蛇");
	setpos (38, 8);
	color (11);
	printf ("貪吃蛇xiao遊戲\n");
	setpos (38, 13);
	printf ("1、開始遊戲");
	setpos (38, 15);
	printf ("2、退出");
	setpos (88, 28);
	printf ("2018/08/26");
	
	setpos (38, 17);
	printf ("請選擇:");
	scanf ("%d", &i);
	system ("cls");
	
	return i;
}

void WelcomeToGame_2 ()
{
	color (11);
	setpos (32, 11);
	printf ("提示:");
	setpos (35, 13);
	printf ("用↑↓←→控制蛇的移動方向,f1加速,f2減速");
	setpos (35, 15);
	printf ("速度越快,得分越多");
	setpos (35, 28);
	printf ("LOADING...");
	Sleep (2500);
	system ("cls");
}

void CreateMap ()
{
	
	int i = 0;
	color (15);
	//上邊界
	for (i=0; i<=58; i+=2)
	{
		setpos (i, 0);
		printf (WALL);
	}
	//左邊界
	for (i=0; i<30; i++)
	{
		setpos (0, i);
		printf (WALL);
	}
	//下邊界
	for (i=2; i<=58; i+=2)
	{
		setpos (i, 29);
		printf (WALL);
	}
	//右邊界
	for (i=1; i<29; i++)
	{
		setpos (58, i);
		printf (WALL);
	}
}

pSnackNode BuySnackNode ()
{
	pSnackNode cur = (pSnackNode)malloc (sizeof (SnackNode));
	if (cur == NULL)
	{
		printf ("建立蛇身節點出錯,遊戲結束");
		exit (1);
	}
	cur ->x = 0;
	cur ->y = 0;
	cur ->_pNext = NULL;
	return cur;
}

void print_snack(pSnack ps)
{
	pSnackNode cur = ps->_psnack;
	while (cur)
	{
		setpos (cur->x, cur->y);
		color (14);
		printf (SNACK);
		cur = cur->_pNext;
	}
	color (12);
}
void InitSnack (pSnack ps)
{
	int i = INIT_LONG;
	pSnackNode cur = NULL;
	pSnackNode first = (pSnackNode)malloc (sizeof (SnackNode));
	if (first == NULL)
	{
		printf ("建立蛇頭節點出錯,遊戲結束");
		exit (1);
	}
	first->x = INIT_X;
	first->y = INIT_Y;
	first->_pNext = NULL;
	while (--i)
	{
		cur = BuySnackNode ();
		cur ->x = first->x + 2;
		cur->y = first->y ;
		cur->_pNext = first;
		first = cur;
	}
	ps->_Dir = RIGHT;
	ps->_psnack = first;
	ps->_AddScore = 10;
	ps->_SleepTime = 200;
	ps->_TotalScore = 0;
	ps->_Status = OK;
	print_snack(ps);
}


void CreateFood (pSnack ps)
{
	int x = 0; 
	int y = 0;
	int col = 0;
	pSnackNode snack = ps ->_psnack ;
	pSnackNode food = BuySnackNode();
	y = rand()% 28 + 1;
	do
	{
		x = rand ()% 55 + 2;
	}while ((x%2) != 0);

	food ->x = x;
	food ->y = y;

	while (snack)
	{
		if (food->x == snack->x && food->y == snack->y)
		{
			break;
		}
		else
		{
			snack = snack->_pNext;
		}
	}
	if (snack == NULL)
	{
		ps->_pfood = food;
		setpos (food->x, food->y);
		col = rand() % 15+1;
		color (col);
		printf (FOOD);
	}
	else
	{
		CreateFood (ps);
	}
	
}

void Killed_by_Self(pSnack ps)
{
	pSnackNode cur = ps->_psnack->_pNext;
	while (cur != NULL)
	{
		if (cur->x == ps->_psnack ->x  &&  cur->y == ps->_psnack ->y)
		{
			setpos (18, 21);
			printf ("很遺憾, 你把自己給咬死了!!!\n");
			ps->_Status = KILL_BY_SELF;
			break;
		}
		cur = cur->_pNext;
	}
	
}

void Killed_By_Wall(pSnack ps)
{
	if (ps->_psnack ->x == 0 || ps->_psnack ->x == 58 || ps->_psnack->y == 29 || ps->_psnack ->y == 0)
	{
		setpos (18, 21);
		printf ("很遺憾,你撞牆了!!!\n");
		ps->_Status = KILL_BY_WALL;
	}
	
}

void GameStart (pSnack ps)
 {
	int i = 0;
	do 
	{
		AboutGame (ps);
		//檢測是否從鍵盤上按下一個值

		if(GetAsyncKeyState(VK_UP) && ps->_Dir != DOWN) //上
		{
			ps->_Dir = UP;
		}

		if(GetAsyncKeyState(VK_DOWN) && ps->_Dir != UP)	//下
		{
			ps->_Dir = DOWN;
		}


		if(GetAsyncKeyState(VK_LEFT) && ps->_Dir != RIGHT)	//左
		{
			ps->_Dir = LEFT;
		}


		if(GetAsyncKeyState(VK_RIGHT) && ps->_Dir != LEFT)	//右
		{
			ps->_Dir = RIGHT;
		}

		if(GetAsyncKeyState(VK_ESCAPE))//退出
		{
			ps->_Status = NORMAL_END;
		}

		if(GetAsyncKeyState(VK_F1))//加速
		{
			if(ps->_SleepTime > 50)
			{
				ps->_SleepTime -= 30;
			}
			ps->_AddScore += 5;
		}

		if(GetAsyncKeyState(VK_F2))//減速
		{
			if(ps->_SleepTime <= 500)
			{
				ps->_SleepTime += 50;
				if(ps->_AddScore != 6)
					ps->_AddScore -= 6;
				else
					ps->_AddScore = 6;
			}
		}
		if(GetAsyncKeyState(VK_SPACE))//暫停
		{
			while(1)
			{
				Sleep(100);
 				if(GetAsyncKeyState(VK_SPACE))
				{
					break;
				}
			}
		}
		Sleep(ps->_SleepTime);
  		SnackMove(ps);
		Killed_By_Wall(ps);
		Killed_by_Self(ps);

	}while (ps->_Status == OK);
	setpos (18, 23);
	printf ("請稍後...");
	Sleep (2500);
	system ("cls");
	setpos (45, 10);
	printf ("GAME OVER");
	setpos (45, 15);
	printf ("總分:%d", ps->_TotalScore);
	setpos (80, 28);
	system ("pause");

}

void Eat_Food(pSnack ps)
{
	//分數增加
	ps->_TotalScore += ps->_AddScore;
	
	//吃掉食物後吧食物銷燬
	free (ps->_pfood );
	ps->_pfood = NULL;

	//列印蛇
	setpos (ps->_psnack ->x, ps->_psnack ->y);
	print_snack (ps);

	//在生成新的食物
	CreateFood (ps);
	//每吃一個食物,速度增加
	if (ps->_SleepTime >= 100)
	{
		ps->_SleepTime -= 10;
		ps->_AddScore += 5;
	}

}

void Not_Eat_food(pSnack ps)	// 沒有吃東西,長度不變,只需要把最後一個節點處列印成空格,在銷燬掉最後一個節點
{
	pSnackNode cur = ps->_psnack;
	while (cur->_pNext ->_pNext != NULL)
	{
		cur = cur->_pNext;
	}
	
	setpos (cur->_pNext ->x, cur->_pNext->y);	
	printf (" ");

	free (cur->_pNext);
	cur->_pNext = NULL;
	//列印蛇
	setpos (ps->_psnack->x, ps->_psnack->y);
	print_snack (ps);

}
void SnackMove (pSnack ps)
{
	pSnackNode cur = BuySnackNode ();
	switch (ps->_Dir)
	{
	case UP:
		{
			cur->x = ps->_psnack ->x;
			cur->y = ps->_psnack ->y-1;
			cur->_pNext = ps->_psnack ;
			ps->_psnack = cur;
			if (cur->x == ps ->_pfood ->x && cur->y == ps->_pfood->y)
			{
				Eat_Food(ps);
			}
			else
			{
				Not_Eat_food(ps);
			}
		}
		break;
	case DOWN:
		{
			cur->x = ps->_psnack ->x;
			cur->y = ps->_psnack ->y+1;
			cur->_pNext = ps->_psnack ;
			ps->_psnack = cur;
			if (cur->x == ps ->_pfood ->x && cur->y == ps->_pfood->y)
			{
				Eat_Food(ps);
			}
			else
			{
				Not_Eat_food(ps);
			}
		}
		break;
	case LEFT:
		{
			cur->x = ps->_psnack ->x-2;
			cur->y = ps->_psnack ->y;
			cur->_pNext = ps->_psnack ;
			ps->_psnack = cur;
			if (cur->x == ps ->_pfood ->x && cur->y == ps->_pfood->y)
			{
				Eat_Food(ps);
			}
			else
			{
				Not_Eat_food(ps);
			}
		}
		break;
	case RIGHT:
		{
			cur->x = ps->_psnack ->x+2;
			cur->y = ps->_psnack ->y;
			cur->_pNext = ps->_psnack ;
			ps->_psnack = cur;
			if (cur->x == ps ->_pfood ->x && cur->y == ps->_pfood->y)
			{
				Eat_Food(ps);
			}
			else
			{
				Not_Eat_food(ps);
			}
		}
		break;
	}
}

void AboutGame (pSnack ps)
{
	color (2);
	setpos (65, 9);
	printf ("遊戲說明:");
	setpos (68, 11);
	printf ("控制方向:↑↓←→");
	setpos (68, 13);
	printf ("暫停:空格");
	setpos (68, 15);
	printf ("退出遊戲:esc");
	setpos (68, 17);
	printf ("f1加速,f2減速");
	setpos (68, 19);
	printf ("分數:+%d", ps->_AddScore);
	color (11);
}

int color(int c)	//改變字型顏色
{
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c);        //更改文字顏色
	return 0;
}

void Exit_Game(pSnack ps)
{
	pSnackNode cur = ps->_psnack;
	pSnackNode pre = NULL;
	while (cur != NULL)
	{
		pre = cur;
		cur = cur->_pNext;
		free (pre);
		pre = NULL;
	}
}

功能測試test.c

#include "snack.h"

void test ()
{
	int i = 0;
	int j = 0;
	Snack snack;
	srand ((unsigned)time(NULL));

	do
	{

		//開始介面
		i = WelcomeToGame_1 ()-2;
			switch (i)
			{
			case -1:
				{
					 WelcomeToGame_2 ();
					//構造地圖(邊界)
					CreateMap ();
					//初始化蛇
					InitSnack (&snack);
					//構造食物	
					CreateFood (&snack);
					//開始遊戲
					GameStart (&snack);
				}
				break;
			case 0:
				setpos (38, 15);
				printf ("退出遊戲!!\n");
				Exit_Game(&snack);
				break;
			default :
				setpos (38, 15);
				printf ("你的選擇有誤,2s後請重新選擇...");
				Sleep (2000);
				break;
			}
	}while (i);
	setpos (66, 28);
	system ("pause");
}

void test_food ()
{
	int i = 0;
	Snack snack;
	WelcomeToGame_2 ();
	//構造地圖(邊界)
	CreateMap ();
	//初始化蛇
	InitSnack (&snack);
	//構造食物	
	for (; i<100; ++i)
	{
		CreateFood (&snack);
		Sleep (500);
	}
}

int main()
{
	test ();
	return 0;
}