c语言-零基础学习收官项目-贪吃蛇

1.需要实现的基本功能:

1.地图绘制;

2.上下左右键控制蛇的动作;

3.蛇撞墙死亡;

4.蛇撞自己死亡;

5.计算得分;

6.蛇加速,减速;

7.暂停游戏。

2.Win32 API:

2.1.介绍:

windows作为开发系统为程序员提供的一些函数接口,用以实现一些功能。

注:只在windows系统中可用。

2.2.控制台程序:

cmd的程序---终端/控制台程序---运行起来的黑框

2.2.1.mode---可控制黑框的大小:
mode con cols=100 lines=30
#include

int main()
{
	system("mode con cols=50 lines=20");//这个函数就能执行系统命令
	printf("hehe\n");
	return 0;
}
2.2.2.title---可修改黑框的名字:
title 贪吃蛇
system("title 贪吃蛇");
2.2.3.pause---可使程序暂停:
system("pause");

注:会自动打印 “ 请按任意键继续. . . ”。

2.2.4.cls---可清理屏幕内容:
system("cls");

2.3.控制台屏幕上的坐标---COORD:

1.需包含

2.COORD为描述坐标所需结构体。

3.横轴向右为x轴正方向,纵轴向下为y轴正方向。

COORD pos = { 3,5 };

2.4.获得句柄:GetStdHandle:

GetStdHandle是一个API函数。它用于从一个特定的标准设备(标准输入,标准输出,标准错误)中取得一个句柄(设备的控制权限),用来操作设备。

HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获得标准输出的句柄

2.5.设置光标大小和可见性:

2.5.1.GetConsoleCursorInfo:

检索有关指定控制台屏幕缓冲区光标大小和可见性信息。

注:结构体:CONSOLE_CURSOR_INFO :

它包含了有关控制台光标的信息。

typedef struct _CONSOLE_CURSOR_INFO {
	DWORD dwSize;
	BOOL bVisible;
}CONSOLE_CURSOR_INFO,*PCONSOLE_CURSOR_INFO;

dwSize:此值介于1到100之间。光标外观发生变化,从完全填充到单元底部的水平线条。
bVisible:光标的可见性。

例:隐藏光标:

cursor_info.bVisible = false;
2.5.2.SetConsoleCursorInfo:

设置有关指定控制台屏幕缓冲区光标大小和可见性信息。

e.g:

int main()
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获得标准输出的句柄
	CONSOLE_CURSOR_INFO cursor_info;
	GetConsoleCursorInfo(handle,&cursor_info);
    cursor_info.dwSize = 1;
	SetConsoleCursorInfo(handle,&cursor_info);	
	return 0;
}

2.6.设置光标位置:SetConsoleCursorPosition:

设置有关指定控制台屏幕缓冲区光标位置。

用法:将坐标信息放在COORD类型的pos中,调用SetConsoleCursorPosition函数将光标位置设置到指定的位置。

例:封装一个SetPos函数,用来设置光标位置:

void SetPos(int x, int y)
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos = { x,y };
	SetConsoleCursorPosition(handle, pos);
}

2.7.获取按键情况:GetAsyncKeyState:

1.功能:获取按键情况。

上下左右键对应的虚拟键代码:

VK_LEFT---0x25 ; VK_UP---0x26 ; VK_RIGHT---0x27 ; VK_DOWN---0x28 ;

2.返回值:

short类型。最高位:1为按下,0为抬起;最低位:1为按下过,0为未按过。

例:封装一个检测vk这个虚拟键值对应的按键是否被按过的宏:

#define PRESS_KET(vk)(GetAsyncKeyState(vk)&0x1?1:0)

3.贪吃蛇游戏设计与分析:

3.1.地图:

由于贪吃蛇墙体,蛇身等字块为宽字符,而c语言默认不包含宽字符,故需要在locale上进行修改。

3.1.1.locale是什么?

locale是c语言针对不同地区字符的区别,所提供的标准和当前地区的语言设置。

如想输出宽字符,需将locale改为Chinese。

3.1.2.类项:

程序员可指定部分设置进行地区修改,各部分称为类项。

c语言-零基础学习收官项目-贪吃蛇_第1张图片

3.1.3.setlocale函数:

char* setlocale ( int category , const chat* locale )

