【贪吃蛇小项目】

文章目录

贪吃蛇

教程来源于C/C++实现贪吃蛇小游戏

一个小项目真的能学到很多新东西!

代码实现

定义结构体—蛇类、食物类、方向

struct Snake
{
	int num;
	int dir;
	int score;
	int size;
	POINT coor[MAX_SNAKE];//包括x,y轴坐标
	/*
	typedef struct tagPOINT
	{
		LONG  x;
		LONG  y;
	} POINT, *PPOINT, NEAR *NPPOINT, FAR *LPPOINT;
	*/
}snake;

POINT本身就是一个结构体,包括x,y坐标

struct Food
{
	POINT fd;
	int flag;//是否有食物的标志 1为有 0为无
	DWORD color;//DWORD相当于unsigned long
}food;
/*
第一个枚举成员的默认值为整型的 0,
后续枚举成员的值在前一个成员上加 1。
UP值为0,DOWN为1,LEFT为2,RIGHT为3
*/
enum DIR
{
	UP,
	DOWN,
	LEFT,
	RIGHT,
};


初始化函数

void GameInit()
{
	//srand((unsigned int)time(nullptr));
	srand(GetTickCount());//以开机时间作为随机数种子
	//初始化蛇
	snake.num = 3;
	snake.dir = RIGHT;//方向
	snake.score = 0;
	snake.size = 10;
	snake.coor[2].x = 0;
	snake.coor[2].y = 0;
	snake.coor[1].x = snake.size;
	snake.coor[1].y = 0;
	snake.coor[0].x = 2 * snake.size;
	snake.coor[0].y = 0;

	//初始化食物
	food.fd.x = rand() % (WIN_WIDTH / 10) * 10;
	food.fd.y = rand() % (WIN_HEIGHT / 10) * 10;
	/*
	因为判断是否迟到食物的标准是 坐标是否相等,而蛇是10倍的变化
	所以食物的坐标随机数也要是10的倍数
	*/
	food.flag = 1;//生成食物
	food.color = RGB(rand() % 256, rand() % 256, rand() % 256);//随机一个颜色
}

注意x,y轴。下和右分别是x,y轴的正方向;上和左分别是x,y轴的逆方向


画图象

void GameDraw()
{
	setbkcolor(RGB(105, 160, 141));//设置窗口颜色 rgb分别为红绿蓝 方法:在画图工具上找到对应的rgb
	cleardevice();//初始化清除设备

	//画蛇
	for (int i = 0; i < snake.num; i++)
	{
		setlinecolor(BLACK);//将蛇的边框设置为黑色
		setfillcolor(YELLOW);
		//左右加x 上下加y 四个参数分别为矩形的四个点 fillrectangle-->有边框 solidrectangle-->无边框
		fillrectangle(snake.coor[i].x, snake.coor[i].y, snake.coor[i].x + snake.size, snake.coor[i].y + snake.size);
		cout << snake.coor[i].x << " " << snake.coor[i].y << endl;
	}
/*
void fillrectangle( int left, int top, int right, int bottom ); 
参数 left 矩形左部 x 坐标。 top 矩形顶部 y 坐标。 right 矩形右部 x 坐标。 bottom 矩形底部 y 坐标。
*/
	//画食物
	if (food.flag == 1)
	{
		setfillcolor(food.color);
		fillellipse(food.fd.x, food.fd.y, food.fd.x + 10, food.fd.y + 10);//椭圆的食物
	}

	//显示分数
	char temp[20] = "";
	sprintf(temp, "分数:%d", snake.score);//打印在游戏窗口上
	setbkmode(TRANSPARENT);//将分数字幕设置为透明
	outtextxy(20, 20, temp);//项目->属性->高级->字符集->改成使用多字节

}

包含头文件#include <graphics.h>//安装easyx才能使用该头文件

画图形的时候几个函数的调用顺序不能改变,我也不知道为什么。。。反正试了不行。。。


移动蛇

void SnakeMove()
{
	for (int i = snake.num - 1; i > 0; i--)
	{
		snake.coor[i] = snake.coor[i - 1];
	}

	switch (snake.dir)
	{
	case UP:
		snake.coor[0].y -= 10;//y轴正方向向下
		if (snake.coor[0].y + 10 <= 0)//撞到边界会从另一头出来
		{
			snake.coor[0].y = WIN_HEIGHT;
		}
		break;
	case DOWN:
		snake.coor[0].y += 10;
		if (snake.coor[0].y - 10 >= WIN_HEIGHT)
		{
			snake.coor[0].y = 0;
		}
		break;
	case LEFT:
		snake.coor[0].x -= 10;
		if (snake.coor[0].x + 10 <= 0)
		{
			snake.coor[0].x = WIN_WIDTH;
		}
		break;
	case RIGHT:
		snake.coor[0].x += 10;
		if (snake.coor[0].x - 10 >= WIN_WIDTH)
		{
			snake.coor[0].x = 0;
		}
		break;
	default:
		break;
	}
}

