用c++写控制台贪吃蛇

目录

创建界面

了解界面的生成和输入

下一步设置我们的提示

构建游戏界面

游戏本体

创建蛇

蛇的行走

编写失败条件。

隐藏光标


效果图

要使用的东西。

1.链表,2.结构体,3.系统操作。

创建界面

了解界面的生成和输入

先本地化环境用于在控制台输出中文setlocale(LC_ALL, "");要包含#include <locale.h>

要使用 system函数,要包括头文件windows.h。

c 复制代码
void cj() {
    setlocale(LC_ALL, "");  // 设置本地化环境
    system("mode con cols=100 lines=40");  // 调整控制台窗口尺寸
    system("pause");  // 防止窗口自动关闭
}

查看光标发现光标的长宽不匹配,调整cols和lines,来判断那个是长的。

发现调整cols后长变了所以cols是窗口的长。

调整后发现2个长等于一个宽,所以我们设置时要设置2的倍数长

设置我们的游戏界面为25*25,加上边框长乘2,54*27,再加上提示,36长。凑个整

100*30.

发现输出都从左上角开始,所以需要定位光标位置来输出,后续的内容也要运用光标的定位,所以我们要定义一个函数来表示光标定位。

c 复制代码
HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);

// 获取控制台输出句柄
// 使用GetStdHandle(STD_OUTPUT_HANDLE)获取标准输出设备句柄

COORD pos = {x, y};  // 定义光标位置坐标(左上角为原点(0,0))

SetConsoleCursorPosition(houtput, pos);  // 将光标定位到指定坐标

void dw(int x, int y) {
    HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD pos = {x, y};
    SetConsoleCursorPosition(houtput, pos);
}

构建欢迎界面,(简介版)

dw(40, 15);

wprintf(L"欢迎来到贪吃蛇游戏");//用wprintf来输出宽字符,L表示后面的是宽字符,如中文等。

dw(40, 16);

system("pause");

到下一个界面我们要清空当前界面。

system("cls");来清空界面

cpp 复制代码
system("cls");  // 清屏操作
dw(40, 16);     // 调用dw函数,参数为40和16
system("pause");// 暂停程序执行
 

下一步设置我们的提示

按W.S.A.D移动,F4加速,F5减速,space暂停,Esc退出。

定位要的位置输出数据,调整位置让它 看起居中。

cpp 复制代码
dw(24, 15);
wprintf(L"按W.S.A.D移动,F4加速,F5减速,space暂停,Esc退出。");
dw(40, 16);
printf("加速能得到更高的分数");
dw(40, 17);
system("pause");

构建游戏界面

构建蛇的活动范围墙

发现高度不够扩高一点到40;下移墙,在顶上标识提示词表明游戏界面。

cpp 复制代码
system("cls");
int i = 0, j = 0;
for (i = 3; i < 30; i++)
{
    dw(0, i);
    for (j = 0; j < 27; j++) {
        if (i == 3 || i == 29 || j == 0 || j == 26)
            wprintf(L"墙");
        else
            wprintf(L"  ");
    }
    printf("\n");
}
dw(14, 1);
wprintf(L"游戏界面");
dw(0, 32);
system("pause");

右边加入提示词

cpp 复制代码
dw(60, 6);
wprintf(L"时间:%.2f", sj);
dw(60, 10);
wprintf(L"不能撞墙,不能碰到自己");
dw(60, 11);
wprintf(L"按W.S.A.D移动\n");
dw(60, 12);
wprintf(L"F4加速,F5减速\n");
dw(60, 13);
wprintf(L"space暂停,Esc退出");
dw(60, 14);
wprintf(L"fish_xk制作");

发现函数太长了,分别封装成函数。

