• 记录+存个档♪(´▽`)
• 慢慢写了一周多,今天终于查完最后一个bug了(芜湖~)
• 参考了《C语言项目开发全程实录》这本书,写的很好
• 加了一些乱七八糟的元素进去(比如按键+光标控制)
• 暴力拼了很多字符画
• 至于名字为什么要叫Ram and Snake…我也不造(
整体思路
1.贯穿整个源代码的绘图及光标函数
这几个函数主要是改变颜色,控制打印位置,隐藏/显示光标。用到了很多windows头文件下的东西,了解了句柄和一些其他的知识。
/*隐藏光标函数*/
void HideCursor()
{
CONSOLE_CURSOR_INFO cursor;
cursor.bVisible = FALSE;
cursor.dwSize = sizeof(cursor);
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorInfo(handle, &cursor);
}
/*显示光标函数*/
void ShowCursor()
{
CONSOLE_CURSOR_INFO cursor;
cursor.bVisible = TRUE;
cursor.dwSize = sizeof(cursor);
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorInfo(handle, &cursor);
}
/*控制光标位置(方便指定打印位置)*/
void gotoxy(int x, int y)
{
COORD c;
c.X = x;
c.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c);
}
/*改变字体颜色(具体颜色请参照十进制颜色对照表)*/
int changecolor(int a)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), a);
return 0;
}
2.主菜单
菜单部分好像就是一直在拼字符画和处理一些琐碎的按键部分。按键主要是用一个按键状态判断函数和通过键码+ascii码判断两种方法,后一种就差不多可以实现光标在选项之间跳动,比较符合我对像素游戏的想象。上代码👇
/*游戏标题(开始界面)*/
void startmenu_gametitle()
{
changecolor(11);
printf(" \n");
printf(" @ @@@@@@@ \n");
printf(" @@@@@@@@@@@@ \n");
printf(" @@@ @@@@@ \n");
printf(" @@@ @@@@ \n");
printf(" @@@ @@@@ @@@@@@@ @ @ @@@@@ @@@@@@ \n");
printf(" @@@@@@@@@@ @ @@@@@@@@@@ @@@@@@@ @ @@@@@@@ @@@ \n");
printf(" @@@@ @@@@ @@@ @@@ @@@ @@@@ @@@@ @ @ \n");
printf(" @@@ @@@@ @@@ @@@@ @@@ @@@ @@@ @@@ @ \n");
printf(" @@@ @@@@ @@@ @@@@ @@@ @@@ @@@ @ @@ \n");
printf(" @@@ @@@@ @@@@ @@ @@ @@@ @@@ @@@ @@ @ @@ \n");
printf(" @@@@@ @@@@@ @@@@@@@ @@@@ @@@ @@@@@ @@@@ @@@@ @@@\n");
gotoxy(snake, 2);
changecolor(14);
printf(" @ @@@@@@ \n");
gotoxy(snake, 3);
printf(" @ @ \n");
gotoxy(snake, 4);
printf(" @ @ \n");
gotoxy(snake, 5);
printf(" @ @@@@ ## @ \n");
gotoxy(snake, 6);
printf(" @ @@ @ @ \n");
gotoxy(snake, 7);
printf(" @ @@ @@@@ \n");
gotoxy(snake, 8);
printf(" @ @@ \n");
gotoxy(snake, 9);
printf(" @ @@ \n");
gotoxy(snake, 10);
printf(" @ @@ \n");
gotoxy(snake, 11);
printf(" @@@@@@ @ @@ \n");
gotoxy(snake, 12);
printf(" @ @@@ @ @@ \n");
gotoxy(snake, 13);
printf("@ @@ @ @ @@@@@@ @ @ @ @@@@@\n");
gotoxy(snake, 14);
printf("@ @@@@@ @ @ @ @ @ @ @ @ @ \n");
gotoxy(snake, 15);
printf(" @ @@ @ @ @@@@@ @@ @@@@ \n");
gotoxy(snake, 16);
printf(" @ @@ @ @ @ @ @ @ @ \n");
gotoxy(snake, 17);
printf(" @ @@@@@@@@@ @ @@ @@ @ @@@@@\n");
}
/*开始界面图画和其他文字的部分*/
void startmenu_picture()
{
gotoxy(3, 13);
changecolor(11);
printf("Ram和他的贪吃蛇的故事......");
}
/*菜单选择项*/
void startmenu_options()
{
changecolor(9);
gotoxy(5, 15);
printf("◆Start Game\n");
gotoxy(5, 17);
printf("◆Instruction\n");
gotoxy(5, 19);
printf("◆Highest Score\n");
gotoxy(5, 21);
printf("◆Exit Game\n");
gotoxy(5, 15); //光标起始位置
startmenu_choices(15);
}
/*主界面上检测按键状态以进入不同的选项中*/
void startmenu_choices(int a) //a为行坐标(15,17,19,21),注意开始时光标本来就是在startgame前面的
{
int ch = getch();
//控制光标的移动
while (ch != 0x0d || ch == 0xE0) //如果按下回车,则跳出循环
//0x0d表示回车,0XE0表示上下左右等键的键码
{
//再判断是哪个键
ch = getch(); //这句千万不要掉了!!
if (ch == 0x48 && a > 15)
{
a -= 2;
gotoxy(5, a);
}
if (ch == 0x50 && a < 21)
{
a += 2;
gotoxy(5, a);
}
}
switch (a)
{
case 15: startourgame(); break;
case 17: instruction(); break;
case 19: highestscore(); break;
case 21: gotoxy(2, 25); exit(0); break;
}
}
/*主菜单函数的合体*/
void startmenu()
{
ShowCursor();
flag = 0;
char chCmd[32]; //这三行用于控制 主菜单 窗口大小
sprintf_s(chCmd, "mode con cols=%d lines=%d", 117, 31);
system(chCmd);
read_file(); //读取最高分
startmenu_gametitle();
startmenu_picture();
startmenu_options();
}
3.游戏主体
这一块就是贪吃蛇内部的逻辑了。贪吃蛇的身体是由链表构成的,而要在屏幕上看到运动的贪吃蛇,就需要在对应的位置打印对应的图案,所以,链表节点的数据域就变成了点的坐标(x,y)。对于显示贪吃蛇的位置,因为每次移动一个距离,只有吃到食物和吃不到食物两种情况,所以有了两种情况下的判断,如果吃到食物,那么在食物的位置打印蛇头,如果没有食物,则使尾节点消失,头节点的坐标随上下左右键的改变而改变,明显除了头节点和尾节点,其他节点的坐标都是不变的。
而生成食物部分,主要用了一个随机数的生成,且食物本身也是一个指针。
/*游戏主体部分整合*/
void startourgame()
{
createmap();
initsnake();
Sleep(100);
createfood();
keystatus();
write_file();
score = 0;
}
/*加载地图*/
void createmap()
{
HideCursor();
system("cls");
char chCmd[32]; //这三行用于控制 主菜单 窗口大小
sprintf_s(chCmd, "mode con cols=%d lines=%d", 51, 26);
system(chCmd);
int i, j;
changecolor(5);
for (i = 0; i < 49; i += 2) //上下两个边框
{
gotoxy(i, 0);
printf("〓");
gotoxy(i, 24);
printf("〓");
}
for (i = 1; i < 25; i++) //左右边框
{
gotoxy(0, i);
printf("〓");
gotoxy(48, i);
printf("〓");
}
changecolor(11);
for (i = 1; i < 24; i++) //控制行数
{
for (j = 2; j < 48; j += 2)
{
gotoxy(j, i);
printf("■");
}
printf("\n");
}
printf("\n");
}
/*初始化蛇身*/
void initsnake()
{
pHead = (PSNAKE)malloc(sizeof(SNAKE)); //头节点
PSNAKE pTail = (PSNAKE)malloc(sizeof(SNAKE)); //尾节点
pTail = pHead;
if (pTail != NULL && pHead != NULL)
{
pHead->Next = NULL;
int i, val = initx;
for (i = 0; i < 4; i++)
{
PSNAKE isnake = (PSNAKE)malloc(sizeof(SNAKE));
if (isnake != NULL)
{
isnake->x = val; //横向摆放初始状态的蛇
isnake->y = inity;
val -= 2;
pTail->Next = isnake;
isnake->Next = NULL;
pTail = isnake;
}
}
PSNAKE p = pHead->Next;
gotoxy(p->x, p->y);
changecolor(5);
printf("●");
p = p->Next;
while (p != NULL) //在地图的初始位置输出初始化的蛇
{
changecolor(5);
gotoxy(p->x, p->y);
printf("◆");
p = p->Next;
}
}
}
/*在地图上随机产生食物,在蛇头吃到食物后再次执行该函数*/
void createfood()
{
srand((unsigned)time(NULL));
PSNAKE ffood = (PSNAKE)malloc(sizeof(SNAKE));
food = (PSNAKE)malloc(sizeof(SNAKE));
if (ffood != NULL && food != NULL)
{
int we = rand() % 46 + 2;
while (we % 2 == 1) //2<= x <=46,且列坐标必须为偶数(食物和格子都占两个字符)
we = rand() % 46 + 2;
ffood->x = we;
ffood->y = rand() % 23 + 1; //1<= y <=23
PSNAKE p = (PSNAKE)malloc(sizeof(SNAKE));
p = pHead->Next;
//在随机出现食物的时候,食物不能与蛇的身体重合
while (p != NULL)
{
if (p->x == ffood->x && p->y == ffood->y) //若重合,则释放该食物空间并重新生成一个食物
{
free(ffood);
ffood = NULL;
createfood();
}
p = p->Next;
}
// 如果未重合,则说明该食物生成成功,并将食物打印在地图上
food = ffood;
changecolor(4);
gotoxy(food->x, food->y);
printf("●");
changecolor(5);
}
}
/*检测游戏过程中各个键的状态*/
void keystatus()
{
sta = 4; //初始蛇向右走
while (1) //死循环,能保证按键按下的时候进行判断并进入movesnake函数
{
if (GetAsyncKeyState(VK_UP) && sta != 2)
sta = 1;
else
{
if (GetAsyncKeyState(VK_DOWN) && sta != 1)
sta = 2;
else
{
if (GetAsyncKeyState(VK_LEFT) && sta != 4)
sta = 3;
else
{
if ((GetAsyncKeyState(VK_RIGHT) && sta != 3))
sta = 4;
}
}
}
if (GetAsyncKeyState(VK_SPACE)) //按空格键暂停
{
while (1)
{
Sleep(300);
if (GetAsyncKeyState(VK_SPACE)) //再次检测到空格键的时候就恢复原游戏状态
break;
}
}
if (GetAsyncKeyState(VK_ESCAPE)) //按esc键直接从游戏时的状态退回到主菜单界面
{
system("cls");
startmenu();
break; //检测到退出->退出该死循环
}
if (movesnake() == false)
{
gameover();
break;
}
}
}
/*上下左右键控制蛇的运动(注意蛇的身体是由两个字符长度的字符组成的,坐标都是以左为基准)*/
bool movesnake()
{
HideCursor();
PSNAKE qq = (PSNAKE)malloc(sizeof(SNAKE)); //移动过程等效于产生一个新节点(坐标在原来蛇头坐标基础上变化)
PSNAKE pp = (PSNAKE)malloc(sizeof(SNAKE)); //并同时去掉尾节点(新节点使用头插法)
pp = pHead->Next; //pp暂存蛇头的地址
if (sta == 1) //向上走
{
qq->x = pp->x;
qq->y = pp->y - 1; //行坐标减一
qq->Next = pp; //原来的蛇头成为第二个节点
pHead->Next = qq; //头节点存这个新开节点的地址,新节点成为蛇头
PSNAKE a = (PSNAKE)malloc(sizeof(SNAKE));
a = pHead->Next; //a为蛇头
if (cannotbiteyourself() == 1 || cannotcrossthewall() == 1) //如果撞了墙或撞到自己
return false;
else
{
if (a->x == food->x && a->y == food->y)//如果蛇向上走了一步,且这一步遇到了食物
{
//尾节点仍然是 ◆
gotoxy(a->x, a->y);
printf("●");
a = a->Next;
while (a != NULL)
{
gotoxy(a->x, a->y);
printf("◆");
a = a->Next;
}
Sleep(200); //减慢打印时间用的(同时也是减慢蛇的速度用的)
createfood();
score += 5;
}
else //如果没有碰到食物,打印尾节点时变成 ■
{
gotoxy(a->x, a->y);
printf("●");
a = a->Next;
while (a->Next->Next != NULL)
{ //最后那a是倒数第二个节点的地址
gotoxy(a->x, a->y);
printf("◆");
a = a->Next;
}
gotoxy(a->x, a->y);
printf("◆");
PSNAKE b = (PSNAKE)malloc(sizeof(SNAKE));
b = a->Next; //b存最后一个节点的地址
a->Next = NULL; //a的指针域为空,倒数第二个节点成为尾节点
changecolor(11);
gotoxy(b->x, b->y);
printf("■");
free(b);
b = NULL;
changecolor(5);
Sleep(200);
}
return true;
}
}
if (sta == 2) //向下走
{
qq->x = pp->x;
qq->y = pp->y + 1;
qq->Next = pp;
pHead->Next = qq;
PSNAKE a = (PSNAKE)malloc(sizeof(SNAKE));
a = pHead->Next;
if (cannotbiteyourself() == 1 || cannotcrossthewall() == 1)
return false;
else
{
if (a->x == food->x && a->y == food->y)
{
gotoxy(a->x, a->y);
printf("●");
a = a->Next;
while (a != NULL)
{
gotoxy(a->x, a->y);
printf("◆");
a = a->Next;
}
Sleep(200);
createfood();
score += 5;
}
else
{
gotoxy(a->x, a->y);
printf("●");
a = a->Next;
while (a->Next->Next != NULL)
{
gotoxy(a->x, a->y);
printf("◆");
a = a->Next;
}
gotoxy(a->x, a->y);
printf("◆");
PSNAKE b = (PSNAKE)malloc(sizeof(SNAKE));
b = a->Next;
a->Next = NULL;
changecolor(11);
gotoxy(b->x, b->y);
printf("■");
free(b);
b = NULL;
Sleep(200);
changecolor(5);
}
return true;
}
}
if (sta == 3) //向左走
{
qq->x = pp->x - 2;
qq->y = pp->y;
qq->Next = pp;
pHead->Next = qq;
PSNAKE a = (PSNAKE)malloc(sizeof(SNAKE));
a = pHead->Next;
if (cannotbiteyourself() == 1 || cannotcrossthewall() == 1)
return false;
else
{
if (a->x == food->x && a->y == food->y)
{
gotoxy(a->x, a->y);
printf("●");
a = a->Next;
while (a != NULL)
{
gotoxy(a->x, a->y);
printf("◆");
a = a->Next;
}
Sleep(200);
createfood();
score += 5;
}
else
{
gotoxy(a->x, a->y);
printf("●");
a = a->Next;
while (a->Next->Next != NULL)
{
gotoxy(a->x, a->y);
printf("◆");
a = a->Next;
}
gotoxy(a->x, a->y);
printf("◆");
PSNAKE b = (PSNAKE)malloc(sizeof(SNAKE));
b = a->Next;
a->Next = NULL;
changecolor(11);
gotoxy(b->x, b->y);
printf("■");
free(b);
b = NULL;
Sleep(200);
changecolor(5);
}
return true;
}
}
if (sta == 4) //向右
{
qq->x = pp->x + 2;
qq->y = pp->y;
qq->Next = pp;
pHead->Next = qq;
PSNAKE a = (PSNAKE)malloc(sizeof(SNAKE));
a = pHead->Next;
if (cannotbiteyourself() == 1 || cannotcrossthewall() == 1)
return false;
else
{
if (a->x == food->x && a->y == food->y)
{
gotoxy(a->x, a->y);
printf("●");
a = a->Next;
while (a != NULL)
{
gotoxy(a->x, a->y);
printf("◆");
a = a->Next;
}
Sleep(200);
createfood();
score += 5;
}
else
{
gotoxy(a->x, a->y);
printf("●");
a = a->Next;
while (a->Next->Next != NULL)
{
gotoxy(a->x, a->y);
printf("◆");
a = a->Next;
}
gotoxy(a->x, a->y);
printf("◆");
PSNAKE b = (PSNAKE)malloc(sizeof(SNAKE));
b = a->Next;
a->Next = NULL;
changecolor(11);
gotoxy(b->x, b->y);
printf("■");
free(b);
b = NULL;
Sleep(200);
changecolor(5);
}
return true;
}
}
}
/*不能撞墙*/
int cannotcrossthewall()
{
if (pHead->Next->x == 0 || pHead->Next->x == 50 || pHead->Next->y == 0 || pHead->Next->y == 26)
flag = 1;
return flag;
}
/*不能撞到自己*/
int cannotbiteyourself()
{
PSNAKE bite = (PSNAKE)malloc(sizeof(SNAKE));
bite = pHead->Next->Next; //bite从第二个节点开始判断
while (bite != NULL)
{
if (bite->x == pHead->Next->x && bite->y == pHead->Next->y)
flag = 1;
bite = bite->Next;
}
return flag;
}
/*游戏结束画面*/
void gameover()
{
if (score > HighScore)
write_file();
system("cls");
changecolor(11);
char chCmd[32];
sprintf_s(chCmd, "mode con cols=%d lines=%d", 60, 25);
system(chCmd);
gotoxy(5,5);
printf(" /\\________/\\\n");
gotoxy(5,6);
printf(" | Q Q |\n");
gotoxy(5,7);
printf(" | ⌒ | ---你死的好惨啊呜呜呜(;′⌒`) \n");
gotoxy(5,8);
printf(" | |\n");
gotoxy(5,9);
printf(" \\____________/ \n");
gotoxy(5,10);
printf(" \n");
gotoxy(5,11);
changecolor(9);
printf(" Your score is %d \n", score);
gotoxy(5,12);
printf("Highest score : %d \n", HighScore);
gotoxy(1,15);
printf("Press space back to the menu......");
gotoxy(35,15);
getchar();
system("cls");
startmenu();
}
4.最高分
这一块应该没什么好说的,主要是文件的读写。
/*读取最高分*/
void read_file()
{
FILE* fp;
fp = fopen("saveeeee.txt", "a+"); //打开文件save.txt
fscanf(fp, "%d", &HighScore); //把文件中的最高分读出来
fclose(fp); //关闭文件
}
/*记录最高分*/
void write_file()
{
FILE* fp;
fp = fopen("saveeeee.txt", "w+"); //以读写的方式建立一个名为save.txt的文件
fprintf(fp, "%d", score); //把分数写进文件中
fclose(fp);
}
/*查看最高分*/
void highestscore()
{
system("cls");
char chCmd[32];
sprintf_s(chCmd, "mode con cols=%d lines=%d", 50, 25);
system(chCmd);
changecolor(14);
gotoxy(5, 5);
printf("Your highest score is %d.\n\n", HighScore);
gotoxy(1, 9);
printf("Press space back to the menu......");
gotoxy(35, 9);
int cha = getch();
while (cha == 0x0d)
{
system("cls");
startmenu();
}
}
工程代码
1.头文件(snakeeee.h)
#pragma warning (disable: 6011)
#pragma warning(disable : 4996)
#include<stdio.h>
#include<windows.h>
#include<conio.h>
#include<time.h>
#include<stdlib.h>
/*******宏定义*******/
#define snake 64 //标题的snake的横坐标
#define inity 11 //初始时蛇头的X坐标
#define initx 24 //初始时蛇头的y坐标
/*贪吃蛇的各种参数*/
typedef struct Snake
{
int x; //列坐标
int y; //行坐标
struct Snake* Next; //指向下一个身体节点的指针
}SNAKE, * PSNAKE;
int sta = 0; //用于检测按键状态
int score = 0; //得分(每吃一个食物得5分)
PSNAKE pHead; //蛇链表的头指针
PSNAKE food; //食物的指针
int flag = 0; //用于指示是否撞墙或撞到自己
int HighScore = 0; //存储最高分
/******函数声明******/
void HideCursor();
void ShowCursor();
void gotoxy(int x, int y);
int changecolor(int a);
void startmenu_gametitle();
void startmenu_picture();
void startmenu_options();
void startmenu_choices(int a);
void instruction();
void startmenu();
void createmap();
void initsnake();
void createfood();
void keystatus();
bool movesnake();
void startourgame();
int cannotcrossthewall();
int cannotbiteyourself();
void gameover();
void write_file();
void read_file();
void highestscore();
2.源代码
#include"snakeeee.h"
int main()
{
startmenu();
return 0;
}
/*隐藏光标函数*/
void HideCursor()
{
CONSOLE_CURSOR_INFO cursor;
cursor.bVisible = FALSE;
cursor.dwSize = sizeof(cursor);
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorInfo(handle, &cursor);
}
/*显示光标函数*/
void ShowCursor()
{
CONSOLE_CURSOR_INFO cursor;
cursor.bVisible = TRUE;
cursor.dwSize = sizeof(cursor);
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorInfo(handle, &cursor);
}
/*控制光标位置(方便指定打印位置)*/
void gotoxy(int x, int y)
{
COORD c;
c.X = x;
c.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c);
}
/*改变字体颜色(具体颜色请参照十进制颜色对照表)*/
int changecolor(int a)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), a);
return 0;
}
/*主界面上检测按键状态以进入不同的选项中*/
void startmenu_choices(int a) //a为行坐标(15,17,19,21),注意开始时光标本来就是在startgame前面的
{
int ch = getch();
//控制光标的移动
while (ch != 0x0d || ch == 0xE0) //如果按下回车,则跳出循环
//0x0d表示回车,0XE0表示上下左右等键的键码
{
//再判断是哪个键
ch = getch(); //这句千万不要掉了!!
if (ch == 0x48 && a > 15)
{
a -= 2;
gotoxy(5, a);
}
if (ch == 0x50 && a < 21)
{
a += 2;
gotoxy(5, a);
}
}
switch (a)
{
case 15: startourgame(); break;
case 17: instruction(); break;
case 19: highestscore(); break;
case 21: gotoxy(2, 25); exit(0); break;
}
}
/*游戏标题(开始界面)*/
void startmenu_gametitle()
{
changecolor(11);
printf(" \n");
printf(" @ @@@@@@@ \n");
printf(" @@@@@@@@@@@@ \n");
printf(" @@@ @@@@@ \n");
printf(" @@@ @@@@ \n");
printf(" @@@ @@@@ @@@@@@@ @ @ @@@@@ @@@@@@ \n");
printf(" @@@@@@@@@@ @ @@@@@@@@@@ @@@@@@@ @ @@@@@@@ @@@ \n");
printf(" @@@@ @@@@ @@@ @@@ @@@ @@@@ @@@@ @ @ \n");
printf(" @@@ @@@@ @@@ @@@@ @@@ @@@ @@@ @@@ @ \n");
printf(" @@@ @@@@ @@@ @@@@ @@@ @@@ @@@ @ @@ \n");
printf(" @@@ @@@@ @@@@ @@ @@ @@@ @@@ @@@ @@ @ @@ \n");
printf(" @@@@@ @@@@@ @@@@@@@ @@@@ @@@ @@@@@ @@@@ @@@@ @@@\n");
gotoxy(snake, 2);
changecolor(14);
printf(" @ @@@@@@ \n");
gotoxy(snake, 3);
printf(" @ @ \n");
gotoxy(snake, 4);
printf(" @ @ \n");
gotoxy(snake, 5);
printf(" @ @@@@ ## @ \n");
gotoxy(snake, 6);
printf(" @ @@ @ @ \n");
gotoxy(snake, 7);
printf(" @ @@ @@@@ \n");
gotoxy(snake, 8);
printf(" @ @@ \n");
gotoxy(snake, 9);
printf(" @ @@ \n");
gotoxy(snake, 10);
printf(" @ @@ \n");
gotoxy(snake, 11);
printf(" @@@@@@ @ @@ \n");
gotoxy(snake, 12);
printf(" @ @@@ @ @@ \n");
gotoxy(snake, 13);
printf("@ @@ @ @ @@@@@@ @ @ @ @@@@@\n");
gotoxy(snake, 14);
printf("@ @@@@@ @ @ @ @ @ @ @ @ @ \n");
gotoxy(snake, 15);
printf(" @ @@ @ @ @@@@@ @@ @@@@ \n");
gotoxy(snake, 16);
printf(" @ @@ @ @ @ @ @ @ @ \n");
gotoxy(snake, 17);
printf(" @ @@@@@@@@@ @ @@ @@ @ @@@@@\n");
}
/*开始界面图画和其他文字的部分*/
void startmenu_picture()
{
gotoxy(3, 13);
changecolor(11);
printf("Ram和他的贪吃蛇的故事......");
}
/*菜单选择项*/
void startmenu_options()
{
changecolor(9);
gotoxy(5, 15);
printf("◆Start Game\n");
gotoxy(5, 17);
printf("◆Instruction\n");
gotoxy(5, 19);
printf("◆Highest Score\n");
gotoxy(5, 21);
printf("◆Exit Game\n");
gotoxy(5, 15); //光标起始位置
startmenu_choices(15);
}
/*主菜单函数的合体*/
void startmenu()
{
ShowCursor();
flag = 0;
char chCmd[32]; //这三行用于控制 主菜单 窗口大小
sprintf_s(chCmd, "mode con cols=%d lines=%d", 117, 31);
system(chCmd);
read_file(); //读取最高分
startmenu_gametitle();
startmenu_picture();
startmenu_options();
}
/*游戏说明及规则*/
void instruction()
{
system("cls");
changecolor(3);
printf("----------------------Hi,Snake!~o(*= ▽=*)ブ----------------------\n"); //上边框
gotoxy(0, 17);
changecolor(3);
printf("------------------------------------------------------------------\n"); //下边框
gotoxy(2, 2);
changecolor(14);
printf("◇ 操作说明 ◇\n");
gotoxy(2, 4);
changecolor(3);
printf("※按↑↓←→键分别控制贪吃蛇向上、下、左、右移动\n");
gotoxy(2, 6);
printf("※按空格键暂停游戏\n");
gotoxy(2, 8);
printf("※按ESC键于游戏中途退出游戏\n");
changecolor(14);
gotoxy(2, 10);
printf("◇ 游戏规则 ◇\n");
gotoxy(2, 12);
changecolor(3);
printf("※肥肠简单,只要不撞到自己或撞到墙就行了(笑)\n");
//增加一个按回车键返回主菜单
gotoxy(1, 16);
printf("Press space back to the menu......");
gotoxy(35, 16);
int cha = getch();
while (cha == 0x0d)
{
system("cls");
startmenu();
}
}
/*游戏主体部分整合*/
void startourgame()
{
createmap();
initsnake();
Sleep(100);
createfood();
keystatus();
write_file();
score = 0;
}
/*加载地图*/
void createmap()
{
HideCursor();
system("cls");
char chCmd[32]; //这三行用于控制 主菜单 窗口大小
sprintf_s(chCmd, "mode con cols=%d lines=%d", 51, 26);
system(chCmd);
int i, j;
changecolor(5);
for (i = 0; i < 49; i += 2) //上下两个边框
{
gotoxy(i, 0);
printf("〓");
gotoxy(i, 24);
printf("〓");
}
for (i = 1; i < 25; i++) //左右边框
{
gotoxy(0, i);
printf("〓");
gotoxy(48, i);
printf("〓");
}
changecolor(11);
for (i = 1; i < 24; i++) //控制行数
{
for (j = 2; j < 48; j += 2)
{
gotoxy(j, i);
printf("■");
}
printf("\n");
}
printf("\n");
}
/*初始化蛇身*/
void initsnake()
{
pHead = (PSNAKE)malloc(sizeof(SNAKE)); //头节点
PSNAKE pTail = (PSNAKE)malloc(sizeof(SNAKE)); //尾节点
pTail = pHead;
if (pTail != NULL && pHead != NULL)
{
pHead->Next = NULL;
int i, val = initx;
for (i = 0; i < 4; i++)
{
PSNAKE isnake = (PSNAKE)malloc(sizeof(SNAKE));
if (isnake != NULL)
{
isnake->x = val; //横向摆放初始状态的蛇
isnake->y = inity;
val -= 2;
pTail->Next = isnake;
isnake->Next = NULL;
pTail = isnake;
}
}
PSNAKE p = pHead->Next;
gotoxy(p->x, p->y);
changecolor(5);
printf("●");
p = p->Next;
while (p != NULL) //在地图的初始位置输出初始化的蛇
{
changecolor(5);
gotoxy(p->x, p->y);
printf("◆");
p = p->Next;
}
}
}
/*在地图上随机产生食物,在蛇头吃到食物后再次执行该函数*/
void createfood()
{
srand((unsigned)time(NULL));
PSNAKE ffood = (PSNAKE)malloc(sizeof(SNAKE));
food = (PSNAKE)malloc(sizeof(SNAKE));
if (ffood != NULL && food != NULL)
{
int we = rand() % 46 + 2;
while (we % 2 == 1) //2<= x <=46,且列坐标必须为偶数(食物和格子都占两个字符)
we = rand() % 46 + 2;
ffood->x = we;
ffood->y = rand() % 23 + 1; //1<= y <=23
PSNAKE p = (PSNAKE)malloc(sizeof(SNAKE));
p = pHead->Next;
//在随机出现食物的时候,食物不能与蛇的身体重合
while (p != NULL)
{
if (p->x == ffood->x && p->y == ffood->y) //若重合,则释放该食物空间并重新生成一个食物
{
free(ffood);
ffood = NULL;
createfood();
}
p = p->Next;
}
// 如果未重合,则说明该食物生成成功,并将食物打印在地图上
food = ffood;
changecolor(4);
gotoxy(food->x, food->y);
printf("●");
changecolor(5);
}
}
/*检测游戏过程中各个键的状态*/
void keystatus()
{
sta = 4; //初始蛇向右走
while (1) //死循环,能保证按键按下的时候进行判断并进入movesnake函数
{
if (GetAsyncKeyState(VK_UP) && sta != 2)
sta = 1;
else
{
if (GetAsyncKeyState(VK_DOWN) && sta != 1)
sta = 2;
else
{
if (GetAsyncKeyState(VK_LEFT) && sta != 4)
sta = 3;
else
{
if ((GetAsyncKeyState(VK_RIGHT) && sta != 3))
sta = 4;
}
}
}
if (GetAsyncKeyState(VK_SPACE)) //按空格键暂停
{
while (1)
{
Sleep(300);
if (GetAsyncKeyState(VK_SPACE)) //再次检测到空格键的时候就恢复原游戏状态
break;
}
}
if (GetAsyncKeyState(VK_ESCAPE)) //按esc键直接从游戏时的状态退回到主菜单界面
{
system("cls");
startmenu();
break; //检测到退出->退出该死循环
}
if (movesnake() == false)
{
gameover();
break;
}
}
}
/*上下左右键控制蛇的运动(注意蛇的身体是由两个字符长度的字符组成的,坐标都是以左为基准)*/
bool movesnake()
{
HideCursor();
PSNAKE qq = (PSNAKE)malloc(sizeof(SNAKE)); //移动过程等效于产生一个新节点(坐标在原来蛇头坐标基础上变化)
PSNAKE pp = (PSNAKE)malloc(sizeof(SNAKE)); //并同时去掉尾节点(新节点使用头插法)
pp = pHead->Next; //pp暂存蛇头的地址
if (sta == 1) //向上走
{
qq->x = pp->x;
qq->y = pp->y - 1; //行坐标减一
qq->Next = pp; //原来的蛇头成为第二个节点
pHead->Next = qq; //头节点存这个新开节点的地址,新节点成为蛇头
PSNAKE a = (PSNAKE)malloc(sizeof(SNAKE));
a = pHead->Next; //a为蛇头
if (cannotbiteyourself() == 1 || cannotcrossthewall() == 1) //如果撞了墙或撞到自己
return false;
else
{
if (a->x == food->x && a->y == food->y)//如果蛇向上走了一步,且这一步遇到了食物
{
//尾节点仍然是 ◆
gotoxy(a->x, a->y);
printf("●");
a = a->Next;
while (a != NULL)
{
gotoxy(a->x, a->y);
printf("◆");
a = a->Next;
}
Sleep(200); //减慢打印时间用的(同时也是减慢蛇的速度用的)
createfood();
score += 5;
}
else //如果没有碰到食物,打印尾节点时变成 ■
{
gotoxy(a->x, a->y);
printf("●");
a = a->Next;
while (a->Next->Next != NULL)
{ //最后那a是倒数第二个节点的地址
gotoxy(a->x, a->y);
printf("◆");
a = a->Next;
}
gotoxy(a->x, a->y);
printf("◆");
PSNAKE b = (PSNAKE)malloc(sizeof(SNAKE));
b = a->Next; //b存最后一个节点的地址
a->Next = NULL; //a的指针域为空,倒数第二个节点成为尾节点
changecolor(11);
gotoxy(b->x, b->y);
printf("■");
free(b);
b = NULL;
changecolor(5);
Sleep(200);
}
return true;
}
}
if (sta == 2) //向下走
{
qq->x = pp->x;
qq->y = pp->y + 1;
qq->Next = pp;
pHead->Next = qq;
PSNAKE a = (PSNAKE)malloc(sizeof(SNAKE));
a = pHead->Next;
if (cannotbiteyourself() == 1 || cannotcrossthewall() == 1)
return false;
else
{
if (a->x == food->x && a->y == food->y)
{
gotoxy(a->x, a->y);
printf("●");
a = a->Next;
while (a != NULL)
{
gotoxy(a->x, a->y);
printf("◆");
a = a->Next;
}
Sleep(200);
createfood();
score += 5;
}
else
{
gotoxy(a->x, a->y);
printf("●");
a = a->Next;
while (a->Next->Next != NULL)
{
gotoxy(a->x, a->y);
printf("◆");
a = a->Next;
}
gotoxy(a->x, a->y);
printf("◆");
PSNAKE b = (PSNAKE)malloc(sizeof(SNAKE));
b = a->Next;
a->Next = NULL;
changecolor(11);
gotoxy(b->x, b->y);
printf("■");
free(b);
b = NULL;
Sleep(200);
changecolor(5);
}
return true;
}
}
if (sta == 3) //向左走
{
qq->x = pp->x - 2;
qq->y = pp->y;
qq->Next = pp;
pHead->Next = qq;
PSNAKE a = (PSNAKE)malloc(sizeof(SNAKE));
a = pHead->Next;
if (cannotbiteyourself() == 1 || cannotcrossthewall() == 1)
return false;
else
{
if (a->x == food->x && a->y == food->y)
{
gotoxy(a->x, a->y);
printf("●");
a = a->Next;
while (a != NULL)
{
gotoxy(a->x, a->y);
printf("◆");
a = a->Next;
}
Sleep(200);
createfood();
score += 5;
}
else
{
gotoxy(a->x, a->y);
printf("●");
a = a->Next;
while (a->Next->Next != NULL)
{
gotoxy(a->x, a->y);
printf("◆");
a = a->Next;
}
gotoxy(a->x, a->y);
printf("◆");
PSNAKE b = (PSNAKE)malloc(sizeof(SNAKE));
b = a->Next;
a->Next = NULL;
changecolor(11);
gotoxy(b->x, b->y);
printf("■");
free(b);
b = NULL;
Sleep(200);
changecolor(5);
}
return true;
}
}
if (sta == 4) //向右
{
qq->x = pp->x + 2;
qq->y = pp->y;
qq->Next = pp;
pHead->Next = qq;
PSNAKE a = (PSNAKE)malloc(sizeof(SNAKE));
a = pHead->Next;
if (cannotbiteyourself() == 1 || cannotcrossthewall() == 1)
return false;
else
{
if (a->x == food->x && a->y == food->y)
{
gotoxy(a->x, a->y);
printf("●");
a = a->Next;
while (a != NULL)
{
gotoxy(a->x, a->y);
printf("◆");
a = a->Next;
}
Sleep(200);
createfood();
score += 5;
}
else
{
gotoxy(a->x, a->y);
printf("●");
a = a->Next;
while (a->Next->Next != NULL)
{
gotoxy(a->x, a->y);
printf("◆");
a = a->Next;
}
gotoxy(a->x, a->y);
printf("◆");
PSNAKE b = (PSNAKE)malloc(sizeof(SNAKE));
b = a->Next;
a->Next = NULL;
changecolor(11);
gotoxy(b->x, b->y);
printf("■");
free(b);
b = NULL;
Sleep(200);
changecolor(5);
}
return true;
}
}
}
/*不能撞墙*/
int cannotcrossthewall()
{
if (pHead->Next->x == 0 || pHead->Next->x == 50 || pHead->Next->y == 0 || pHead->Next->y == 26)
flag = 1;
return flag;
}
/*不能撞到自己*/
int cannotbiteyourself()
{
PSNAKE bite = (PSNAKE)malloc(sizeof(SNAKE));
bite = pHead->Next->Next; //bite从第二个节点开始判断
while (bite != NULL)
{
if (bite->x == pHead->Next->x && bite->y == pHead->Next->y)
flag = 1;
bite = bite->Next;
}
return flag;
}
/*游戏结束画面*/
void gameover()
{
if (score > HighScore)
write_file();
system("cls");
changecolor(11);
char chCmd[32];
sprintf_s(chCmd, "mode con cols=%d lines=%d", 60, 25);
system(chCmd);
gotoxy(5,5);
printf(" /\\________/\\\n");
gotoxy(5,6);
printf(" | Q Q |\n");
gotoxy(5,7);
printf(" | ⌒ | ---你死的好惨啊呜呜呜(;′⌒`) \n");
gotoxy(5,8);
printf(" | |\n");
gotoxy(5,9);
printf(" \\____________/ \n");
gotoxy(5,10);
printf(" \n");
gotoxy(5,11);
changecolor(9);
printf(" Your score is %d \n", score);
gotoxy(5,12);
printf("Highest score : %d \n", HighScore);
gotoxy(1,15);
printf("Press space back to the menu......");
gotoxy(35,15);
getchar();
system("cls");
startmenu();
}
/*读取最高分*/
void read_file()
{
FILE* fp;
fp = fopen("saveeeee.txt", "a+"); //打开文件save.txt
fscanf(fp, "%d", &HighScore); //把文件中的最高分读出来
fclose(fp); //关闭文件
}
/*记录最高分*/
void write_file()
{
FILE* fp;
fp = fopen("saveeeee.txt", "w+"); //以读写的方式建立一个名为save.txt的文件
fprintf(fp, "%d", score); //把分数写进文件中
fclose(fp);
}
/*查看最高分*/
void highestscore()
{
system("cls");
char chCmd[32];
sprintf_s(chCmd, "mode con cols=%d lines=%d", 50, 25);
system(chCmd);
changecolor(14);
gotoxy(5, 5);
printf("Your highest score is %d.\n\n", HighScore);
gotoxy(1, 9);
printf("Press space back to the menu......");
gotoxy(35, 9);
int cha = getch();
while (cha == 0x0d)
{
system("cls");
startmenu();
}
}