首先看成品:
第一步 写出头文件
#include<stdio.h>
#include<graphics.h>
#include<conio.h>
//多媒体设备接口
#include<mmsystem.h>
#pragma comment(lib,"winmm.lib")
#include<stdio.h>
标准输入输出头文件,在此程序中用到的函数有:printf,用于输出文字
#include<graphics.h>
C语言图形库,如果需要使用这个头文件,那么可以下载easyX,b站有很多教程,这里就不在阐述了。initgraph、BeginBatchDraw()等函数的头文件都是它。
#include<conio.h>
通用输入输出库,主要是一些用户通过按键盘产生的对应操作,比如getch()函数。
#include<mmsystem.h>
#pragma comment(lib,"winmm.lib") 运用这个头文件来加入背景音乐
第二步 对游戏中的对象赋予属性
用这个来定义蛇的最大节数
#define SNAKE_NUM 500//蛇的最大节数
定义蛇的结构体,定义结构体时要把所有参数考虑进去,想不到的参数用到了再加。
//定义蛇的结构体
struct Snake
{
int size;//蛇的节数
int dir;//蛇的方向
int speed;//蛇的速度
POINT coor[SNAKE_NUM];//蛇的坐标
}snake;
定义食物结构
//食物结构
struct Food
{
int x;
int y;
int r; //食物大小
bool flag; //食物是否被吃
DWORD color; //食物的颜色
}food;
第三步,利用枚举,枚举出方向
(枚举类型可以让我们的程序使用一些固定长度和固定数值的变量值范围)
enum DIR//枚举出方向
{
UP,
DOWN,
LEFT,
RIGHT,
};
第四步,数据初始化
//数据初始化
void GameInit()
{
//播放背景音乐
mciSendString("open snakemusic.mp3 alias BGM",0,0,0);
mciSendString("play BGM repeat", 0, 0, 0);
//初始化窗口
initgraph(600, 600); //创建图形窗口600*600像素
//设置随机数种子
srand(GetTickCount());//获取系统开机到现在所经过的毫秒数
//初始化蛇
snake.size = 3;
snake.dir=RIGHT;
snake.speed = 10;
for (int i = 0; i <snake.size; i++)
{
snake.coor[snake.size - i - 1].x = 10 * i + 10;
snake.coor[snake.size - i - 1].y = 10;
}
//初始化食物
// rand()随机产生一个整数,如果没有设置随机数种子,每次产生的都是固定的整数
//设置种子需要头文件 stdlib.h,一般把随机数作为种子,因为时间在不断变化
food.x = rand() % 500;
food.y = rand() % 500;//% 500的意思是food不能超过窗口的边界
food.color = RGB(rand() % 256, rand() % 256, rand() % 256);//256,RGB的界限
food.r = rand() % 10 + 5;
food.flag = true;
}
函数:
1、mciSendString 用于播放背景音乐,在上面的函数中,音乐的名字为snakemusic.mp3,在调用音乐和图片前,我们需要将图片和音乐复制到和源文件的同一个文件夹中,可以按照这个步骤:
mciSendString函数的用法:播放并重复音乐,通过open与play这两个指令进行播放。
如·:播放snakemusic.mp3这个音乐
mciSendString("open snakemusic.mp3 alias BGM",0,0,0);
mciSendString("play BGM repeat", 0, 0, 0);
在这里,alias的意思是“别名”,所以这里起到的作用是将snakemusic.mp3起一个别名为BGM,在这里BGM可以任意,然后下面play直接调用BGM就行。
2.initgraph(600, 600) 初始化窗口函数
(600, 600)的意思是600*600像素,作用是创建一个像素为600*600的窗口。
创建完窗口以后,我们需要写一个随机数函数,用于各种对象的正常活动。在电脑中没有真正的随机数,我们只能取一个与随机数相近的数。
3.srand(GetTickCount()); 设置随机数种子
这个函数用于获取系统开机到现在所经过的毫秒数,从而得到一个随机数种子。
顺便介绍rand()函数,这是一个可以通过随机数种子产生一个伪随机数的函数,rand()的内部是用线性同余法实现的,所以可以将它看作一个差不多的随机数。在调用rand()函数之前,可以使用srand()函数设置随机数种子,如果没有设置随机数种子,rand()函数在调用时,自动设计随机数种子为1。随机种子相同,每次产生的随机数也会相同。详细可以参考http://t.csdn.cn/QD9mR
第五步、设置得分函数模块
//得分函数
void yourPoint()
{
char a, b, c;
a = '0';
b = '0';
c = '0';
int v = 0;
v = (snake.size - 3)*10;
b = '0' + v %100/10;
a = '0' + v % 1000/100;
outtextxy(500, 100, "你的得分是");
outtextxy(500, 120, a);
outtextxy(510, 120, b);
outtextxy(520, 120, c);
}
outtextxy函数 属于easyX函数 用于在指定位置输出字符串
用法:outtextxy(横坐标, 纵坐标, 需要输入的内容);
注意:横纵坐标对应的是像素。
第六步、绘制
// 绘制开始界面
void begin()
{
IMAGE BeginUI; //定义图像变量
initgraph(600, 600);
loadimage(&BeginUI, "Begin.jpg", 600, 600);//将图片的宽拉到600,长拉到600像素点
putimage(0, 0, &BeginUI);
while (_getch())
{
break;
}
}
//k头
void kkhuizhi()
{
IMAGE kk; //定义图像变量
loadimage(&kk, "kkHead.jpg", 30, 30);//将图片的宽拉到30,长拉到30像素点
putimage(snake.coor[0].x-5, snake.coor[0].y-10, &kk);//将图片添加在蛇对应的坐标中
}
//绘制
void GameDraw()
{
//双缓冲绘图
BeginBatchDraw();
//设置背景颜色
setbkcolor(RGB(85, 170, 255));
cleardevice();
//绘制背景图片
IMAGE bk;
loadimage(&bk, "bk.jpg", 600, 600);
putimage(0, 0, &bk);
//绘制蛇
for (int i = 0; i < snake.size; i++)
{
IMAGE KkBody;
loadimage(&KkBody,"kkbody.jpg", 20, 20);
putimage(snake.coor[i].x, snake.coor[i].y, &KkBody);
//solidcircle(snake.coor[i].x, snake.coor[i].y, 5);
}
//绘制食物
if (food.flag)
{
setfillcolor(food.color);
IMAGE ball;
loadimage(&ball, "ball.jpg",20, 20);
putimage(food.x, food.y, &ball);
//fillcircle(food.x, food.y, food.r);
//solidcircle(food.x,food.y, food.r);
}
//绘制得分板
yourPoint();
//绘制kk
kkhuizhi();
EndBatchDraw();//这个函数用于结束批量绘制,并执行未完成的绘制任务
}
1、双缓冲绘图
BeginBatchDraw(); 这个函数用于开始批量绘图。执行后,任何绘图操作都将暂时不输出到绘图窗口上,直到执行 FlushBatchDraw 或 EndBatchDraw 才将之前的绘图输出。
2、输出图片,用到的函数有三个(IMAGE loadimage putimage)
IMAGE kk; //定义图像变量
loadimage(&kk, "kkHead.jpg", 30, 30); //将图片的宽拉到30,长拉到30像素点
putimage(snake.coor[0].x-5, snake.coor[0].y-10, &kk); //将图片添加在蛇对应的坐标中
第七步、结束游戏模块
//结束游戏
void gameover()
{
mciSendString("close BGM ", 0, 0, 0);
mciSendString("open why.mp3 alias sBGM", 0, 0, 0);
mciSendString("play sBGM ", 0, 0, 0);
IMAGE endUI; //定义图像变量
initgraph(600, 600);
loadimage(&endUI, "endUI.jpg", 600, 600);//将图片的宽拉到600,长拉到600像素点
putimage(0, 0, &endUI);
yourPoint();
while (1)
{
if (_getch() == 'R'|| _getch() == 'r')
{
snake.size = 3;
snake.dir = RIGHT;
snake.speed = 10;
for (int i = 0; i < snake.size; i++)
{
snake.coor[snake.size - i - 1].x = 10 * i + 10;
snake.coor[snake.size - i - 1].y = 10;
}
break;
}
if (_getch() == 'B'|| _getch() == 'b')
exit(1);
}
}
那么早写结束游戏模块是因为在后面的函数模块中会经常调用结束游戏模块
exit函数
头文件:stdlib.h
功 能: 关闭所有文件,终止正在执行的进程。详细:http://t.csdn.cn/y9IYK
getch函数
头文件:conio.h 函数用途:从控制台读取一个字符,但不显示在屏幕上
我们一般所使用的 getchar() 函数在读入一个字符时必须按一下 Enter 键,该代码才会继续运行;但是 _getch() 函数读入一个字符时 不用 enter 代码会继续跑。
第八步、移动和互动模块
//移动蛇
void snakeMove()
{
//让身体跟着头移动
for (int i = snake.size-1; i >0; i--)
{
snake.coor[i] = snake.coor[i - 1];
}
//选择方向
switch (snake.dir)
{
case RIGHT:
snake.coor[0].x+=snake.speed;
if (snake.coor[0].x == 610)//撞墙后弹出gameover
{
gameover();
}//超出右边的边界
break;
case LEFT:
snake.coor[0].x-= snake.speed;
if (snake.coor[0].x <= 0)
{
gameover();
}//超出左边的边界
break;
case UP:
snake.coor[0].y-= snake.speed;
if (snake.coor[0].y <= 0)
{
gameover();
}//超出上边的边界
break;
case DOWN:
snake.coor[0].y+= snake.speed;
if (snake.coor[0].y ==610)
{
gameover();
}//超出下边的边界
break;
}
}
//碰到身体,gameover
void touchself()
{
for (int i = 1; i <= snake.size; i++)
{
if (snake.coor[i].x == snake.coor[0].x&& snake.coor[i].y == snake.coor[0].y)
gameover();
}
}
//通过按键移动蛇的移动方向
//72 80 75 77 上下左右键值
void keyControl()
{
//_kbhit()判断是否有按键,如果有按键就返回真。
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;
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;
}
}
}
//吃食物
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)
{
//Sleep(25);
//mciSendString("open zy.mp3 alias sBGM", 0, 0, 0);
//mciSendString("play sBGM close ", 0, 0, 0);
srand(GetTickCount());//获取系统开机到现在所经过的毫秒数
food.x = rand() % 500;
food.y = rand() % 500;//food不能超过窗口的边界
food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
food.r = rand() % 10 + 5;
food.flag = true;
}
}
第九步、游戏暂停模块
//游戏暂停
void stop()
{
if (_kbhit())
{
if (_getch() == ' ')
{
while (1)
{
IMAGE pauseUI; //定义图像变量
initgraph(600, 600);
loadimage(&pauseUI, "pause.jpg", 600, 600);//将图片的宽拉到600,长拉到600像素点
putimage(0, 0, &pauseUI);
if (_getch() == ' ')
{
break;
}
}
}
}
}
第十步、调用模块
int main()
{
printf("请在此框输入\n");
system("pause");
begin();
GameInit();
while (1)
{
snakeMove();
GameDraw();
keyControl();
EatFood();
Sleep(100);
touchself();
stop();
}
return 0;
}
整体代码:(图片和音频在后面)
#include<stdio.h>
#include<graphics.h>
#include<conio.h>
//多媒体设备接口
#include<mmsystem.h>
#pragma comment(lib,"winmm.lib")
#define SNAKE_NUM 50//蛇的最大节数
enum DIR//枚举出方向
{
UP,
DOWN,
LEFT,
RIGHT,
};
//食物结构
struct Food
{
int x;
int y;
int r; //食物大小
bool flag; //食物是否被吃
DWORD color; //食物的颜色
}food;
//定义蛇的结构体
struct Snake
{
int size;//蛇的节数
int dir;//蛇的方向
int speed;//蛇的速度
POINT coor[SNAKE_NUM];//蛇的坐标
}snake;
//数据初始化
void GameInit()
{
//播放背景音乐
mciSendString("open snakemusic.mp3 alias BGM",0,0,0);
mciSendString("play BGM repeat", 0, 0, 0);
//初始化窗口
initgraph(600, 600); //创建图形窗口600*600像素
//设置随机数种子
srand(GetTickCount());//获取系统开机到现在所经过的毫秒数
//初始化蛇
snake.size = 3;
snake.dir=RIGHT;
snake.speed = 10;
for (int i = 0; i <snake.size; i++)
{
snake.coor[snake.size - i - 1].x = 10 * i + 10;
snake.coor[snake.size - i - 1].y = 10;
}
//初始化食物
// rand()随机产生一个整数,如果没有设置随机数种子,每次产生的都是固定的整数
//设置种子需要头文件 stdlib.h,一般把随机数作为种子,因为时间在不断变化
food.x = rand() % 500;
food.y = rand() % 500;//food不能超过窗口的边界
food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
food.r = rand() % 10 + 5;
food.flag = true;
}
//得分函数
void yourPoint()
{
char a, b, c;
a = '0';
b = '0';
c = '0';
int v = 0;
v = (snake.size - 3)*10;
b = '0' + v %100/10;
a = '0' + v % 1000/100;
outtextxy(500, 100, "你的得分是");
outtextxy(500, 120, a);
outtextxy(510, 120, b);
outtextxy(520, 120, c);
}
// 绘制开始界面
void begin()
{
IMAGE BeginUI; //定义图像变量
initgraph(600, 600);
loadimage(&BeginUI, "Begin.jpg", 600, 600);//将图片的宽拉到600,长拉到600像素点
putimage(0, 0, &BeginUI);
while (_getch())
{
break;
}
}
//k头
void kkhuizhi()
{
IMAGE kk; //定义图像变量
loadimage(&kk, "kkHead.jpg", 30, 30);//将图片的宽拉到600,长拉到600像素点
putimage(snake.coor[0].x-5, snake.coor[0].y-10, &kk);
}
//绘制
void GameDraw()
{
//双缓冲绘图
BeginBatchDraw();
//设置背景颜色
setbkcolor(RGB(85, 170, 255));
cleardevice();
//绘制背景图片
IMAGE bk;
loadimage(&bk, "bk.jpg", 600, 600);
putimage(0, 0, &bk);
//绘制蛇
for (int i = 0; i < snake.size; i++)
{
IMAGE KkBody;
loadimage(&KkBody,"kkbody.jpg", 20, 20);
putimage(snake.coor[i].x, snake.coor[i].y, &KkBody);
//solidcircle(snake.coor[i].x, snake.coor[i].y, 5);
}
//绘制食物
if (food.flag)
{
setfillcolor(food.color);
IMAGE ball;
loadimage(&ball, "ball.jpg",20, 20);
putimage(food.x, food.y, &ball);
//fillcircle(food.x, food.y, food.r);
//solidcircle(food.x,food.y, food.r);
}
//绘制得分板
yourPoint();
//绘制kk
kkhuizhi();
EndBatchDraw();//这个函数用于结束批量绘制,并执行未完成的绘制任务
}
//结束游戏
void gameover()
{
mciSendString("close BGM ", 0, 0, 0);
mciSendString("open why.mp3 alias sBGM", 0, 0, 0);
mciSendString("play sBGM ", 0, 0, 0);
IMAGE endUI; //定义图像变量
initgraph(600, 600);
loadimage(&endUI, "endUI.jpg", 600, 600);//将图片的宽拉到600,长拉到600像素点
putimage(0, 0, &endUI);
yourPoint();
while (1)
{
if (_getch() == 'R' || _getch() == 'r')
{
snake.size = 3;
snake.dir = RIGHT;
snake.speed = 10;
for (int i = 0; i < snake.size; i++)
{
snake.coor[snake.size - i - 1].x = 10 * i + 10;
snake.coor[snake.size - i - 1].y = 10;
}
break;
}
if (_getch() == 'B' || _getch() == 'b')
exit(1);
}
}
//移动蛇
void snakeMove()
{
//让身体跟着头移动
for (int i = snake.size-1; i >0; i--)
{
snake.coor[i] = snake.coor[i - 1];
}
//选择方向
switch (snake.dir)
{
case RIGHT:
snake.coor[0].x+=snake.speed;
if (snake.coor[0].x == 610)//撞墙后弹出gameover
{
gameover();
}//超出右边的边界
break;
case LEFT:
snake.coor[0].x-= snake.speed;
if (snake.coor[0].x <= 0)
{
gameover();
}//超出左边的边界
break;
case UP:
snake.coor[0].y-= snake.speed;
if (snake.coor[0].y <= 0)
{
gameover();
}//超出上边的边界
break;
case DOWN:
snake.coor[0].y+= snake.speed;
if (snake.coor[0].y ==610)
{
gameover();
}//超出下边的边界
break;
}
}
//碰到身体,gameover
void touchself()
{
for (int i = 1; i <= snake.size; i++)
{
if (snake.coor[i].x == snake.coor[0].x&& snake.coor[i].y == snake.coor[0].y)
gameover();
}
}
//通过按键移动蛇的移动方向
//72 80 75 77 上下左右键值
void keyControl()
{
//_kbhit()判断是否有按键,如果有按键就返回真。
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;
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;
}
}
}
//吃食物
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)
{
//Sleep(25);
//mciSendString("open zy.mp3 alias sBGM", 0, 0, 0);
//mciSendString("play sBGM close ", 0, 0, 0);
srand(GetTickCount());//获取系统开机到现在所经过的毫秒数
food.x = rand() % 500;
food.y = rand() % 500;//food不能超过窗口的边界
food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
food.r = rand() % 10 + 5;
food.flag = true;
}
}
//游戏暂停
void stop()
{
if (_kbhit())
{
if (_getch() == ' ')
{
while (1)
{
IMAGE pauseUI; //定义图像变量
initgraph(600, 600);
loadimage(&pauseUI, "pause.jpg", 600, 600);//将图片的宽拉到600,长拉到600像素点
putimage(0, 0, &pauseUI);
if (_getch() == ' ')
{
break;
}
}
}
}
}
int main()
{
printf("请在此框输入\n");
system("pause");
begin();
GameInit();
while (1)
{
snakeMove();
GameDraw();
keyControl();
EatFood();
Sleep(100);
touchself();
stop();
}
return 0;
}
图片和音频在二维码里面,复制粘贴就行,名字都一样