【C 语言小游戏】手打贪吃蛇 1
先实现两个最简单的功能
播放和关闭音乐功能。
退出游戏功能。
实现三种关卡模式
其实三种关卡模式的逻辑是差不多的,只是我们在最基础的普通模式上面增加了一些限制或者解除了一些限制,所以才有了另外两种模式
无尽模式的实现
当我们的鼠标移在普通模式按键的范围后,通过点击左键就开始实现我们的普通模式
首先我们要重新绘制一个游戏的界面,而初始界面的形成需要我们给出所有的参数,比如蛇的参数(如初始长度、初始坐标、每节的半径、初始方向、蛇的颜色)、食物的参数(如随机产生的食物的坐标、食物的半径、食物的颜色、食物有没有被吃掉的判断标志)。
当我们将这些参数初始化好了之后,我们就可以通过 EasyX 图形库的众多功能将我们这些参数给绘制出来。
接下来就是让小蛇动起来。大家可以先思考下小蛇在你不进行控制时怎样勇敢向前的移动呢?并且遇到边界后需要怎样变化?蛇头碰到身体会怎样?
之后我们看到一条在图形内移动的小蛇,我们需要通过按键控制小蛇的移动使它接近食物。
我们接着要判断有没有吃到食物,如果吃到了,食物的判断标志将变成亦吃掉,并将继续生成新的食物。
但是当我们走到这一步的时候,会发现好像已经没有下一步了,所以是结束了吗?当然不是,我们将往复的进行循环(绘制此时蛇的参数—>蛇按蛇头的方向移动—>控制蛇改变移动方向—>判断是否吃到食物),直到蛇头碰到蛇身就死亡。
死亡后我们就绘制一个新的界面,背景可以是小菜鸡的图片,哈哈。最终再回到开始菜单。
仿照普通模式改变其他关卡模式的功能
菜单界面的绘制其实很简单,其实都是用的 EasyX 图形库里的函数。但是想要使用它还得去安装一下,这一步很简单的,大家可以百度。我就直接上我的代码啦!!!
void menuInit()
{
//创建一个 width 宽和 heigth 的界面
initgraph(width, heigth);
//用图 1 将这个界面填满
loadimage(&img1, "图 1.png", width, heigth);
//图 1 的左上角的顶点为(0,0)
putimage(0, 0, &img1);
//设置填制背景为透明,这样填充文字时背景才是图片的颜色
setbkmode(TRANSPARENT);
//在左上角坐标为(x,y)处输出文字
outtextxy(260, 50, "贪吃蛇大作战");
outtextxy(155, 250, "普通模式");
outtextxy(395, 250, "障碍模式");
outtextxy(275, 250, "无尽模式");
outtextxy(155, 300, "继续游戏");
outtextxy(395, 300, "退出游戏");
outtextxy(155, 350, "打开音乐");
outtextxy(395, 350, "关闭音乐");
//在左上角(x1,y1)和右下角(x2,y2)范围画一个矩形,这个是设置的鼠标点击各按键时的范围
rectangle(150, 245, 225, 270);
rectangle(270, 245, 345, 270);
rectangle(390, 245, 465, 270);
rectangle(150, 295, 225, 320);
rectangle(390, 295, 465, 320);
rectangle(150, 345, 225, 370);
rectangle(390, 345, 465, 370);
}
首先我们要加上播放音乐的头文件
#include<mmstream.h>
和 多媒体设备接口#pragma comment(lib ,"winmm.lib")
。再使用
mciSendString(TEXT("open 路径\\音乐名.mp3 alias XXX"), NULL, 0, NULL);
我们的程序就可以播放这个音乐了,并且该音乐使用时叫做 XXX播放音乐时我们就使用
mciSendString(TEXT("play XXX repeat"), NULL, 0, NULL);
,其中 repeat 是可以删掉的,加它的含义就是让歌曲不断的播放不会结束。关闭音乐时我们就使用
mciSendString(TEXT("close XXX"), NULL, 0, NULL);
这个功能超级简单啦!我自己就是绘制了一个新界面,并用了一张可爱的图片表示再见。
![在这里插入图片描述](https://img-blog.csdnimg.cn/6059b21f05c54901a329488e7ffc1504.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk
,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MTM2Nzg0NQ==,size_16,color_FFFFFF,t_70#pic_center)
上代码!!!
void gameExit()
{
initgraph(width, heigth);
loadimage(&img2, "图 2.jpg", width, heigth);
putimage(0, 0, &img2);
}
首先我们要初始化一个新的界面
我们要初始化蛇的参数。先通过结构体定义蛇的各类参数,看看我的代码吧!
struct Snake
{
int size; //蛇的长度
int speed; //蛇的速度
int dir; //蛇的方向
POINT coor[SNAKE_MAX]; //蛇的最大长度
}snake;
其中 POINT 本身就是一个结构体,它里面包含了 x 和 y 坐标
我们再初始化食物的参数。
struct Food
{
int x;
int y;
int r;
bool flag; //1 没有吃掉 0 吃掉了
DWORD color;
}food;
//typedef unsigned long DWORD;
//DWORD 表示 32bit 无符号整数
接下来我们则对以上参数进行初始化,上代码!
void gameInit1()
{
initgraph(width, heigth);
//小蛇初始化
snake.size = 3;
snake.speed = 10;
snake.dir = LEFT;
for (int i = snake.size - 1; i >= 0; i--)
{
snake.coor[i].x = 10 * i + 400;
snake.coor[i].y = 30;
}
//食物初始化
food.r = rand() % 16 + 5; //食物半径控制在 5~15
food.x = rand() % width; //初始化 x
food.y = rand() % heigth; //初始化 y
food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
food.flag = true;
}
这也是很容易实现的部分,既然有了参数,我们只要对参数通过 EasyX 图形库进行绘制就行
void gameDraw1()
{
loadimage(&img4, "图 4.png", width, heigth);
putimage(0, 0, &img4);
//设置绘图填充颜色
setfillcolor(RGB(61, 89, 171));
for (int i = 0; i < snake.size; i++)
{
//将每个坐标绘制成半径为 5 的园
solidcircle(snake.coor[i].x, snake.coor[i].y, 5);
}
//将绘图填充色修改成食物的颜色
setfillcolor(food.color);
//判断食物存在则在坐标处绘制出半径为食物半径的圆
if (food.flag)
solidcircle(food.x, food.y, food.r);
//结束批量绘制
EndBatchDraw();
}
我们该怎么让小蛇移动呢?是不是蛇其实都是跟着蛇头走的,再具体一点,每一节都是跟着前一节移动。所以我们只要从最后一节到第二节开始循环,每节的坐标等于前一节就行,而蛇头坐标则要靠此时小蛇前进的方向进行改变。可以使用一个 switch 语句进行改变。
我们要思考当蛇头遇到边界了该怎么办。
如果蛇头的坐标等于蛇身的坐标,那也就失败了。我们直接上代码吧!
bool snakeMove1()
{
for (int i = snake.size - 1; i > 0; i--)
{
snake.coor[i] = snake.coor[i - 1];
}
switch (snake.dir)
{
case UP:
snake.coor[0].y -= snake.speed;
if (snake.coor[0].y <= 0)
return false;
break;
case DOWN:
snake.coor[0].y += snake.speed;
if (snake.coor[0].y >= heigth)
return false;
break;
case LEFT:
snake.coor[0].x -= snake.speed;
if (snake.coor[0].x <= 0)
return false;
break;
case RIGHT:
snake.coor[0].x += snake.speed;
if (snake.coor[0].x >= width)
return false;
break;
}
for (int i = snake.size - 1; i > 0; i--)
{
if (snake.coor[0].x == snake.coor[i].x && snake.coor[0].y == snake.coor[i].y)
return false;
}
return true;
}
这里当蛇头遇到边界时,我直接在改变后进行边界判断。而蛇头碰到蛇身则是用一个循环进行判断,碰到了就返回 false。因为我各个功能都是模块化的,在使用这条函数时,用一个判断语句就行。所以我只要返回 false ,其实就会实现该局失败的函数,你们则会看到这张图
首先我们要先简单知道两个函数
kbhit() 函数
功能及返回值:检查当前是否有键盘输入,若有则返回一个非 0 值,否则返回 0
包含头文件:
<conio.h>
getch() 函数
功能:从控制台读取一个字符,但不显示在屏幕上
包含头文件:
<conio.h>
所以首先要判断我们的键盘上是否有输入,没有就下一步,有就将从键盘上读取的字符进行判断,可以使用一个 switch 语句。上代码!
void keyControl()
{
if (_kbhit())
{
switch (_getch())
{
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;
评论