cpp 复制代码
float sj = 0;
void dw(int x, int y) {
    HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD pos = { x,y };
    SetConsoleCursorPosition(houtput, pos);
}
void help() {
    dw(60, 6);
    wprintf(L"时间:%.2f", sj);
    dw(60, 10);
    wprintf(L"不能撞墙,不能碰到自己");
    dw(60, 11);
    wprintf(L"按W.S.A.D移动\n");
    dw(60, 12);
    wprintf(L"F4加速,F5减速\n");
    dw(60, 13);
    wprintf(L"space暂停,Esc退出");
    dw(60, 14);
    wprintf(L"fish_xk制作");
    system("pause");
}
void ks() {
    setlocale(LC_ALL, "");
    system("mode con cols=100 lines=40");
    dw(40, 15);
    wprintf(L"欢迎来到贪吃蛇游戏");
    dw(40, 16);
    system("pause");
    system("cls");
}
void zj() {
    dw(24, 15);
    wprintf(L"按W.S.A.D移动,F4加速,F5减速,space暂停,Esc退出。");
    dw(40, 16);
    printf("加速能得到更高的分数");
    dw(40, 17);
    system("pause");
    system("cls");
}
void yxjm() {
int i = 0, j = 0;
    for (i = 3; i < 30; i++)
    {
        dw(0, i);
        for (j = 0; j < 27; j++) {
            if (i == 3 || i == 29 || j == 0 || j == 26)
                wprintf(L"墙");
            else
                wprintf(L"  ");
        }
        printf("\n");
    }
    dw(22, 1);
    wprintf(L"游戏界面");
    dw(0, 32);
}
void cj() {
    ks();
    zj();
    yxjm();
    help();
}

这样就完成了所有的界面的制作。

游戏本体

创建蛇

蛇由一个个节点组成,用链表来创建。重命名一下方便使用

cpp 复制代码
typedef struct snakebody {
    struct tcs* next;
    int x;
    int y;
}snake,st;

有蛇,有果子,有速度。用一个结构体来表示。

蛇可以用头节点来表示。

果可以看作一个在外的蛇身。

cpp 复制代码
typedef struct tcs{
    snake* head;
    int v;
    snake* guo;
}tcs;

还要有状态,方向,总分数,单个果子的权重。

cpp 复制代码
typedef struct snakebody{
    struct tcs* next;
    int x;
    int y;
}snake;
enum gamezt {
    OK=1,
    FAIL,
    DIE_BYWALL,
    DIE_BYSELF
};
enum snakefx {
    UP = 1,
    DOWN,
    LEFT,
    RIGHT
};
typedef struct tcs{
    snake* head;//蛇
    snake* guo;//果
    int v;//速度
    enum gamezt zt;//状态
    enum snakefx fx;//方向
    int food_weight;//单个分数
    int score;//总分数
}tcs;

初始化一下蛇的身体。

尾插链表来初始蛇身

cpp 复制代码
void push(snake**ps,int x,int y) {//插入蛇的节点
    snake* news = (snake*)malloc(sizeof(snake));//开辟节点
    news->next = NULL;
    news->x = x;
    news->y = y;
    if (*ps == NULL) {
        *ps = news;
    }
    else
    {
        snake* pos = *ps;
        while (pos->next) {//链接到末尾
            pos = pos->next;
        }
        pos->next = news;
    }
}

snake* body = (snake*)malloc(sizeof(snake));
body = NULL;
int i;
for (i = 22; i < 27; i = i + 1) {
    push(&body,22+(i-22)*2, 16);
}
ps->head = body;
print(ps);
cpp 复制代码
void print(snake*ps) {//打印蛇
	assert(ps);
	snake* cur = ps;
	while (cur) {//遍历链表
		dw(cur->x, cur->y);
		wprintf(L"蛇");
		cur = cur->next;
	}
}
tcs* cjsnake() {//返回蛇的头位置
	tcs*ps = (tcs*)malloc(sizeof(tcs));
	snake* body = (snake*)malloc(sizeof(snake));
	body = NULL;
	int i;
	for (i = 22; i < 27; i = i + 1) {//循环添加5个节点
		push(&body,22+(i-22)*2, 16);
	}
	ps->head = body;
	print(ps->head);//打印开始的蛇身
	cjguo(ps);
	ps->v = 200;
	ps->zt = OK;
	ps->fx = LEFE;
	ps->score = 0;
	ps->food_weight = 10;
	return ps;
}

