c语言贪吃蛇设计

此游戏需要Easyx图形库,没有图形库去百度上搜索 目前还没有遇到Bug,此游戏还差一功能从文件里面读取最高分数。目前不想做。 游戏没有加入重新开始,一站到底。 游戏截图: 游戏思路: 这里利用头插法让蛇移动起来: 蛇头坐标等于食物那就继续移动,反之释放掉尾巴 一开始选择速度等级,然后在改变main

此游戏需要Easyx图形库,没有图形库去百度上搜索

目前还没有遇到Bug,此游戏还差一功能从文件里面读取最高分数。目前不想做。

游戏没有加入重新开始,一站到底。

游戏截图:

游戏思路:

这里利用头插法让蛇移动起来:

蛇头坐标等于食物那就继续移动,反之释放掉尾巴

一开始选择速度等级,然后在改变main.ccp里面Sleep(所对应的速度等级的值);来达到蛇移动速度的快慢

让蛇移动起来坐标不断的++/--达到蛇的移动,检测蛇头的坐标和食物坐标是不是相同

如果相同那就分数++,然后调用食物生成函数,重新生成食物位置而且食物位置与蛇坐标不相同。

判断蛇头的坐标和蛇坐标的身体坐标是不是相等,如果相等那就证明蛇咬到自己了,结束游戏!。

 

代码:多文件编写

Sanke.h:

#pragma once
#define Widht 900			//界面的宽,高
#define Hight  500 
#define sankeRadius 5			//蛇的半径
#define Initlength	 3				//蛇的初始长度
/************************************
	项目:贪吃蛇;
	图形工具:Easxy
************************************/
enum dir			//用于设置蛇的方向
{
	UP = 1, Down = 2, Right = 3, Left = 4
};
//设计蛇结构
typedef struct Sanke
{
	int x;				//蛇的坐标
	int y;
	bool sankeHead;		//用于判断是不是蛇头,是蛇头那蛇头变成红色,不是那就黄色
	dir move;
	struct Sanke* next;		//下一节身体
}Sanke,*pSanke;

//设计食物结构
typedef struct food
{
	int x;			//食物坐标
	int y;
}Food,*pFood;

//初始化
 void initSanke();

//画出界面
void showsanke();

//游戏结束
void end();

//按键检测
void key_input();

//蛇移动
void movesanke();

//判断食物情况
void food();

实现.h文件

sanke.cpp:

#pragma once
#include "Sanke.h"
#include <stdio.h>
#include <stdlib.h>
#include <graphics.h>
#include <time.h>
#include <conio.h>
int speed;
			//用于连接头->next = 蛇头
pSanke Headsanke, sanke;		//sanke用于临时用的。可以不用到时候创建一个就可以。
pFood  sankefood;					//创建食物变量
int score;									//记录分数
bool ply;									//用于判断游戏是否结束
char Text_Game[200];			//这个变量用于存字符串用于打印到屏幕上