第一个参数为修改类项;

第二个参数只有两个取值:“C”和“ ”(本地模式)

3.1.4.打印宽字符:
#include
int main()
{
	setlocale(LC_ALL, "");
	wchar_t ch1 = L'几';//宽字符的类型
	wchar_t ch2 = L'个';

	printf("ab\n");

	wprintf(L"%lc\n", ch1);//打印宽字符
	wprintf(L"%lc\n", ch2);
	wprintf(L"%lc\n", ch1);
	wprintf(L"%lc\n", ch2);
	return 0;
}

c语言-零基础学习收官项目-贪吃蛇_第2张图片

3.1.5.地图坐标:

x轴坐标需为偶数,其他详见2.3 。

3.2.数据结构的设计:

enum DIRECTION
{
	UP = 1,
	DOWN,
	LEFT,
	RIGHT
};

enum GAME_STATUS
{
	OK,
	END_NORMAL,
	KILL_BY_WALL,
	KILL_BY_SELF
};

//贪吃蛇的节点
typedef struct SnakeNode
{
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode,*pSnakeNode;

//贪吃蛇的结构
typedef struct Snake
{
	pSnakeNode _psnake;//指向贪吃蛇头节点的指针
	pSnakeNode _pFood;//指向食物节点的指针
	int _Score;//贪吃蛇积累的总分
	int _FoodWeight;//一个食物的分数
	int _SleepTime;//每走一步休息的时间,时间越短速度越快
	enum DIRECTION _Dir;//描述蛇的方向
	enum GAME_STATUS _Status;//游戏的状态:正常,退出,撞墙,撞到自己
}Snake,*pSnake;

4.具体实现:

头文件:snake.h

#pragma once
#include
#include
#include
#include
#include
#include

#define WALL L'□'
#define BODY L'●'
#define FOOD L'★'

#define POS_X 24
#define POS_Y 5

#define PRESS_KET(vk)(GetAsyncKeyState(vk)&0x1?1:0)

enum DIRECTION
{
	UP = 1,
	DOWN,
	LEFT,
	RIGHT
};

enum GAME_STATUS
{
	OK,
	END_NORMAL,
	KILL_BY_WALL,
	KILL_BY_SELF
};

//贪吃蛇的节点
typedef struct SnakeNode
{
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode,*pSnakeNode;

//贪吃蛇的结构
typedef struct Snake
{
	pSnakeNode _psnake;//指向贪吃蛇头节点的指针
	pSnakeNode _pFood;//指向食物节点的指针
	int _Score;//贪吃蛇积累的总分
	int _FoodWeight;//一个食物的分数
	int _SleepTime;//每走一步休息的时间,时间越短速度越快
	enum DIRECTION _Dir;//描述蛇的方向
	enum GAME_STATUS _Status;//游戏的状态:正常,退出,撞墙,撞到自己
}Snake,*pSnake;

//1.游戏开始 - 初始化游戏
void GameStart(pSnake ps);
void WelcomeToGame();//游戏开始的欢迎界面
void CreateMap();//打印地图
void InitSnake(pSnake ps);//初始化蛇
void CreateFood(pSnake ps);//创建食物
//2.游戏运行 
void GameRun(pSnake ps);
void PrintHelpInfo();//打印辅助信息
void SnakeMove(pSnake ps);//蛇的移动
int NextIsFood(pSnake ps, pSnakeNode pNext);//判断下一个位置是否是食物
void KillByWall(pSnake ps);//蛇是否撞墙
void  KillBySelf(pSnake ps);//蛇是否自杀
//3.游戏结束 
void GameEnd(pSnake ps);

源文件:snake.c

#include"snake.h"

//1.游戏开始 - 初始化游戏
void SetPos(int x, int y)
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos = { x,y };
	SetConsoleCursorPosition(handle, pos);
}

void WelcomeToGame()
{
	//定位光标
	SetPos(40, 15);
	printf("欢迎来到贪吃蛇小游戏!");
	SetPos(40, 25);
	system("pause");
	system("cls");
	SetPos(25, 15);
	printf("使用↑.↓.←.→控制蛇的移动,F3是加速,F4是减速");
	SetPos(40, 25);
	system("pause");
	system("cls");
}

void CreateMap()
{
	//上
	SetPos(0, 0);
	int i = 0;
	for (i = 0; i <= 56; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	//下
	SetPos(0, 26);
	for (i = 0; i <= 56; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	//左
	for (i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}
	//右
	for (i = 1; i <= 25; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc", WALL);
	}
}

void InitSnake(pSnake ps)
{
	pSnakeNode cur = NULL;
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		cur = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (cur == NULL)
		{
			perror("InitSnake()::malloc()");
			return;
		}
		cur->x = POS_X + 2 * i;
		cur->y = POS_Y;
		cur->next = NULL;

		//头插法
		if (ps->_psnake == NULL)
		{
			ps->_psnake = cur;
		}
		else
		{
			cur->next = ps->_psnake;
			ps->_psnake = cur;
		}
	}
	//打印蛇身
	cur = ps->_psnake;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	ps->_Status = OK;
	ps->_Score = 0;
	ps->_pFood = NULL;
	ps->_SleepTime = 200;
	ps->_FoodWeight = 10;
	ps->_Dir = RIGHT;
}

void CreateFood(pSnake ps)
{
	int x;
	int y;
	again:
	do
	{
		x = rand() % 53 + 2;
		y = rand() % 25 + 1;
	} while (x % 2 != 0);

	//坐标不能和蛇的身体冲突
	pSnakeNode cur = ps->_psnake;
	if (cur->x == x && cur->y == y)
	{
		goto again;
	}
	pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pFood == NULL)
	{
		perror("CreateFood()::malloc()");
		return;
	}
	pFood->x = x;
	pFood->y = y;
	ps->_pFood = pFood;

	//打印食物
	SetPos(x, y);
	wprintf(L"%lc", FOOD);
}

void GameStart(pSnake ps)
{
	//控制台窗口的设置
	system("mode con cols=100 lines=30");
	system("title 贪吃蛇");
	
	//光标隐藏
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获得标准输出的句柄
	CONSOLE_CURSOR_INFO cursor_info;
	GetConsoleCursorInfo(handle, &cursor_info);
	cursor_info.bVisible = false;
	SetConsoleCursorInfo(handle, &cursor_info);
	
	//打印欢迎界面
	WelcomeToGame();
	//创建地图
	CreateMap();	
	//初始化贪吃蛇
	InitSnake(ps);
	//创建食物
	CreateFood(ps);
}

//2.游戏运行 

void PrintHelpInfo()//打印辅助信息
{
	SetPos(64, 15);
	printf("1.不能撞墙,不能咬自己。");
	SetPos(64, 16);
	printf("2.使用↑、↓、←、→控制蛇的移动。");
	SetPos(64, 17);
	printf("3.F3加速,F4减速。");
	SetPos(64, 18);
	printf("4.ESC退出,空格暂停。");
	SetPos(64, 20);
	printf("几个几个@版权");
}

int NextIsFood(pSnake ps, pSnakeNode pNext)//判断下一个位置是否是食物
{
	if (ps->_pFood->x == pNext->x && ps->_pFood->y == pNext->y)
		return 1;
	else return 0;
}

void KillByWall(pSnake ps)//蛇是否撞墙
{
	if (ps->_psnake->x == 0 || ps->_psnake->x == 56 || ps->_psnake->y == 0 || ps->_psnake->y == 26)
	{
		ps->_Status = KILL_BY_WALL;
	}
}
void  KillBySelf(pSnake ps)//蛇是否自杀
{
	pSnakeNode cur = ps->_psnake->next;
	while (cur)
	{
		if (ps->_psnake->x == cur->x && ps->_psnake->y == cur->y)
		{
			ps->_Status = KILL_BY_SELF;
		}
		cur = cur->next;
	}
}

void SnakeMove(pSnake ps)//蛇的移动
{
	pSnakeNode pNext = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pNext == NULL)
	{
		perror("SnakeMove()::malloc()");
		return;
	}
	pNext->next = NULL;
	switch (ps->_Dir)
	{
	case UP:
		pNext->x = ps->_psnake->x;
		pNext->y = ps->_psnake->y - 1;
		break;
	case DOWN:
		pNext->x = ps->_psnake->x;
		pNext->y = ps->_psnake->y + 1;
		break;	
	case LEFT:	
		pNext->x = ps->_psnake->x - 2;
		pNext->y = ps->_psnake->y;
		break;
	case RIGHT:
		pNext->x = ps->_psnake->x + 2;
		pNext->y = ps->_psnake->y;
		break;
	}

	//判断下一个位置是否是食物
	if (NextIsFood(ps, pNext))//是
	{
		//头插
		pNext->next = ps->_psnake;
		ps->_psnake = pNext;
		//打印蛇身
		SetPos(ps->_psnake->x, ps->_psnake->y);
		wprintf(L"%lc", BODY);
		//处理食物
		free(ps->_pFood);
		ps->_Score += ps->_FoodWeight;
		CreateFood(ps);
	}
	else//不是
	{
		//头插
		pNext->next = ps->_psnake;
		ps->_psnake = pNext;
		//删除最后节点并打印蛇身
		SetPos(ps->_psnake->x, ps->_psnake->y);
		wprintf(L"%lc", BODY);
		pSnakeNode cur = ps->_psnake;
		while (cur->next->next)
		{
			cur = cur->next;
		}
		SetPos(cur->next->x, cur->next->y);
		printf("  ");
		free(cur->next);
		cur->next = NULL;
	}

	//蛇是否撞墙
	KillByWall(ps);
	//蛇是否自杀
	KillBySelf(ps);
}