我认为是项目里面最关键的部分。是由静态的蛇变成动态蛇的关键:从最后节点依次赋值成前节点,而头节点即snake.coor[0]用switch讨论上下左右的情况,在此之下再讨论当走到边界的时候从另一个边界出来。还有:写在while(1)的死循环里面蛇就能动起来啦


读取键盘方向

void KeyControl()
{
	//使用win32API获取键盘消息
	if (GetAsyncKeyState(VK_UP) && snake.dir != DOWN)//不能逆方向走
	{
		snake.dir = UP;
	}
	else if (GetAsyncKeyState(VK_DOWN) && snake.dir != UP)
	{
		snake.dir = DOWN;
	}
	else if (GetAsyncKeyState(VK_LEFT) && snake.dir != RIGHT)
	{
		snake.dir = LEFT;
	}
	else if (GetAsyncKeyState(VK_RIGHT) && snake.dir != LEFT)
	{
		snake.dir = RIGHT;
	}
}

吃食物

void EatFood()
{
	if (snake.coor[0].x == food.fd.x && snake.coor[0].y == food.fd.y 
	&& food.flag == 1)
	{
		snake.num++;
		snake.score += 10;
		food.flag = 0;//标志着食物被吃掉 产生下一个食物
	}

	if (food.flag == 0)
	{
		//让生成的食物的坐标一定是10的倍数
		food.fd.x = rand() % (WIN_WIDTH / 10) * 10;
		food.fd.y = rand() % (WIN_HEIGHT / 10) * 10;
		food.flag = 1;
		food.color = RGB(rand() % 256, rand() % 256, rand() % 256);//随机一个颜色
	}
}

游戏结束标志

void DontEatSelf()
{
	for (int i = 4; i < snake.num; i++)
	{
		//当头撞到身体的时候结束
		if (snake.coor[0].x == snake.coor[i].x && snake.coor[0].y == 
		snake.coor[i].y)
		{
			//outtextxy(200, 200, "Game Over!");
			exit(0);
		}
	}
}

Snake.h

#pragma once
#include <iostream>
#include <graphics.h>//安装easyx才能使用该头文件
#include <windows.h>
#include <conio.h>
#include <ctime>
#define WIN_WIDTH 640
#define WIN_HEIGHT 480
#define MAX_SNAKE 100

using namespace std;

/*
第一个枚举成员的默认值为整型的 0,
后续枚举成员的值在前一个成员上加 1。
UP值为0,DOWN为1,LEFT为2,RIGHT为3
*/
enum DIR
{
	UP,
	DOWN,
	LEFT,
	RIGHT,
};

struct Snake
{
	int num;
	int dir;
	int score;
	int size;
	POINT coor[MAX_SNAKE];//包括x,y轴坐标
	/*
	typedef struct tagPOINT
	{
		LONG  x;
		LONG  y;
	} POINT, *PPOINT, NEAR *NPPOINT, FAR *LPPOINT;
	*/
}snake;

struct Food
{
	POINT fd;
	int flag;
	DWORD color;
}food;

void GameInit()
{
	srand(GetTickCount());//以开机时间作为随机数种子
//	srand((unsigned int)time(nullptr));
	//初始化蛇
	snake.num = 3;
	snake.dir = RIGHT;
	snake.score = 0;
	snake.size = 10;
	snake.coor[2].x = 0;
	snake.coor[2].y = 0;
	snake.coor[1].x = snake.size;
	snake.coor[1].y = 0;
	snake.coor[0].x = 2 * snake.size;
	snake.coor[0].y = 0;


	//初始化食物
	food.fd.x = rand() % (WIN_WIDTH / 10) * 10;
	food.fd.y = rand() % (WIN_HEIGHT / 10) * 10;
	/*
	因为判断是否迟到食物的标准是 坐标是否相等,而蛇是10倍的变化
	所以食物的坐标随机数也要是10的倍数
	*/
	food.flag = 1;
	food.color = RGB(rand() % 256, rand() % 256, rand() % 256);//随机一个颜色
}

