目录
0前言
C入门小白一枚,为了更好地深入学习C++,跟随博主们撰写游戏编程代码。
本文是在小破站跟随一个博主写的代码。C语言必学项目:贪吃蛇!大一计算机专业巩固C语言基础常用项目,一个小时带你做出C语言入门级游戏!_哔哩哔哩_bilibili代码比较粗糙,属于入门级别。
1.游戏初始化
首先要初始化一个游戏界面大小,一条蛇的形状大小、移动速度、移动方向、蛇头蛇身坐标。
设置窗口大小用到 initgraph() 函数。
注意:设置蛇头蛇身时注意头身坐标。蛇的速度和蛇身(实心圆)的半径要形成关系,否则行走的位移大小不一样,蛇身会重合。
void GameInit()
{
//init 初始化游戏窗口大小 graph 图形窗口大小为640*480
initgraph(640, 480);
//初始化蛇
snake.size = 3;
snake.speed = 10;// 蛇的速度和蛇身(实心圆)的半径要形成关系,否则行走的位移大小不一样,蛇身会重合
snake.dir = RIGHT;
for (int i = 0; i < snake.size; i++)
{
snake.coor[i].x = 40-10*i;//坐标:蛇尾20蛇身30蛇头40
snake.coor[i].y = 10;
}
}
//绘制图形
void GameDraw()
{
//双缓冲绘图
BeginBatchDraw();
//设置窗口背景颜色
setbkcolor(RGB(47,79,79));
cleardevice();//清除原设备
//绘制蛇
/*setfillcolor(BLUE);*/ //设置蛇的颜色
for (int i = 0; i < snake.size; i++)
{
solidcircle(snake.coor[i].x, snake.coor[i].y, 5);//绘制实心圆作为蛇的一节
}
EndBatchDraw();//结束缓冲
}
2.实现蛇的移动
蛇初始状态就应能够实现自主移动。蛇的移动是坐标的改变,这里注意头身的半径和每一次位移的步长。步长==蛇的直径。
蛇的移动方向变换由键盘输入键值实现。通过kbhit()和getch()函数实现(包含在头文件<conio.h>中)。同时要撰写枚举,定义UP、Down、Right、Left。
蛇向上走必不能向下掉头,向左走不能向右掉头。(否则就是把自己吃掉了)
3.吃食物
首先要随机生成食物。系统产生一个随机数,如果单纯的用rand函数 ,每次执行产生的结果都是一样的,这导致随机数并不随机。因此需要需要设置随机数种子,srand()函数包含在头文件<stdlib.h>中。
// 数据的初始化
void GameInit()
{
// 设置随机数种子
srand(GetTickCount());//获取系统开机到现在所经过的毫秒数
// 初始化食物
food.x = rand() % HEIGHT;//求模
food.y = rand() % LENGTH;
// 设置随机数种子需要头文件stdlib.h 一般把时间作为随机数种子,因为时间是在不短变化的
food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
food.r = rand() % 10 + 5;
food.flag = true;
}
4.完整代码
#include<stdio.h>
#include<graphics.h>//绘图头文件
#include<conio.h>//获取按键信息头文件
#include<stdlib.h>//设置随机数种子需该头文件
#include<mmsystem.h>//定义的多媒体API 实现声音输出,在程序中加入背景音乐要包含该头文件
#pragma comment(lib,"winmm.lib")//告诉编译器,加载winmm.lib库文件(音乐)
/*
贪吃蛇
知识点:结构体、循环、函数、easyx,数组
API接口输入
*/
//做一个界面,创建一个图形窗口
#define SNAKE_NUM 500 //宏的定义,蛇的最大节数
#define HEIGHT 480
#define LENGTH 640
/*enum 创建枚举类型;创建数据类型DIR,和 DIR 数据类型关联的命名整数常量被称为枚举量,DIR 数据类型的变量可能只是关联到这些枚举量的值之一
* enum Colors { red, orange, yellow = 9, green, blue };
* 在该示例中,命名常量 red 将被赋值为 0,orange 将为 1,yellow 将为 9,green 将为 10,blue 将为 11。
*/
enum DIR
{
UP,
DOWN,
LEFT,
RIGHT,
};
//蛇的结构
struct Snake
{
int size;//蛇的节数
int dir;//蛇的方向
int speed;//蛇的速度
POINT coor[SNAKE_NUM];//坐标,蛇的最大节数为500节
}snake;
// 食物的结构
struct Food
{
int x;
int y;
int r; // 食物半径大小
bool flag; // 食物是否被吃了
DWORD color; // 食物颜色 DWORD 表示Double Word双精度
}food;
// 数据的初始化
void GameInit()
{
// 播放背景音乐
//mciSendString("open BGM.mp3 alias MYBGM",0,5,0);// 向多媒体设备发射一个字符串
// alias BGM给该音乐取名为BGM
//mciSendString("play MYBGM repeat",0,0,0);// repeat循环播放
//这里需要添加一个.mp3纯文件到项目文件夹里。
//init 初始化 graph 图形窗口大小为640*480 SHOWCONSOLE显示控制台
initgraph(LENGTH, HEIGHT/*,SHOWCONSOLE*/);
// 设置随机数种子
srand(GetTickCount());//获取系统开机到现在所经过的毫秒数
//初始化蛇
snake.size = 3;
snake.speed = 10;// 蛇的速度和蛇身(实心圆)的半径要形成关系,否则行走的位移大小不一样,蛇身会重合
snake.dir = RIGHT;
for (int i = 0; i < snake.size; i++)
{
snake.coor[i].x = 40-10*i;//坐标:蛇尾20蛇身30蛇头40
snake.coor[i].y = 10;
}
// 初始化食物 rand():随机产生一个数,如果没有设置随机数种子,每次产生的都是固定的整数
food.x = rand() % HEIGHT;
food.y = rand() % LENGTH;
// 设置随机数种子需要头文件stdlib.h 一般把时间作为随机数种子,因为时间是在不短变化的
food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
food.r = rand() % 10 + 5;
food.flag = true;
}
//绘制图形
void GameDraw()
{
//双缓冲绘图
BeginBatchDraw();
//设置窗口背景颜色
setbkcolor(RGB(47,79,79));
cleardevice();//清除原设备
//绘制蛇
/*setfillcolor(BLUE);*/ //设置蛇的颜色
for (int i = 0; i < snake.size; i++)
{
solidcircle(snake.coor[i].x, snake.coor[i].y, 5);//绘制实心圆作为蛇的一节
}
// 绘制食物
if (food.flag)
{
solidcircle(food.x, food.y, food.r);
}
EndBatchDraw();//结束缓冲
}
//移动蛇
void snakeMove()
{
// snake.coor[i].x++;
// snake.coor[i].y++;
// 让身体跟随头部移动
for (int i = snake.size-1; i > 0 ; i--)
{
snake.coor[i] = snake.coor[i - 1];
}
// 移动是坐标发生改变
switch (snake.dir)//判断移动方向,位置坐标发生相应改变
{
case UP:
if (snake.coor[0].y <= 0)
{
snake.coor[0].y = HEIGHT;
}
snake.coor[0].y-=snake.speed;//蛇头向上移动
break;
case DOWN:
if (snake.coor[0].y >= HEIGHT)
{
snake.coor[0].y = 0;
}
snake.coor[0].y+= snake.speed;//蛇头向下移动
break;
case LEFT:
if (snake.coor[0].x <= 0)
{
snake.coor[0].x = LENGTH;
}
snake.coor[0].x-= snake.speed;//蛇头向左移动
break;
case RIGHT:
if (snake.coor[0].x >= LENGTH)
{
snake.coor[0].x = 0;
}
snake.coor[0].x+= snake.speed;//蛇头向右移动
break;
}
}
// 通过按键改变蛇的移动方向,kbhit()和getch()需要使用一个头文件conio.h
void keyControl()
{
//判断有无键值输入
if (_kbhit())//_kbhit()检测是否有键值输入,若有按键输入则返回真1
{
//键值72 80 75 77分别表示上下左右键值
switch (_getch())//识别获取按键方向使用getch()函数,_getch是一个阻塞函数,当把该函数放入main函数中时,按一下键盘它才动一次,所以要先判断有无键值输入
{
case 'w'://上
case 'W':
case '72'://数字键值用不了,为什么??????
//改变方向
if (snake.dir != DOWN)
{
snake.dir = UP;
}
break;
case 's'://下
case 'S':
//case '80':
if (snake.dir != UP)
{
snake.dir = DOWN;
}
break;
case 'a'://左
case 'A':
case '75':
if (snake.dir != RIGHT)
{
snake.dir = LEFT;
}
break;
case 'd'://右
case 'D':
case '77':
if (snake.dir != LEFT)
{
snake.dir = RIGHT;;
}
break;
// 暂停游戏
case ' ':
while (1)// 输入空格,则进入死循环
{
if (_getch() == ' ')//若再键入空格,则返回主函数
{
return;
}
}
break;
}
}
}
//吃食物
void EatFood()
{
if (food.flag && snake.coor[0].x <= food.x + food.r && snake.coor[0].x >= food.x - food.r &&
snake.coor[0].y <= food.y + food.r && snake.coor[0].y >= food.y - food.r)
{
food.flag = false;
snake.size++;
}
// 食物被吃掉消失,重新生成一个食物
if (!food.flag)
{
// 初始化食物
food.x = rand() % HEIGHT;
food.y = rand() % LENGTH;
food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
food.r = rand() % 10 + 5;
food.flag = true;
}
}
int main()
{
GameInit();
// GameDraw();
//snakeMove();
while (1)//无限循环,把界面锁死,不闪屏
{
snakeMove();
keyControl();
GameDraw();
EatFood();
Sleep(100);//延迟50ms
}
return 0;
}
5.拓展(播放背景音乐)
mciSendString("open BGM.mp3 alias MYBGM",0,5,0);// 向多媒体设备发射一个字符串
// alias BGM给该音乐取名为BGM
mciSendString("play MYBGM repeat",0,0,0);// repeat循环播放
写该段代码前需添加一个.mp3纯文件到项目文件夹里。
6.改进
该代码还存在一些bug和缺陷本文中还未解决。
bug1:游戏开始界面有概率不会出现食物。
bug2:游戏开始一半后有概率不会再出现食物。
缺陷1:每次只出现一个食物。
缺陷2:贪吃蛇的模样比较简约。
缺陷3:贪吃蛇不会撞墙死。