//打印信息		
//这函数好好看参数,一句话就是给定坐标打印传入的字符串,按照传入的颜色打印
void showPrint(int left, int top, int right, int bottom, char Text[], COLORREF color)
{
	settextcolor(color);
	// 在屏幕中央输出字符串				//以下函数去Easxy帮助文档进行了解
	RECT r = { left, top, right , bottom };
	drawtext(_T(Text), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

}

//加速等级  这很好理解就不过多解释
void initSpeed()
{
	char te[] = "	请选择游戏速度: 慢 A  中 S  快 D !\n";
	showPrint(0, 0, 639, 479, te, RED);
	//选择速度 慢,中,快
	char sp;
	sp = _getch();
	switch (sp)
	{
	case 'A':
	case 'a':
		speed = 260;				//这里值的改变会影响主函数里的Sleep();速度
		break;
	case 'S':
	case 's':
		speed = 150;
		break;
	case 'D':
	case 'd':
		speed = 55;
		break;
	}

}

//初始化
void initSanke()
{
	srand((unsigned int)time(NULL));				//种子
	//对变量初始化
	score = 0;
	speed = 0;
	ply = true;
	Headsanke = NULL;								
	sanke = NULL;
	Headsanke = (pSanke)malloc(sizeof(Sanke));		//给连接头分配空间
	if (Headsanke == NULL)			//判断内存分配是否成功
	{
		return;
	}
	//默认给定蛇出生的坐标
	int sanke_x, sanke_y;
	sanke_x = 200;
	sanke_y = 200;
	for (int i = 0; i < Initlength; i++)		//Initlength(宏定义) 蛇的初始长度 
	{
		pSanke Newsanke = (pSanke)malloc(sizeof(Sanke));
		if (Newsanke == NULL)				//判断内存分配是否成功
			{
				return;
			}
		if (i == 0)			//第一个节点到最后会变成蛇的尾巴,因为是头插法,
		{							//所以用sanke指向他,然后最后让尾巴->next = NULL
			sanke = Newsanke;
		}
		Newsanke->x = sanke_x;
		Newsanke->y = sanke_y;
		//因为蛇的默认方向是---->
		//所以只加x的值y值不要动,因为蛇的半径是5所以直径是10
		sanke_x = sankeRadius*2 + sanke_x;
		//因为目前不知道哪个是蛇头所以设置成false , sankeHead = false;
		Newsanke->sankeHead = false;
		//下面两步看不懂的,建议画图,
		Newsanke->next = Headsanke->next;		
		Headsanke->next = Newsanke;					

		}
	//连接头->蛇头 , 所以把sankeHead = true, 因为要蛇头为红色,蛇身为黄色
	Headsanke->next->sankeHead = true;
	//蛇的默认方向--->
	Headsanke->next->move = Right;
	//这里在上面保留了蛇尾巴, 让尾巴->next = MULL;
	sanke->next = NULL;
	//调用初始化食物
	food();
	//调用初始化速度
	initSpeed();
}

//食物生成
void food()
{
		srand((unsigned int)time(NULL));			//种子
		sankefood = (pFood)malloc(sizeof(Food));		//给食物分配空间
		bool temp = true;			//循环条件
		int sankefood_x, sankefood_y;			//食物坐标
		while (temp)
		{
			while (1)
			{
				//因为有围墙食物的坐标不能和围墙相等,所以要加上围墙的宽度,
				//然后生成范围(15~(Widht-315)),总之要在围墙之内
				sankefood_x = rand() % (Widht-315) + 15;		
				sankefood_y = rand() % (Hight - 25) + 15;
				//这里因为蛇移动是要+10(直径)这样每次移动都是加10
				//坐标不会等于 如:123,343 这些坐标,如果是这些坐标,那蛇的坐标永远不会等于
				//这些坐标,也就意味着吃不了食物
				//所以食物坐标为10,20,30......,判断方法就是取出坐标的个位数判断是等于0
				//因为个位等于0,就证明这个是(10,110,20,440,30...)等
				if ((sankefood_x % 10) == 0 && (sankefood_y % 10) == 0)
				{
					break;
				}
			}

			sanke = Headsanke->next;
			while (sanke != NULL)
			{
				//判断食物坐标是否和蛇的坐标相同
				if (sanke->x == sankefood_x && sanke->y == sankefood_y)
				{
					break;
				}
				else
				{
					temp = false;
				}
				sanke = sanke->next;
			}
		}
		//赋值给食物坐标
		sankefood->x = sankefood_x;
		sankefood->y = sankefood_y;
}

//画蛇/食物
void showsanke()
{
	pSanke p = Headsanke->next;
	//判断食物
	if (sankefood != NULL)
	{
		setfillcolor(LIGHTGREEN);
		setlinecolor(LIGHTBLUE);
		fillcircle(sankefood->x, sankefood->y, sankeRadius);
	}

	if (p == NULL)
	{
		return;
	}
	//这一步虽然有多此一举,但最好还是判断一下
	//判断是不是蛇头如果是那就蛇头为红色
	if (p->sankeHead == true)
	{
		setfillcolor(RED);
		setlinecolor(LIGHTBLUE);
		fillcircle(p->x, p->y, sankeRadius);
		p = p->next;
	}
	//画蛇身
	while (p != NULL )
	{
		setfillcolor(YELLOW);
		setlinecolor(LIGHTBLUE);
		fillcircle(p->x, p->y, sankeRadius);
		p = p->next;
	}
	//打印围墙
	settextcolor(CYAN);
	for (int i = 0; i <= Widht-300; i += 12)		//打印上下
	{
		outtextxy(i, 0, "■");
		outtextxy(i, Hight-15, "■");
	}
	
	for (int  i = 15; i < Hight-25; i +=12)	//打印左右
	{
		outtextxy(0, i, "■");
		outtextxy(Widht - 300, i, "■");
	}

	//判断游戏是否结束,如果ply = false,那就不打印,去游戏结束里面打印
	//不懂sprintf 函数去百度查查
	if (ply)
	{
		sprintf_s(Text_Game, "当前分数为:%d", score);
		showPrint(Widht, 150, Widht - 300, 100, Text_Game, CYAN);
	}
	sprintf_s(Text_Game, "按键: W ↑    S ↓    R →    A ←");
	showPrint(Widht, 150, Widht - 300, 300, Text_Game, CYAN);
}

//游戏结束
void end()
{
	ply = false;
	sprintf_s(Text_Game," 游戏结束!当前分数为:%d",score);
	showPrint(Widht, 150, Widht - 300, 100, Text_Game, RED);
	showsanke();
}

//按键检测
void key_input()
{
	char key;
	if (_kbhit())					//需要好好了解一下这个函数
	{
		key = _getch();
		switch (key)
		{
		case'w':
		case'W':
		case 72:
			if (Headsanke->next->move == Down)					//防止蛇回头下面一样
				break;
			Headsanke->next->move = UP;
			break;
		case's':
		case'S':
		case 80:
			if (Headsanke->next->move == UP)
				break;
			Headsanke->next->move = Down;
			break;
		case'a':
		case'A':
		case 75:
			if (Headsanke->next->move == Right)
				break;
			Headsanke->next->move = Left;
			break;
		case'd':
		case'D':
		case 77:
			if (Headsanke->next->move == Left)
				break;
			Headsanke->next->move = Right;
			break;
		}
	}
	
}


//蛇移动
void movesanke()
{
	//保留头节点的坐标,等会移动生成新的节点要用到,
	int sanke_x = Headsanke->next->x;
	int sanke_y = Headsanke->next->y;

	//移动判断,然后进行计算
	if (Headsanke->next->move == UP )
	{
		Headsanke->next->y -= 10;
	}
	else if(Headsanke->next->move == Down )
	{
		Headsanke->next->y +=10;

	}
	else if (Headsanke->next->move == Right)
	{
		Headsanke->next->x +=10;

	}
	else if (Headsanke->next->move == Left)
	{
		Headsanke->next->x -=10;

	}
	
	/*
	上面代码蛇头经过移动X,Y已经改变了,蛇脖子断了,所以生成新的节点填入刚刚保留的坐标,
	这样脖子就连接上了,记住释放尾巴;
	*/
	pSanke  Newsanke = (pSanke)malloc(sizeof(Sanke));
	Newsanke->x = sanke_x;												//脖子(新的节点)的坐标
	Newsanke->y = sanke_y;
	Newsanke->sankeHead = false;
	Newsanke->next = Headsanke->next->next;			//新的节点指向蛇头的脖子(下一个节点)
	Headsanke->next->next = Newsanke;						//把蛇头->next指向新的节点,这样就连接上了

	//判断蛇头是否碰到墙,因为打印了围墙因为围墙宽是15所以得从15开始判断
	if (Headsanke->next->x <=15 || Headsanke->next->x >= Widht-300 || 
		Headsanke->next->y <= 15 || Headsanke->next->y >= Hight-25)
	{
		//碰到调用结束函数
		end();
	}

	//判断蛇头是否和食物坐标相同
	if ( sankefood != NULL && sankefood->x == Headsanke->next->x && 
		sankefood->y == Headsanke->next->y)
	{
		//相同 调用食物函数,分数++
		score++;
		food();
	}
	else			//如果不和食物相同那就判断蛇头是否和蛇身相同,用蛇头和每一节身体比较
	{
		pSanke p = Headsanke->next;
		while (p->next->next != NULL)
		{
			//这里要是理解不了,可以进行调试进行查看或者画图
			if (p->next->next->x == Headsanke->next->x && p->next->next->y == Headsanke->next->y)
			{
				end();
			}
			p = p->next;
		}
		//释放尾巴
		free(p->next);
		p->next = NULL;
	}

}

 主函数 main.cpp:

#include <stdio.h>
#include <graphics.h>
#include <conio.h>
#include "Sanke.h"
/************************************
	编译环境: vs2019
	项目:贪吃蛇;
	图形工具:Easxy
************************************/
extern bool ply;
extern int speed;

int main()
{

	initgraph(Widht, Hight,NULL);
	initSanke();
	setbkcolor(BLACK);
	cleardevice();
	while (ply)
	{
		cleardevice();
		showsanke();
		key_input();
		movesanke();
		showsanke();
		Sleep(speed);
		setorigin(0, 0);
	}
	_getwch();
	closegraph();
	return 0;
}

 

 

知秋君
上一篇 2024-07-06 17:02
下一篇 2024-07-06 16:36

相关推荐