void GameRun(pSnake ps)
{
	PrintHelpInfo();
	do
	{
		//打印按键得分
		SetPos(64, 10);
		printf("得分:%05d", ps->_Score);
		SetPos(64, 11);
		printf("每个食物的分数:%2d", ps->_FoodWeight);

		//判断按键情况
		if (PRESS_KET(VK_UP) && ps->_Dir != DOWN)
		{
			ps->_Dir = UP;
		}		
		else if (PRESS_KET(VK_DOWN) && ps->_Dir != UP)
		{
			ps->_Dir = DOWN;
		}		
		else if (PRESS_KET(VK_LEFT) && ps->_Dir != RIGHT)
		{
			ps->_Dir = LEFT;
		}		
		else if (PRESS_KET(VK_RIGHT) && ps->_Dir != LEFT)
		{
			ps->_Dir = RIGHT;
		}		
		else if (PRESS_KET(VK_ESCAPE))
		{
			ps->_Status = END_NORMAL;
		}		
		else if (PRESS_KET(VK_SPACE))
		{
			while (1)
			{
				Sleep(100);
				if (PRESS_KET(VK_SPACE))
				{
					break;
				}
			}
		}		
		else if (PRESS_KET(VK_F3))//加速
		{
			if (ps->_SleepTime >= 80)
			{
				ps->_SleepTime -= 30;
				ps->_FoodWeight += 2;
			}
		}		
		else if (PRESS_KET(VK_F4))//减速
		{
			if (ps->_SleepTime <= 320)
			{
				ps->_SleepTime += 30;
				ps->_FoodWeight -= 2;
			}
		}

		//蛇的移动
		Sleep(ps->_SleepTime);
		SnakeMove(ps);

	} while (ps->_Status == OK);
}

//3.游戏结束 
void GameEnd(pSnake ps)
{
	SetPos(20, 12);
	switch (ps->_Status)
	{
	case END_NORMAL:
		printf("退出游戏\n");
		break;	
	case KILL_BY_SELF:
		printf("撞自己了,游戏结束。\n");
		break;	
	case KILL_BY_WALL:
		printf("撞墙了,游戏结束。\n");
		break;
	}
	SetPos(0, 27);
	//释放蛇身节点
	pSnakeNode cur = ps->_psnake;
	while (cur)
	{
		pSnakeNode del = cur;
		cur = cur->next;
		free(del);
	}
	ps->_psnake = NULL;
}

main函数:main.c

#include"snake.h"
#include

#include

int main()
{
	//设置程序适应本地环境
	setlocale(LC_ALL, "");
	srand(time(NULL));
	Snake snake = { 0 };
	//1.游戏开始 - 初始化游戏
	GameStart(&snake);
	//2.游戏运行 
	GameRun(&snake);
	//3.游戏结束 
	GameEnd(&snake);
	return 0;
}

c语言学习收关,撒花

你可能感兴趣的:(学习,c语言)