这次玩个小项目,经典游戏《贪吃蛇》,先放一张路飞4档蛇人的图镇镇场。
你所需要具备的知识(转而言之小项目所涉及到的,不了解的话我之前文章都有涉及,可以回头看看):
- 指针
- 函数之通信
- 多线程(不涉及锁)
- 链表
- 文件的操作
贪吃蛇框架设计(也没那么高大尚,你可以理解为有啥功能):
- 地图构建
- 创建一条蛇
- 方向和暂停功能
- 蛇的运动逻辑
- 自撞和撞墙的失败检测
- 计分系统
- 分数存储(非易失存储上)
- 游戏难度处理
PS:
- 如果可以尽量少用全局变量
- 函数间通讯入参是指针
- 不影响功能的前提下,耗时操作尽量用线程
- 文件的操作要格外小心
- 全局变量的命名、局部变量的命名 最好统一规范,这个的好处在于你代码越多所越体现而出
废话上说,直接上代码!
snake.c
/*
* Project Name:Gluttonous Snake
* Version:1.0
*/
#include<stdio.h>
#include<conio.h>
#include<windows.h>
#include<stdlib.h>
#include<time.h>
#include <pthread.h>
#include"snake.h"
int g_WaitTime = 300;
int g_Score = 0;
int g_EndGameStatus = 0;
snake *g_Head;
snake *g_P;
int g_Status = R;
int g_Key;
int g_Pos = 0;
int foodX, foodY;
pthread_t ThreadSaveScore;
void Pos(int x, int y) //光标定位
{
COORD pos;
HANDLE hOutput;
pos.X = x;
pos.Y = y;
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOutput, pos);
}
void Stop() //暂停函数
{
while (1)
{
if (g_Key = getch() == ' ')
break;
}
}
void EndGame() //游戏结束调度
{
system("cls");
Pos(27, 13);
if (g_EndGameStatus == 1)
printf("您撞到墙了");
if (g_EndGameStatus == 2)
printf("您咬到了自己");
Pos(27, 14);
printf("您的得分为 %d", g_Score);
exit(0);
}
void Welcome() //欢迎界面
{
Pos(27, 13);
printf("欢迎来到贪吃蛇游戏!");
Pos(27, 14);
printf("Weclome to Gluttonous Snake!");
Pos(27, 15);
system("pause");
system("cls");
}
int BitSelf() //自撞检测
{
g_P = g_Head->next;
while (g_P)
{
if (g_P->x == g_Head->x && g_P->y == g_Head->y) {
return True;
}
g_P = g_P->next;
}
return False;
}
void CreatMap() //地图创建
{
int i, j;
for (i = 0; i <= MAX_LEN; i++) {
for (j = 0; j <= MAX_WIDE; j++)
{
Pos(i, 0);
printf("@");
Pos(i, MAX_WIDE);
printf("@");
Pos(0, j);
printf("@");
Pos(MAX_LEN, j);
printf("@");
}
}
}
void CreateMapThread() //地图创建线程
{
pthread_t ThreadCreateMap;
pthread_create(&ThreadCreateMap, NULL, CreatMap, NULL);
pthread_join(ThreadCreateMap, NULL);
sem_destroy(&ThreadCreateMap);
}
void CrossWall() //撞墙检测
{
if (g_Head->x == 0 || g_Head->y == 0 || g_Head->x == MAX_LEN || g_Head->y == MAX_WIDE)
{
g_EndGameStatus = 1;
EndGame();
}
}
void CreatFood() //食物创建
{
srand(time(NULL));
foodX = rand() % (MAX_LEN - 1) + 1;
foodY = rand() % (MAX_WIDE -1) + 1;
Pos(foodX, foodY);
printf("*");
}
void MovingCheck(int *xFlag,int *yFlag) //移动检测
{
CrossWall();
if (BitSelf())
{
g_EndGameStatus = 2;
EndGame();
}
switch (g_Status) {
case R:
*xFlag = 1;
*yFlag = 0;
return;
case L:
*xFlag = -1;
*yFlag = 0;
return;
case U:
*xFlag = 0;
*yFlag = -1;
return;
case D:
*xFlag = 0;
*yFlag = 1;
return;
default:
return;
}
}
void SnakeMoving() //蛇移动处理函数
{
int xFlag = 0;
int yFlag = 0;
snake *newhead;
newhead = (snake*)malloc(sizeof(snake));
MovingCheck(&xFlag, &yFlag);
if (g_Head->x == foodX && g_Head->y == foodY)
{
g_Score = g_Score + 10;
newhead->x = g_Head->x + xFlag;
newhead->y = g_Head->y + yFlag;
newhead->next = g_Head;
g_Head = newhead;
g_P = g_Head;
while (g_P)
{
Pos(g_P->x, g_P->y);
printf("*");
g_P = g_P->next;
}
CreatFood();
}
else
{
newhead->x = g_Head->x + xFlag;
newhead->y = g_Head->y + yFlag;
newhead->next = g_Head;
g_Head = newhead;
g_P = g_Head;
while (g_P->next->next)
{
Pos(g_P->x, g_P->y);
printf("*");
g_P = g_P->next;
}
Pos(g_P->next->x, g_P->next->y);
printf(" ");
free(g_P->next);
g_P->next = NULL;
}
}
void UserContrl() //用户控制检测
{
if (kbhit())
g_Key = getch();
switch (g_Key)
{
case KEY_RIGHT:
if (g_Status != L)
g_Status = R;
break;
case KEY_LEFT:
if (g_Status != R)
g_Status = L;
break;
case KEY_UP:
if (g_Status != D)
g_Status = U;
break;
case KEY_DOWN:
if (g_Status != U)
g_Status = D;
break;
case KEY_SPACE:
Stop();
break;
default:
break;
}
}
void SnakeSpeed() //蛇移动速度处理
{
int flag[5] = { 50, 100, 150, 200 ,300 };
if ((g_Score == flag[g_Pos]) && (g_Pos < 5)) {
g_WaitTime = g_WaitTime - 50;
g_Pos++;
}
Sleep(g_WaitTime);
}
void SaveScore() //存储分数
{
int res;
int HistoryScore = GetHistoryBest();
FILE *fp;
while (1) {
fp = fopen(SCOREPATH, "wt+");
if (fp == NULL) {
printf("open a null file");
}
if (HistoryScore < g_Score) {
fprintf(fp, "%dn", g_Score);
} else {
fprintf(fp, "%dn", HistoryScore);
}
fclose(fp);
Sleep(50);
}
}
void SaveScoreThread() //存储分数线程
{
pthread_create(&ThreadSaveScore, NULL, SaveScore, NULL);
}
int GetHistoryBest() {
int HistoryScore = 0;
FILE *fp;
fp = fopen(SCOREPATH, "rt+");
if (fp == NULL) {
printf("open a null file");
return False;
}
rewind(fp);
fscanf(fp, "%d", &HistoryScore);
fclose(fp);
return HistoryScore;
}
void GameProcess() //游戏处理
{
int res;
res = GetHistoryBest();
Pos(80, 4);
printf("操作说明");
Pos(80, 5);
printf("W A S D 分别对应上 左 下 右 ");
Pos(80, 6);
printf("按空格键暂停");
Pos(80, 18);
printf("历史最高分: %d", res);
while (1)
{
Pos(80, 20);
printf("游戏分数:%d", g_Score);
UserContrl();
SnakeSpeed();
SnakeMoving();
}
}
void InitSnake() //蛇的初始化
{
int i;
snake *tail;
g_Head = (snake*)malloc(sizeof(snake));
g_Head->x = MAX_LEN / 2;
g_Head->y = MAX_WIDE / 2;
g_Head->next = NULL;
for (i = 1; i < 4; i++)
{
tail = (snake*)malloc(sizeof(snake));
tail->x = MAX_LEN / 2 + i * 1;
tail->y = MAX_WIDE / 2;
tail->next = g_Head;
g_Head = tail;
}
while (tail)
{
Pos(tail->x, tail->y);
printf("*");
tail = tail->next;
}
}
int main()
{
Welcome(); //欢迎界面
CreateMapThread(); //地图创建线程
CreatFood(); //食物创建
InitSnake(); //蛇的初始化
SaveScoreThread(); //分数存档线程
GameProcess(); //游戏处理
return 0;
}
snake.h
#define KEY_UP 'w' // 向上方向键
#define KEY_DOWN 's' // 向下方向键
#define KEY_RIGHT 'd' // 向右方向键
#define KEY_LEFT 'a' // 向左方向键
#define KEY_SPACE ' '
#define R 1 //向右的状态量
#define L 2 //向左的状态量
#define U 3 //向上的状态量
#define D 4 //向下的状态量
#define True 1
#define False 0
#define MAX_LEN 54
#define MAX_WIDE 26
#define SCOREPATH "score.txt"
typedef struct node
{
int x;
int y;
struct node*next;
}snake;
最后,别忘了创建个score.txt里面空就好~大功告成!