/////////////////////////////////////////////////////////////
//贪吃蛇
//PS:1._stprintf_s(s, _T("%d"), x);// >= vc6 的写法
// 2.加载音乐,要将音乐放在和.cpp文件同一个文件夹,加载图片也是一样
// 3.最好在游戏开始前就打开音乐,不然游戏时打开音乐会有延迟
#include <easyx.h>
#include <graphics.h>
#include <conio.h>
#include <stdlib.h>
#include <time.h>
#pragma comment(lib,"Winmm.lib")
#define W 40 //宽
#define H 40 //高
#define PIXEL 20 //像素大小
#define ms 200 //延时时间
int length;
typedef struct node {
int x;
int y;
int dir; //蛇运动方向,0123分别对应上下左右
COLORREF color;
struct node * next;
} * snakeBody;
int food[2]; //食物
snakeBody snake=(snakeBody)malloc(sizeof(struct node)); //玩家的蛇
void startup(); //数据初始化
void show(); //绘制图画
void updateWithoutInput(); //与用户输入无关的更新
void updateWithInput(); //与用户输入有关的更新
void timeDelay(); //精确延时
void addHeadNode(int dir); //增加头节点,增加节点的xy就是蛇头部下一步的位置(只有在蛇吃到食物后才会增加,能吃食物说明食物就在蛇头部下一步的位置)
void eatFood(char i); //吃食物
void change(char dir); //进行具体的移动,dir是蛇头节点的操作后移动方向
void gameover(); //游戏结束
void screenBeGray(); //让屏幕变灰
void ifStartNewGame(); //调用Windows Api函数Message Box,出现对话框询问是否开始新的一局
int main(void)
{
initgraph(W * PIXEL, H * PIXEL);
startup();
while (true)
{
srand((unsigned)time(NULL));
show();
updateWithoutInput();
updateWithInput();
timeDelay();
}
_getch();
EndBatchDraw();
closegraph();
}
void startup()
{
length = 1;
snake->x = 3;
snake->y = 21;
snake->dir = 3;
snake->color = RGB((rand() % 256), (rand() % 256), (rand() % 256));
snake->next = NULL;
while (1)
{
food[0] = rand() % 38 + 1;
food[1] = rand() % 38 + 1;
if ((food[0] != (snake->x)) || (food[1] != (snake->y)))
break;
}
//打开音乐
mciSendString(_T("open background.mid"), NULL, 0, NULL);
mciSendString(_T("open eatfood.mp3"), 0, NULL, 0);
mciSendString(_T("open died.mp3"), 0, NULL, 0);
//播放背景音乐
mciSendString(_T("play background.mid repeat"), NULL, 0, NULL);
}
void show()
{
int i, j;
TCHAR l[3];
snakeBody p=snake;
BeginBatchDraw();
//清空屏幕
setbkcolor(BLACK);
cleardevice();
//显示长度
settextcolor(WHITE);
settextstyle(50,30,_T("黑体"));
setbkmode(TRANSPARENT);
_stprintf_s(l, _T("%d"), length); // >= vc6 的写法
outtextxy(10, 20,_T("长度:"));
outtextxy(160, 20, l);
//绘制墙壁
setfillcolor(BLUE);
setlinecolor(BLUE - 100);
setlinestyle(PS_SOLID,2);
for(i=0;i<40;i++)
for (j = 0; j < 40; j++) {
if ((i == 0) || (i == 39)) {
rectangle(j*PIXEL+1, i*PIXEL+1, (j + 1)*PIXEL, (i + 1)*PIXEL);
solidrectangle(j*PIXEL+4, i*PIXEL+4, (j + 1)*PIXEL-4, (i + 1)*PIXEL-4);
}
if ((j == 0) || (j == 39)) {
rectangle(j*PIXEL + 1, i*PIXEL + 1, (j + 1)*PIXEL, (i + 1)*PIXEL);
solidrectangle(j*PIXEL + 4, i*PIXEL + 4, (j + 1)*PIXEL - 4, (i + 1)*PIXEL - 4);
}
}
//绘制食物
setfillcolor(RED);
solidcircle(PIXEL*food[0]+PIXEL/2, PIXEL*food[1] + PIXEL / 2,PIXEL/2);
//绘制小蛇
for (p; p != NULL; p = p->next) {
setfillcolor(p->color);
solidrectangle((p->x)*PIXEL + 2, (p->y)*PIXEL + 2, ((p->x) + 1)*PIXEL - 2, ((p->y) + 1)*PIXEL - 2);
}
FlushBatchDraw();
}
void updateWithoutInput()
{
}
void updateWithInput()
{
int t=0; //默认键盘输入无效
char input;
while (_kbhit()) {
input = _getch();
switch (input) {
case 'w':eatFood('w');
change('w');t = 1;
break;
case 'a':eatFood('a');
change('a');t = 1;
break;
case 's':eatFood('s');
change('s');t = 1;
break;
case 'd':eatFood('d');
change('d');t = 1;
break;
}
}
if (t == 0) {
eatFood(' ');
change(' ');
}
}
void timeDelay()
{
static clock_t oldclock = clock();// 静态变量,记录上一次 tick
oldclock += (clock_t)ms;// 更新 tick,ms为延时时间
if (clock() > oldclock)// 如果已经超时,无需延时
oldclock = clock();
else
while (clock() < oldclock)// 延时
Sleep(1);// 释放 CPU 控制权,降低 CPU 占用率
}
void addHeadNode(int dir)
{
//正确写法
snakeBody temp = (snakeBody)malloc(sizeof(struct node)); //新节点
temp->x = food[0];
temp->y = food[1];
temp->color = RGB((rand() % 256), (rand() % 256), (rand() % 256));
temp->dir = dir;
temp->next = snake;
snake = temp;
}
void eatFood(char i)
{
int nx, ny, dir;
int t = 0; //判断食物合法的标志
snakeBody p;
nx = snake->x; //保存蛇头现在的位置
ny = snake->y;
switch (i) {
case 'w':ny --; dir = 0; break;
case 'a':nx --; dir = 2; break; //更新为操作后蛇头的位置
case 's':ny ++; dir = 1; break;
case 'd':nx ++; dir = 3; break;
case ' ':switch (snake->dir) {
case 0:ny --; dir = 0; break;
case 1:ny ++; dir = 1; break; //如果没有输入,根据蛇头的方向判断下一步位置
case 2:nx --; dir = 2; break;
case 3:nx ++; dir = 3; break;
}
break;
}
if ((nx == food[0]) && (ny == food[1])) {
mciSendString(_T("play eatfood.mp3 from 0"), 0, NULL, 0);
addHeadNode(dir);
p = snake;
while (t==0)
{
t = 1;
food[0] = rand() % 38 + 1;
food[1] = rand() % 38 + 1;
for (p; p != NULL; p = p->next)
if ((food[0] == p->x) && (food[1] == p->y)) //如果食物产生的位置在蛇身上,t=0表示食物位置不合法,继续循环直到合法
{
t = 0;
break;
}
}
length++;
}
}
void change(char dir)//进行具体的移动,dir是蛇头节点的操作后移动方向
{
int t = 0;
struct node pre=*snake;
struct node now;
snakeBody p = snake->next;
snakeBody q = snake->next;
switch (dir) {
case 'w': if ((snake->dir) != 1)
{
(snake->y)--; (snake->dir) = 0; t = 1;
} break;
case 'a': if ((snake->dir) != 3)
{
(snake->x)--; (snake->dir) = 2; t = 1;
} break;
case 's': if ((snake->dir) != 0)
{
(snake->y)++; (snake->dir) = 1; t = 1;
} break;
case 'd': if ((snake->dir) != 2)
{
(snake->x)++; (snake->dir) = 3; t = 1;
} break;
}
if(t==0)
switch (snake->dir) {
case 0:(snake->y)--; break;
case 1:(snake->y)++; break;
case 2:(snake->x)--; break;
case 3:(snake->x)++; break;
}
//判断会不会撞墙
if (((snake->x) == 0) || ((snake->x) == 39) || ((snake->y) == 0) || ((snake->y) == 39))
gameover();
//判断会不会撞自己
for (p; p != NULL; p = p->next)
if (((snake->x) == (p->x)) && ((snake->y) == (p->y)))
gameover();
//移动蛇身
for (q; q != NULL; q = q->next) {
now = *q;
q->x = pre.x;
q->y = pre.y;
q->dir = pre.dir;
pre = now;
}
}
void gameover()
{
screenBeGray(); //屏幕变灰
EndBatchDraw(); //结束批量绘图
mciSendString(_T("stop background.mid"), NULL, 0, NULL);
mciSendString(_T("close background.mid"), NULL, 0, NULL);
mciSendString(_T("play died.mp3"), 0, NULL, 0);
setcolor(RGB((rand() % 256), (rand() % 256), (rand() % 256)));
settextstyle(200, 0, _T("宋体"));
outtextxy((W/2)*PIXEL-200,(H/2)*PIXEL-200, _T("失败"));
Sleep(3000);
ifStartNewGame(); //询问是否开始下一场游戏
}
void screenBeGray()
{
int i, j, c;
float h, s, l;
for(i=0;i<H*PIXEL;i++)
for (j = 0; j < W*PIXEL; j++) {
c = getpixel(j,i);
RGBtoHSL(c,&h,&s,&l);
s = 0;
putpixel(j,i,HSLtoRGB(h,s,l));
}
}
void ifStartNewGame()
{
mciSendString(_T("stop eatfood.mp3"), NULL, 0, NULL);
mciSendString(_T("close eatfood.mp3"), NULL, 0, NULL);
mciSendString(_T("stop died.mp3"), NULL, 0, NULL);
mciSendString(_T("close died.mp3"), NULL, 0, NULL);
//开始下一局之前,一定要将所有音乐关闭
HWND wnd = GetHWnd();
if (MessageBox(wnd, _T("游戏结束。\n重来一局吗?"), _T("询问"), MB_YESNO | MB_ICONQUESTION) == IDYES)
{
//停止播放并关闭音乐
main();
}
else
exit(0);
}