文章目录

目录

文章目录

前言

一、游戏功能和效果

1.游戏功能

2.游戏效果 

二. 游戏初步实现

 1.确定地图大小

2.创建蛇对象,食物对象 

 3.初始化蛇

 4.生成随机食物

5.画蛇 

 6.画食物

7. 游戏内移动控制

1.当按上下左右时移动方向改变 。

 2.开始游戏时的默认方向

 8.判断蛇撞墙,撞身体,撞食物3种情况

1.蛇撞墙:判断蛇头坐标是否和行和列相等,如果相等游戏结束!

2.蛇撞身体:判断蛇头坐标是否和蛇身坐标相等,如果相等游戏结束!

3.蛇撞食物:撞到食物后当前长度增加,再重新生成食物,移动速度变快,分数增加!

4.和蛇移动结合 

 9.画墙和初步实现

 1.画墙 

 2.初步实现

三.游戏的改进与完善

1.解决频闪 

2.去掉蛇尾 

3.去除光标 

四、完整游戏代码

1.game.h文件

 2.test.c文件

总结


前言

本文使用C语言做出简易的贪吃蛇程序,此程序比较简单,适合新手入门或小白。

在这里主要实现一个简易蛇的移动和方向的控制。以及当蛇撞到墙壁,身体,食物这几种情况的判断。

一、游戏功能和效果

1.游戏功能

1.wasd,控制蛇移动的方向。

2.每吃到一个食物移动速度变快。

3.游戏结束显示游戏分数(吃到食物+10分)

2.游戏效果 

二. 游戏初步实现