下一步生成果子,要有随机数要包含#include<time.h>

设置随机数

cpp 复制代码
srand(time(NULL))

void cjguo(tcs* ps) {
    snake* news = (snake*)malloc(sizeof(snake));
    news->next = NULL;
    snake* cur = ps->head;
    ag:
    news->x = (rand()%25+1+1)*2;//第一个+1是保证1到25,后一个+1,是不能是墙
    news->y = (rand() % 25 + 1+4 );
    cur = ps->head;
    while (cur){
        if (cur->x == news->x && cur->y == news->y)//如果和蛇重叠重新生成。
            goto ag;
        cur = cur->next;
    }
    ps->guo = news;
    dw(news->x, news->y);
    wprintf(L"果");//打印果的位置
}

设置其他数据

cpp 复制代码
    ps->v = 200;//速度
    ps->zt = OK;//状态
    ps->fx = LEFT;//方向
    ps->score = 0;//成绩
    ps->food_weight = 10;//每个果子的分数

创建完毕

蛇的行走

cpp 复制代码
while (cur->zt == OK) {
dw(60, 6);
wprintf(L"时间:%.2f秒", sj);
dw(60, 8);
printf("得分:%d", cur->score);
dw(60, 9);
printf("食物分值:%d", cur->food_weight);
snakedong(cur);//行动的函数
sj+=cur->v/1000.0;//行动时间
Sleep(cur->v);//行动间隔
}

设置循环判断游戏状态。可以再加入时间

设置程序的休息时间,来实现运动间隔。

要控制程序,需要读取输入信息,所以定义一个读取的宏

cpp 复制代码
#define KEY_PRESS(vk)   ((GetAsyncKeyState(vk)&1)?1:0)

读取一个虚拟键盘值,判断是否按过。

cpp 复制代码
if (KEY_PRESS(0x53) && snake->fx != UP) {//识别W
	snake->fx = DOWN;
}
else if (KEY_PRESS(0x57) && snake->fx != DOWN) {//识别S
	snake->fx = UP;
}
else if (KEY_PRESS(0x41) && snake->fx != RIGHT) {//识别A
	snake->fx = LEFE;
}
else if (KEY_PRESS(0x44) && snake->fx != LEFE) {//识别B
	snake->fx = RIGHT;
}
else if (KEY_PRESS(VK_SPACE)) {//识别空格
	while (1) {
		Sleep(200);
		if (KEY_PRESS(VK_SPACE)) {//循环停止游戏
			break;
		}
	}
}
else if (KEY_PRESS(VK_ESCAPE)) {//识别ESC
	snake->zt = END_NORMAL;
}
else if (KEY_PRESS(VK_F4)) {//识别F4
	snake->v = snake->v - 10;
	snake->food_weight = snake->food_weight + 5;
}
else if (KEY_PRESS(VK_F5)) {//识别F5
	snake->v = snake->v + 10;
	snake->food_weight = snake->food_weight - 5;
}

加入一个全局变量防止加速过头

根据蛇的方向来改变蛇的身体坐标