void GameDraw()
{
	setbkcolor(RGB(105, 160, 141));//设置窗口颜色 rgb分别为红绿蓝 方法:在画图工具上找到对应的rgb
	cleardevice();//初始化清除设备

	//画蛇
	for (int i = 0; i < snake.num; i++)
	{
		setlinecolor(BLACK);//将蛇的边框设置为黑色
		setfillcolor(YELLOW);
		//左右加x 上下加y 四个参数分别为矩形的四个点 fillrectangle-->有边框 solidrectangle-->无边框
		fillrectangle(snake.coor[i].x, snake.coor[i].y, snake.coor[i].x + snake.size, snake.coor[i].y + snake.size);
		cout << snake.coor[i].x << " " << snake.coor[i].y << endl;
	}

	//画食物
	if (food.flag == 1)
	{
		setfillcolor(food.color);
		fillellipse(food.fd.x, food.fd.y, food.fd.x + 10, food.fd.y + 10);//椭圆的食物
	}

	//显示分数
	char temp[20] = "";
	sprintf(temp, "分数:%d", snake.score);
	setbkmode(TRANSPARENT);//将分数字幕设置为透明
	outtextxy(20, 20, temp);//项目->属性->高级->字符集->改成使用多字节

}

void SnakeMove()
{
	for (int i = snake.num - 1; i > 0; i--)
	{
		snake.coor[i] = snake.coor[i - 1];
	}

	switch (snake.dir)
	{
	case UP:
		snake.coor[0].y -= 10;//y轴正方向向下
		if (snake.coor[0].y + 10 <= 0)//撞到边界会从另一头出来
		{
			snake.coor[0].y = WIN_HEIGHT;
		}
		break;
	case DOWN:
		snake.coor[0].y += 10;
		if (snake.coor[0].y - 10 >= WIN_HEIGHT)
		{
			snake.coor[0].y = 0;
		}
		break;
	case LEFT:
		snake.coor[0].x -= 10;
		if (snake.coor[0].x + 10 <= 0)
		{
			snake.coor[0].x = WIN_WIDTH;
		}
		break;
	case RIGHT:
		snake.coor[0].x += 10;
		if (snake.coor[0].x - 10 >= WIN_WIDTH)
		{
			snake.coor[0].x = 0;
		}
		break;
	default:
		break;
	}
}

void KeyControl()
{
	//使用win32API获取键盘消息
	if (GetAsyncKeyState(VK_UP) && snake.dir != DOWN)//不能逆方向走
	{
		snake.dir = UP;
	}
	else if (GetAsyncKeyState(VK_DOWN) && snake.dir != UP)
	{
		snake.dir = DOWN;
	}
	else if (GetAsyncKeyState(VK_LEFT) && snake.dir != RIGHT)
	{
		snake.dir = LEFT;
	}
	else if (GetAsyncKeyState(VK_RIGHT) && snake.dir != LEFT)
	{
		snake.dir = RIGHT;
	}
}

void EatFood()
{
	if (snake.coor[0].x == food.fd.x && snake.coor[0].y == food.fd.y && food.flag == 1)
	{
		snake.num++;
		snake.score += 10;
		food.flag = 0;//标志着食物被吃掉 产生下一个食物
	}

	if (food.flag == 0)
	{
		food.fd.x = rand() % (WIN_WIDTH / 10) * 10;
		food.fd.y = rand() % (WIN_HEIGHT / 10) * 10;
		food.flag = 1;
		food.color = RGB(rand() % 256, rand() % 256, rand() % 256);//随机一个颜色
	}
}

//游戏结束
void DontEatSelf()
{
	for (int i = 4; i < snake.num; i++)
	{
		if (snake.coor[0].x == snake.coor[i].x && snake.coor[0].y == snake.coor[i].y)
		{
			//outtextxy(200, 200, "Game Over!");
			exit(0);
		}
	}
}

Snake.cpp

#include "Snake.h"

int main()
{
	initgraph(WIN_WIDTH, WIN_HEIGHT, SHOWCONSOLE);//初始化一个图形窗口
	GameInit();
	DWORD t1, t2;
	t1 = t2 = GetTickCount();
	BeginBatchDraw();//解决闪屏问题
	while (1)
	{
		if (t2 - t1 > 100)//数值不同 蛇的速度也不同 即0.1秒移动一次
		{
			//移动一次节点就画一次蛇
			SnakeMove();
			t1 = t2;
		}
		t2 = GetTickCount();//返回已用过的毫秒数
		GameDraw();
		EatFood();
		FlushBatchDraw();//解决闪屏问题
		KeyControl();
		DontEatSelf();
//		Sleep(50);//有点减慢帧数的感觉 让屏幕休眠50毫秒
	}

/*
BeginBatchDraw. 这个函数用于开始批量绘图。执行后,任何绘图操作都将暂时
不输出到绘图窗口上,直到执行 FlushBatchDraw 或 EndBatchDraw 才将之前的绘图输出。
*/


	system("pause");//不加这句不显示游戏窗口
	closegraph();//关闭窗口
	return 0;
}

他们都说一切都是最好的安排,就让爱教我学会忍耐。我愿承受所有绝望与苦难,如果一切是最好的安排。---《最好的安排》 曲婉婷

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值