根据上图可以看到游戏界面大致可以分为3个:1.蛇的图形 2.产生的食物 3.周围的墙壁。(这里‘#’代表食物,‘*’代表蛇身,‘@’代表蛇头)

再细分的话:1.创建蛇的结构体,初始化,改变光标位置,画蛇。2.创建食物结构体,在地图中随机放食物。3.让蛇动起来,用4个键控制蛇的方向,撞到食物身体增长,碰到墙壁结束。4.画墙。 

 1.确定地图大小

 实现程序要有一个适当的范围,行和列可以使用两层for循环来确定:例如20行80列

 

 确定后在头文件定义row和col为20,80就可以了。

2.创建蛇对象,食物对象 

//蛇身对象
struct BODY
{
int X;
int Y;
};
//蛇对象
struct SNAKE
{
struct BODY body[ROW*COL];//蛇身最大长度
int size;//当前长度
}snake;

//食物对象
struct FOOD
{
int X;
int Y;
}food;

 3.初始化蛇

 这里根据效果图,蛇要居中,那么x,y坐标是行和列的1半。

 蛇头是正中间,坐标是第一个:snake.body[0]。

蛇身就是从1开始的下标,第1个下标减1:snake.body[1].X = COL / 2 - 1;是第一节蛇身,第2个下标减2是第二节蛇身,那么使用for循环控制蛇身的长度。

void Initsnake()
{
	//初始化蛇头
	snake.size = 4;
	snake.body[0].X = COL / 2;
	snake.body[0].Y = ROW / 2;
	//初始化蛇身
	int i = 0;
	for (i = 1; i <= snake.size; i++)
	{
		snake.body[i].X = COL / 2 - i;
		snake.body[i].Y = ROW / 2;
	}
}

 4.生成随机食物

将生成的随机数%上行和列,控制在范围内,赋值到food的X,Y坐标上。 

void Initfood()//生成食物
{
		food.X = rand() % COL;
		food.Y = rand() % ROW;
}

5.画蛇 

在画蛇时我们要在地图的中间进行画蛇,那么怎么才能在中间呢?

一般在我们打字时,后面都有一个光标,光标在哪字就打在哪。所以在这里需要设置光标的位置,使他在中间就可以了。这里要用到光标修改函数:我们把相应的坐标给这个函数,就会定位到相应的位置。

void InitUI()
{//画蛇
	COORD coord = { 0 };
	int i = 0;
	for (i = 0; i < snake.size; i++)//改变光标位置
	{
		coord.X = snake.body[i].X;
		coord.Y = snake.body[i].Y;
		SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
		if (i == 0)
		{
			putchar('@');
		}
		else
		{
			putchar('*');
		}
	}

 在这里我们直接使用就可以,当i==0时,就画蛇头,其他情况就是蛇尾,画‘*’号。

 6.画食物

和画蛇相同 

//画食物    
            coord.X = food.X;
            coord.Y = food.Y;
            SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
            putchar('#'); 

7. 游戏内移动控制

1.当按上下左右时移动方向改变 。

这里可以看图:这里蛇头的坐标是(6,4),当按下上键坐标为(6,3),按下键为(6,5)当按左键右键时,坐标为(5,4),(7,4)。

那么改变方向只需要将X与Y坐标进行+1,-1就可以实现,这里我们定义两个全局变量kx,ky。

 2.开始游戏时的默认方向

 当我们打开游戏,如果不按方向键,蛇也会默认向一个方向移动,这里我们要使用非阻塞方式检测是否有输入:kbhit()函数和无回显的取一个字符:getch()函数。

 

 具体写法如下:默认向右移动,当有键盘输入时改变方向。

 8.判断蛇撞墙,撞身体,撞食物3种情况

 这里可以使用while()来将这3种情况和蛇移动放在一起,进行实现。

1.蛇撞墙:判断蛇头坐标是否和行和列相等,如果相等游戏结束!

while (snake.body[0].X >= 0 && snake.body[0].X < COL&&
        snake.body[0].Y >= 0 && snake.body[0].Y < ROW)
    {

2.蛇撞身体:判断蛇头坐标是否和蛇身坐标相等,如果相等游戏结束!

//蛇头撞身体
        int i = 0;
        for (i = 1; i < snake.size; i++)
        {
            if (snake.body[0].X == snake.body[i].X&&snake.body[0].Y == snake.body[i].Y)
            {
                return;
            }
        }

3.蛇撞食物:撞到食物后当前长度增加,再重新生成食物,移动速度变快,分数增加!

//蛇头撞食物
        if (snake.body[0].X == food.X&&snake.body[0].Y == food.Y)
        {
            snake.size++;//身体增长
            Initfood();//食物消失
            result += 10;//加分
            if (speed != 60)
            {
                speed -= 30;//速度增加,到最大速度时停止
            }
        } 

4.和蛇移动结合 

void Playgame()//游戏控制
{
    char key = 'd';
    //蛇撞墙    
    while (snake.body[0].X >= 0 && snake.body[0].X < COL&&
        snake.body[0].Y >= 0 && snake.body[0].Y < ROW)
    {
     
        //接收按键输入
        if (_kbhit())
        {
            key = _getch();
        }
        switch (key)
        {
        case 'w':kx = 0; ky = -1; break;
        case 's':kx = 0; ky = +1; break;
        case 'd':kx = +1; ky = 0; break;
        case 'a':kx = -1; ky = 0; break;
        default:
            break;
        }
        //蛇头撞身体
        int i = 0;
        for (i = 1; i < snake.size; i++)
        {
            if (snake.body[0].X == snake.body[i].X&&snake.body[0].Y == snake.body[i].Y)
            {
                return;
            }
        }
        //蛇头撞食物
        if (snake.body[0].X == food.X&&snake.body[0].Y == food.Y)
        {
            snake.size++;//身体增长
            Initfood();//食物消失
            result += 10;//加分
            if (speed != 60)
            {
                speed -= 30;//速度增加,到最大速度时停止
            }
        }
 

 9.画墙和初步实现

 1.画墙 

void Initwall()//画墙
{
	int i = 0;
	int j = 0;
	for (i = 0; i <= ROW; i++)
	{
		for (j = 0; j <= COL; j++)
		{
			if (i == 0)
			{
				printf("-");
			}
			else if (i == ROW)
			{
				printf("-");

			}
			else if (j == 0)

			{
				printf("|");
			}
			else if (j == COL)
			{
				printf("|");
			}
			else
			{
				printf(" ");
			}
		}
		printf("\n");
	}
}

 2.初步实现

将墙画好后,蛇还不能移动,我们先看图:

蛇原来的坐标是(6,5)(6,4)6,3)(6,2),当向前走1格后是(6,6)(6,5)(6,4)(6,3)。

原来蛇头的坐标(6,5)给第一节蛇身,第一节蛇身给第2节,以此类推。也就是蛇头的坐标根据方向进行+1或-1,蛇身由上一节的进行赋值。

写好后再用清屏函数system("cls")进行更新,就可以初步实现!

但是运行后发现不断清屏会出现频闪,游戏效果非常不好!

 

三.游戏的改进与完善

1.解决频闪 

初步完成后,蛇确实可以跑起来,但游戏体验太差!下面解决频闪问题! 

这里第一想到的便是将清屏函数去掉换成延时函数:Sleep(),这样不需要一直更新,但又会出现新的问题:后面蛇尾会一直在,不会消失!那么就需要将最后一节蛇尾去掉。 

2.去掉蛇尾 

首先我们将蛇尾的坐标存起来:

//存储蛇尾的坐标
        lastX = snake.body[snake.size - 1].X;
        lastY = snake.body[snake.size - 1].Y;
 

然后我们在画蛇的函数中InitUI() ,将蛇尾坐标变成空格:

//去除蛇尾
    coord.X = lastX;
    coord.Y = lastY;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
    putchar(' ');
 

到这里我们就完美的解决掉问题了! 最后将光标去除就好了!!

3.去除光标 

这里直接使用此函数就可以! 

 

四、完整游戏代码

1.game.h文件

#ifndef __GAME_H__
#define __GAME_H__
//定义行和列
#define ROW 20
#define COL 80
//头文件
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<conio.h>
#include<string.h>
#include<windows.h>

void Initsnake();//初始化蛇
void Initfood();//初始化食物
void InitUI();//画蛇
void Initwall();//画墙
void Playgame();//游戏:控制

int speed = 300;//初始速度
int result = 0;//游戏分数
lastX = 0;//存放蛇尾坐标
lastY = 0;
int kx = 0;//方向控制
int ky = 0;

//蛇身结构体
struct BODY
{
	int X;
	int Y;
};

//创建蛇结构体
struct SNAKE
{
	struct BODY body[ROW*COL];//蛇身最大长度
	int size;//长度
}snake;

//创建食物结构体
struct FOOD
{
	int X;
	int Y;
}food;

#endif

 2.test.c文件

#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"

void Initsnake()
{
	//初始化蛇头
	snake.size = 4;
	snake.body[0].X = COL / 2;
	snake.body[0].Y = ROW / 2;
	//初始化蛇身
	int i = 0;
	for (i = 1; i <= snake.size; i++)
	{
		snake.body[i].X = COL / 2 - i;
		snake.body[i].Y = ROW / 2;
	}
}


void Initfood()//生成食物
{
		food.X = rand() % COL;
		food.Y = rand() % ROW;
}

void InitUI()
{//画蛇
	COORD coord = { 0 };
	int i = 0;
	for (i = 0; i < snake.size; i++)//改变光标位置
	{
		coord.X = snake.body[i].X;
		coord.Y = snake.body[i].Y;
		SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
		if (i == 0)
		{
			putchar('@');
		}
		else
		{
			putchar('*');
		}
	}
	//去除蛇尾
	coord.X = lastX;
	coord.Y = lastY;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
	putchar(' ');

	//画食物	
			coord.X = food.X;
			coord.Y = food.Y;
			SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
			putchar('#');

			//结束时,改变位置
			coord.X = 0;
			coord.Y = ROW + 2;
			SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
void Initwall()//画墙
{
	int i = 0;
	int j = 0;
	for (i = 0; i <= ROW; i++)
	{
		for (j = 0; j <= COL; j++)
		{
			if (i == 0)
			{
				printf("-");
			}
			else if (i == ROW)
			{
				printf("-");

			}
			else if (j == 0)

			{
				printf("|");
			}
			else if (j == COL)
			{
				printf("|");
			}
			else
			{
				printf(" ");
			}
		}
		printf("\n");
	}
}

void Playgame()//游戏控制
{
	char key = 'd';
	//蛇撞墙	
	while (snake.body[0].X >= 0 && snake.body[0].X < COL&&
		snake.body[0].Y >= 0 && snake.body[0].Y < ROW)
	{
		InitUI();//更新蛇
		//接收按键输入
		if (_kbhit())
		{
			key = _getch();
		}
		switch (key)
		{
		case 'w':kx = 0; ky = -1; break;
		case 's':kx = 0; ky = +1; break;
		case 'd':kx = +1; ky = 0; break;
		case 'a':kx = -1; ky = 0; break;
		default:
			break;
		}
		//蛇头撞身体
		int i = 0;
		for (i = 1; i < snake.size; i++)
		{
			if (snake.body[0].X == snake.body[i].X&&snake.body[0].Y == snake.body[i].Y)
			{
				return;
			}
		}
		//蛇头撞食物
		if (snake.body[0].X == food.X&&snake.body[0].Y == food.Y)
		{
			snake.size++;//身体增长
			Initfood();//食物消失
			result += 10;//加分
			if (speed != 60)
			{
				speed -= 30;//速度增加,到最大速度时停止
			}
		}
		//存储蛇尾的坐标
		lastX = snake.body[snake.size - 1].X;
		lastY = snake.body[snake.size - 1].Y;

		//蛇移动,前一节身体给后一节身体赋值
		for (i = snake.size - 1; i > 0; i--)
		{
			snake.body[i].X = snake.body[i - 1].X;
			snake.body[i].Y = snake.body[i - 1].Y;

		}
		snake.body[0].X += kx;
		snake.body[0].Y += ky;
		Sleep(speed);
	}
	return;
}



void game()//游戏函数
{	
	//去除光标
	CONSOLE_CURSOR_INFO cci;
	cci.dwSize = sizeof(cci);
	cci.bVisible = FALSE;
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);
	//初始化蛇
	Initsnake();
	//初始化食物
	Initfood();
	//画墙
	Initwall();
	//画蛇
	InitUI();
	//游戏控制
	 Playgame();
	//成绩
	 printf("Game over!游戏结束!\n");
	 printf("游戏分数:%d\n", result);
}

int main()
{
	srand((unsigned int)time(NULL));
	game();//游戏函数
	return 0;
}


总结

以上就是今天贪吃蛇的内容,主要就是蛇移动的实现比较难。这里就是比较简单实现方法,230行左右就可以实现,还是比较容易理解的!可以根居其他的加一些功能来进行完善!

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