cpp 复制代码
snake* toupush(snake*ps,int x,int y) {//插入头部,到达的节点
	snake* news = (snake*)malloc(sizeof(snake));
	news->next = ps;
	news->x = x;
	news->y = y;
	dw(news->x, news->y);
	wprintf(L"蛇");
	ps = news;
	return ps;
}
snake* pop(snake* ps) {//没吃到果子,删除多出来的节点
	snake* tail = ps;
	while (tail->next->next) {
		tail = tail->next;
	}
	dw(tail->next->x, tail->next->y);
	wprintf(L"  ");
	free(tail->next);
	tail->next = NULL;
	return ps;
}
if (snake->fx == UP) {//向上
	snake->head=toupush(snake->head, snake->head->x, snake->head->y - 1);
}
else if (snake->fx == DOWN) {//向下
	snake->head=toupush(snake->head, snake->head->x, snake->head->y + 1);
}
else if (snake->fx == LEFE) {//向左
	snake->head=toupush(snake->head, snake->head->x-2, snake->head->y );
}
else if (snake->fx == RIGHT) {//向右
	snake->head=toupush(snake->head, snake->head->x+2, snake->head->y );
}
if (snake->head->x != snake->guo->x || snake->head->y != snake->guo->y)//是否吃到果子
	snake->head=pop(snake->head);//没吃到,删除尾节点
else {
	free(snake->guo);//释放果子的节点
	snake->guo = NULL;
	cjguo(snake);//重新生成果子
	snake->score += snake->food_weight;//加分
}

吃到果子就不删尾,分数加权重,没吃到就删尾。

编写失败条件。

cpp 复制代码
if (snake->head->x == 0 || snake->head->x == 52 || snake->head->y == 4 || snake->head->y == 29)//到达墙体
	snake->zt = DIE_BYWALL;//改变状态
st* self = snake->head->next;
	while (self) {//遍历自身,看是否相交
		if (snake->head->x == self->x && snake->head->y == self->y)
			snake->zt = DIE_BYSELF;//如相交更新状态
		self = self->next;
	}

结束后输出分数

cpp 复制代码
system("cls");
dw(40, 15);
if (cur->zt == DIE_BYSELF)
	wprintf(L"失败于撞到自己");
if (cur->zt == DIE_BYWALL)
	wprintf(L"失败于撞墙");
if (cur->zt == END_NORMAL)
	wprintf(L"正常退出");
dw(40, 16);
wprintf(L"你坚持的时间是%.2f秒", sj);
dw(40, 17);
wprintf(L"你的得分是%d",cur->score);

隐藏光标

cpp 复制代码
void gbyc() {
	//获取标准输出设备的句柄;
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	//定义一个光标结构体
	CONSOLE_CURSOR_INFO cursor_info = { 0 };
	//获取houtput句柄相关的控制台上的光标信息,存放cursor_info
	GetConsoleCursorInfo(houtput, &cursor_info);
	//设置吧光标的占比
	cursor_info.bVisible = false;//光标消失
	//设置和houtput句柄相关的控制台的光标信息
	SetConsoleCursorInfo(houtput, &cursor_info);
}
void ck() {
	//创建窗口
	setlocale(LC_ALL, "");
	COORD pos = { 0,0 };
	system("mode con cols=100 lines=40");
	system("title.贪吃蛇");
	gbyc();
	//打印开始界面
}

到这里已经全部完成了。

相关推荐
GoodStudyAndDayDayUp42 分钟前
WIN11安装配置验证java\maven
java·开发语言·maven
王燕龙(大卫)44 分钟前
rust:trait
开发语言·后端·rust
合作小小程序员小小店44 分钟前
桌面开发,物业管理系统开发,基于C#,winform,mysql数据库
开发语言·数据库·sql·mysql·microsoft·c#
程序员-周李斌1 小时前
LinkedList 源码深度分析(基于 JDK 8)
java·开发语言·数据结构·list
chéng ௹1 小时前
前端转编码(encodeURIComponent)以及解码(decodeURIComponent)
开发语言·前端·javascript
bbq粉刷匠1 小时前
java刷题-day1
java·开发语言
温轻舟1 小时前
禁毒路上,任重道远 | HTML页面
开发语言·前端·javascript·css·html·温轻舟
Unlyrical1 小时前
线程池详解(c++手撕线程池)
c++·线程·线程池·c++11
学历真的很重要1 小时前
Hello-Agents —— 03大语言模型基础 通俗总结
开发语言·人工智能·后端·语言模型·自然语言处理·面试·langchain