贪吃蛇(c语言+widows API 小学生低配版

贪吃蛇游戏的设计,需要考虑三个步骤
1.数据设计
2.模块化设计
3.实现过程
其中数据设计和模块化设计是最重要的两个步骤

  1. 数据设计

数据设计要考虑到贪吃蛇的基本元素,包括地图,蛇,食物,以及按键操作。

#include
#include
#include          //光标设置的API
#include            //食物随机
#include           //按键监控

//*********1、数据设计**************//

//辅助宏定义
#define MAPHEIGHT 25   //地图的高
#define MAPLENGHT 60   //地图的宽
#define SNAKESIZE 50   //蛇的最大节数

//食物
struct {
	int x, y;
}food;

//蛇
struct {
	//蛇每一节的位置
	int x[SNAKESIZE], y[SNAKESIZE];
	//蛇的长度
	int len;
	//蛇的速度
	int speed;
}snake;
//DWORD t1, t2;				//控制蛇的速度

//全局变量
char key = 'a';				//初始化蛇的移动方向
int changflag = 0;			//蛇的变化标记
char lastkey = 'A';			//记录上一个按键
  1. 模块化设计
    游戏逻辑如下图所示,可以抽象为五个函数,同时我们需要一个控制光标移动的函数来实现输出。
    贪吃蛇(c语言+widows API 小学生低配版_第1张图片
void drawMap();
void createFood();
void keyDown();
void move(char key);
int snakeStatus();
void gotoxy(int x, int y);

主函数

int main()
{
	drawMap();        //初始化地图
	while (1)
	{
		createFood();//产生食物
		Sleep(snake.speed);			
		keyDown();//按键处理

		if (snakeStatus()== 0)break;//判断蛇的状态
	}

	
	gotoxy(MAPLENGHT / 2, MAPHEIGHT / 2);
	printf("GameOver\t");   
	system("pause");      
	return 0;

}

辅助函数:光标移动
游戏是在控制台进行,所以我们需要一个函数来控制光标的移动,通过移动光标来输出内容

void gotoxy(int x, int y)
{
   //调用win32 API 去设置控制台的光标位置
   //1.找到控制台的这个窗口
   HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
   //2.光标的结构体
   COORD coord;
   //
   //3.设置坐标
   coord.X = x;
   coord.Y = y;
   //4.同步到控制台
   SetConsoleCursorPosition(handle, coord);
}

初始化
初始化包括绘制地图,绘制蛇的初始状态,以及绘制初始状态的食物。
这里需要注意的是,表示蛇头的字符 ■ 占用两个字节,产生的食物坐标必须是偶数。否则会出现蛇经过食物而没有增长的bug。

//初始化
void drawMap(){
	srand((unsigned int)time(NULL));//随机函数种子
	//圈地
	int i;
	for (i = 0; i <= MAPLENGHT; i+=2)//上下边框
	{
		gotoxy(i, 0);
		printf("■");
		gotoxy(i, MAPHEIGHT);
		printf("■");
	}
	for (i = 0; i <= MAPHEIGHT;i++)//左右边框
	{
		gotoxy(0, i);
		printf("■");
		gotoxy(MAPLENGHT, i);
		printf("■");
	}
	//画蛇
		//画蛇头
	snake.len = 3;
	snake.speed = 300;
	snake.x[0] = MAPLENGHT / 2;
	snake.y[0] = MAPHEIGHT / 2;
	gotoxy(snake.x[0], snake.y[0]);
	printf("■");
		//画蛇身
	int k;
	for (k = 1; k < snake.len; k ++)
	{
		snake.x[k] = snake.x[k - 1] + 2;
		snake.y[k] = snake.y[k - 1] ;
		gotoxy(snake.x[k], snake.y[k]);
		printf("■");
	}


	//画食物
	    //确定坐标
	while (1)
	{
		food.x = rand() % (MAPLENGHT - 4) + 2;  
		food.y = rand() % (MAPHEIGHT - 2) + 1;  
		if (food.x % 2 == 0 && food.y % 2 == 0)break;
	}
	    //画
	gotoxy(food.x, food.y);
	printf("○");
}

产生食物

//产生食物
void createFood() {
	
	int flag=0;
	//蛇吃食物后才产生新食物
	if (snake.x[0] == food.x&&snake.y[0] == food.y)
	{
		while (1)
		{
			srand((unsigned int)time(NULL));
			food.x = rand() % (MAPLENGHT - 4) + 2;
			food.y = rand() % (MAPHEIGHT - 2) + 1;
			//判断食物是否产生在蛇的身体上
			for (int i = 0; i < snake.len; i++)
			{
				if (food.x == snake.x[i] && food.y == snake.y[i])
					flag = 1;//食物产生在蛇身上的标记
			}
			if (flag == 0 && (food.x % 2 == 0 && food.y % 2 == 0))//食物必须产生在偶数坐标上
				break;
		}
		gotoxy(food.x, food.y);
		printf("○");
		snake.len++;
		changflag++;//蛇的变化标志
	}

}

按键处理
注意:这里需要屏蔽掉与上一次键入方向相反的方向,以免蛇反向运动时出现自杀的情况。

//按键操作
void keyDown() {
	//无按键的处理
	if (_kbhit())//******************?
	{//有按键接收
		fflush(stdin);//**********?
		key = _getch();//接收按键
	} 
	//判断是否反向
		switch(lastkey)
		{
			case'a':case'A':
				if (key == 'w' || key == 'W' || key == 's' || key == 'S'||key=='a'||key=='A')move(key); break;
			case'd':case'D':
				if (key == 'w' || key == 'W' || key == 's' || key == 'S'||key == 'd' || key == 'D')move(key); break;
			case'w':case'W':
				if (key == 'a' || key == 'A' || key == 'd' || key == 'D'|| key == 'w' || key == 'W')move(key); break;
			case's':case'S':
				if (key == 'a' || key == 'A' || key == 'd' || key == 'D'|| key == 's' || key == 'S')move(key); break;
		}
	}

控制蛇的运动
运动的逻辑是,若蛇身不增长(即蛇没有吃食物),则将最后一节蛇身擦去,否则不擦去;蛇整体向前移动2个坐标,某一节蛇身的位置取代该节的前一节蛇身;最后根据键入的方向判断蛇头的位置。

//控制蛇的运动
void move(char key) {

		//擦除最后位置
		if (!changflag)
		{
			gotoxy(snake.x[snake.len - 1], snake.y[snake.len - 1]);
			printf("  ");
		}

		//蛇身移动
		for (int k = snake.len - 1; k > 0; k--)
		{
			snake.x[k] = snake.x[k - 1];
			snake.y[k] = snake.y[k - 1];
		}

		//按键处理
		switch (key)
		{
		case 'w':case 'W':snake.y[0]--; break;
		case 's':case 'S':snake.y[0]++; break;
		case 'a':case 'A':snake.x[0] -= 2; break;
		case 'd':case 'D':snake.x[0] += 2; break;
		}
		gotoxy(snake.x[0], snake.y[0]);
		printf("■");
		changflag = 0;
		gotoxy(MAPLENGHT + 2, 0);//将光标移至一侧

		lastkey = key;//更新lastkey
	}

蛇的状态函数
蛇撞到墙或者蛇撞到自己时游戏结束

//蛇的状态
int snakeStatus()
{

	//蛇撞到墙
	if ((snake.x[0] == 2 || snake.x[0] == MAPLENGHT-2) || (snake.y[0] == 1 || snake.y[0] == MAPHEIGHT-1))
	{
		return 0;
	}


	//蛇撞到身体
	for (int i = 1; i < snake.len; i++)
	{
		if (snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i])
		{
			return 0;
		}
	}

	return 1;
}

关于贪吃蛇萌新需要了解的关键Windows API以及船新函数

1、船新函数

  • Sleep函数
    功能: 执行挂起一段时间,也就是等待一段时间在继续执行
    用法:Sleep(时间)
    头文件:Windows下为–> windows.h Linux 下为 --> unistd.h
    注意:
    (1)Sleep是区分大小写的,有的编译器是大写,有的是小写。
    (2)Sleep括号里的时间,在windows下是已毫秒为单位,而Linux是以秒为单位
    贪吃蛇中用该函数来控制蛇的运行速度
  • _kbhit()函数
    如果在调用该函数时,有按键被按下,则返回一个非零值,否则该函数的返回值是0。需要注意的是,该函数是一个非阻塞函数,不管有没有按键被按下,该函数都会立即返回。
    c语言不需要添加额外头文件
    用此函数来实现贪吃蛇的自动前进

通过上面两个函数可以使贪吃蛇保持自动前进的状态并且控制速度

  • system(“pause”)
    头文件
    可以实现冻结屏幕,便于观察程序的执行结果;

2.Windows API

  • GetStdHandle
    它用于从一个特定的标准设备(标准输入、标准输出或标准错误)中取得一个句柄(用来标识不同设备的数值)。
    GetStdHandle()返回标准的输入(STD_INPUT_HANDLE)、输出(STD_OUTPUT_HANDLE)或错误(STD_ERROR_HANDLAPIE)的设备的句柄,也就是获得输入、输出/错误的屏幕缓冲区的句柄。
  • SetConsoleCursorPosition是API中定位光标位置的函数。
    获取一个输出的设备的句柄后在定义一个光标变量,就可以通过SetConsoleCursorPosition来控制光标的位置

参考
C编程——实现贪吃蛇大作战

你可能感兴趣的:(贪吃蛇(c语言+widows API 小学生低配版)