再来一次的C语言贪吃蛇小游戏(二)
5.游戏地图
贪吃蛇的游戏地图由一个个方格组成,每一格方格可以表示地面,墙,食物,蛇的身体等等,蛇只能在地面上行动,吃到食物则生长一节蛇身,撞到墙就GG。
我们可以同样可以使用枚举来表示地图上的不同物体
//game.h
typedef enum stuff_mark
{wall_mark,ground_mark,food_mark,snake_mark
}Stuff_Mark;
为了便于表示我们创建一个物品结构体,用来保存地图上每个物理的位置,和物体的类别
//game.h
typedef struct stuff {int pos_x;int pos_y;Stuff_Mark mark;
}Stuff;
这样一来整个游戏的表示框架就定好了。
//game.h
//游戏当前状态
typedef enum state {in_menu,gaming,to_quit
}State;//WIDTH, HEIGHT为宏定义的地图宽高,根据喜好设置
struct game
{int score;Stuff map[HEIGHT][WIDTH];Stuff *food;State state;
};
为什么要单独保存食物在游戏结构体中呢?map中不是保存了地图上所有相关信息吗?不急,预计下下一篇揭晓哈。
关于游戏中的操作,暂时我们定义操作行为可以有:
- 上移:
move_up
,对应w
键 - 下移:
move_down
,对应s
键 - 左移:
move_left
,对应a
键 - 右移:
move_right
,对应d
键 - 退出:
esc
,对应Esc
键 - 无操作:
no_option
为了对操作进行规范,我们也定义为枚举类型
//game.h
typedef enum option
{move_up = 'w',move_left = 'a',move_down = 's',move_right = 'd',esc = 27,no_option = 0
}Option;
6. 初始化游戏,在win32控制台显示出来
关于游戏中要展示的物体,我们都已经定义好了,那么怎么样展示出来呢?
//game.h
void start_game();
void init_map(struct game* Game);
void display_map(struct game* Game);
void display_snake(struct game *Game, Snake *snake);
void display_mark(Stuff *stuff);
void grow_food(struct game*Game);
void snake_move(struct game*Game, Snake* snake, Direction dir);
void judge_move_input(struct game*Game, Snake* snake, int* input, int* last_input);
首先解释一下这个display_mark这个方法,这个方法的唯一功能就是在一个物体所在的地方,输出表示该物体的符号,通过调用此方法,我们可以展示游戏中的所有内容,set_cursor_positon
,set_console_color
分别实我自定义的控制台函数,用来设置控制台输出位置和颜色。相关代码在本系列(三)所提供的源码中。
//game.c
void display_mark(Stuff *stuff)
{set_cursor_position(stuff->pos_x, stuff->pos_y);switch (stuff->mark){case wall_mark:set_console_color(6, 0);printf("##");break;case ground_mark:set_console_color(7, 0);printf(" ");break;case food_mark:set_console_color(4, 0);printf("@");break;case snake_mark:set_console_color(2, 0);printf("■");break;default:break;}
}
游戏初始化操作
//game.h
void init_map(struct game* Game)
{for (int i = 0; i < WIDTH; i++) {for (int j = 0; j < HEIGHT; j++){Game->map[j][i].pos_x = i;Game->map[j][i].pos_y = j;if (j == 0 || j == HEIGHT-1 || i==0 || i==WIDTH-1){Game->map[j][i].mark = wall;}else Game->map[j][i].mark = ground;}}
}void grow_food(struct game* Game)
{int pos_x = rand() % WIDTH;int pos_y = rand() % HEIGHT;Stuff *stuff = &Game->map[pos_y][pos_x];stuff->mark = food_mark;Game->food = stuff;display_mark(stuff);
}void display_snake(struct game*Game, Snake *snake)
{Snake_Body_Node *head = snake->head;Stuff *stuff;while (head->next_node != head){stuff = &Game->map[head->pos_y][head->pos_x];stuff->mark = snake_mark;display_mark(stuff);}stuff = &Game->map[head->pos_y][head->pos_x];stuff->mark = snake_mark;display_mark(stuff);
}
当蛇移动的时候,我们在操作蛇的链表时并没有改变全部的节点,同时我们在显示蛇的时候,也没有必要更新所有蛇身的显示,我们只需要更新头尾的显示即可
void snake_move(struct game*Game, Snake* snake, Direction dir)
{Stuff *stuff;int pos_x = snake->head->pos_x;int pos_y = snake->head->pos_y;pos_x = dir == left ? pos_x - 1 : dir == right ? pos_x + 1 : pos_x;pos_y = dir == up ? pos_y - 1 : dir == down ? pos_y + 1 : pos_y;switch (Game->map[pos_y][pos_x].mark){case ground_mark:stuff = &Game->map[pos_y][pos_x];stuff->mark = snake_mark;display_mark(stuff);stuff = &Game->map[snake->head->previous_node->pos_y][snake->head->previous_node->pos_x];stuff->mark = ground_mark;display_mark(stuff);move(snake, dir, pos_x, pos_y);break;case food_mark:stuff = &Game->map[pos_y][pos_x];stuff->mark = snake_mark;display_mark(stuff);eat(snake, dir, pos_x, pos_y);grow_food(Game);Game->score++;case snake_mark:exit(1);break;case wall_mark:exit(1);break;default:break;}
}
最初效果如下:■■■■表示蛇,@表示食物
7. 游戏控制
关于操作的定义,我们已经在上文中说明了,但是在具体控制中,还有部分细节问题要处理
- 连续按同一个方向键,蛇加速
- 蛇不能立刻反向移动,比如蛇正在向左运动,即使你先按按下右移键,蛇也不会立刻右转
- 加速中的蛇方面变化后则回复正常移速
void judge_move_input(struct game*Game, Snake* snake, int* input, int* last_input)
{if (*last_input + *input == move_down + move_up ||*last_input + *input == move_left + move_right){snake_move(Game, snake, snake->head->dir);return;}else if (*last_input == *input){snake->speed = fast;}else{snake->speed = normal;*last_input = *input;}Direction dir = *input == move_up ? up : *input == move_down ?down : *input == move_left ? left : right;snake_move(Game, snake, dir);
}
总的游戏循环如下
void start_game()
{struct game *Game = (struct game*)malloc(sizeof(struct game));Snake *snake = new_born_snake(5, 5);Game->score = 0;Game->state = gaming;init_map(Game);display_map(Game);display_snake(Game, snake);grow_food(Game);int input = no_option;int last_input = no_option;while (Game->state == gaming){if (_kbhit()){input = _getch();FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));}else input = no_option;switch (input){case move_up:case move_right:case move_down:case move_left:judge_move_input(Game, snake, &input, &last_input);break;case esc:Game->state = back_to_menu;break;case no_option:default:snake_move(Game, snake, snake->head->dir);break;}}free(Game);
}
8.游戏菜单实现
下一篇链接
再来一次的C语言贪吃蛇小游戏(二)相关推荐
- 再来一次的C语言贪吃蛇小游戏(三)
8.游戏的不同界面 为了便于实现主要功能,之前我们所有的状态控制都是放在游戏中,但实际上我们应该把这些状态控制抽离出来,通过菜单来控制,以便在不同游戏界面间切换. 菜单界面 游戏界面 排行榜 游戏结束 ...
- 再来一次的C语言贪吃蛇小游戏(一)
0. 写在开头 学习编程也有两三年时间了,中间也玩(学校安排学习)过很多东西 ,从汇编到C到Java和python.用Java和Python也就图一快,真要体会编程乐趣还得看我C语言(开玩笑 ) 为什 ...
- C语言小游戏大全,C语言贪吃蛇小游戏(附源码)
一.C语言小游戏大全,C语言贪吃蛇小游戏(附源码) 贪吃蛇小游戏源码和更多C语言课设项目小游戏源码免 费 下 载 链 接 如下: c语言项目课设小游戏源码资料压缩包.zip-C文档类资源-CSDN下载 ...
- c语言贪吃蛇小游戏的简易实现
命令行界面通过打印字符实现了贪吃蛇游戏的基本功能 代码仅130行 实现原理为,采用链式单调数列在矩阵地图中储存为蛇的坐标信息. //贪吃蛇的实现原理在于,将蛇在矩阵中的点表示为正数,并且由蛇尾到蛇头是 ...
- c语言--贪吃蛇小游戏
第一步:下载visual 2019编程软件 和 Easyx 第二步:设置头文件: 编写代码: 首先得做一个看得过去的运行窗口: initgraph(680, 480); //设置背景颜色 setbkc ...
- 手把手教你做一个Java贪吃蛇小游戏
大家好,我是孙不坚1208,这篇博客给大家分享一下:如何做一个贪吃蛇小游戏(Java版)的exe应用程序,希望能给需要帮助的朋友带来方便. 手把手教你做一个Java贪吃蛇小游戏的exe应用程序 一.J ...
- 基于C语言Ncurse库和链表的简单贪吃蛇小游戏
参考:基于C语言Ncurse库和链表的简单贪吃蛇小游戏 作者:三速何时sub20 发布时间:2020-09-29 10:23:51 网址:https://blog.csdn.net/weixin_44 ...
- c语言小游戏 精简_一个简易的贪吃蛇小游戏C语言源码
/* *程序名称:贪吃蛇v2.1 *程序描述:一个简易的贪吃蛇小游戏 *版本信息:v2.1 *v1.1版本更新:1:加入菜单选择项 *v1.2版本更新:1:修复菜单选择bug *v1.3班本更新:1: ...
- c语言贪吃蛇打包到桌面,C语言实现桌面贪吃蛇小游戏
本篇写的是桌面贪吃蛇小游戏,大家自己看吧,感谢大家的支持,谢谢!O(∩_∩)O~~ #define _CRT_SECURE_NO_WARNINGS #include #include #include ...
最新文章
- Android AutoWrapTextView 解决中英文排版问题
- GNOME启动时激活NumLock
- 关于const 修饰的一些知识
- bzoj2194 快速傅里叶之二
- 恢复被CVS被Override and Update后的代码
- html语言书写注意事项,HTML注意事项(学习笔记)
- 工作162:选择日期自传父
- Flutter实战一Flutter聊天应用(三)
- linux设置usb选择性暂停,USB大容量存储设备无法启动的解决方法
- TCP全连接端口扫描器
- utc时间 单位换算_UTC转换本地时间
- 2016级移动应用开发在线测试13-Location、Sensor Network
- 标准交换机和分布式交换机区别
- 阿里巴巴2015校园招聘面试大礼包
- 利用Hudi Bootstrap转化现有Hive表的parquet/orc文件为Hudi表
- 金智塔CTO陈超超:构建产学研用价值闭环,持续探索隐私计算技术前沿 | 数据猿专访...
- 杰理下载器强制下载工具的使用介绍_AC695N696NAD14AD15全系列支持,看完此文就足够了
- 在h文件中函定义+ #pragma once 是什么意思?
- Ubuntu双系统+pytorch环境搭建日记
- windows10开机密码忘记了怎么解决
热门文章
- 约瑟夫问题的循环链表实现
- C语言经典例4-某一天是这一年的第几天
- 【Linux 内核】CFS 调度器 ④ ( 调度子系统组件模块 | 主调度器、周期性调度器 | 调度器类 )
- 【Linux 内核】进程管理 ( 进程相关系统调用源码分析 | fork() 源码 | vfork() 源码 | clone() 源码 | _do_fork() 源码 | do_fork() 源码 )
- 【Linux 内核】进程管理 task_struct 结构体 ④ ( comm 字段 | 进程优先级字段 | cpus_ptr 字段 | mm、active_mm 字段 | fs 字段 )
- 【数字信号处理】周期序列 ( 正弦序列特性 | 单个模拟周期采集 m 个数字样本 | Q 个模拟周期采集 P 个数字样本 | 非周期序列的情况 | 数字信号周期 )
- 【Windows 逆向】使用 CE 工具挖掘关键数据内存真实地址 ( 完整流程演示 | 查找临时内存地址 | 查找真实指针地址 )
- 【Android 逆向】Android 中常用的 so 动态库 ( 拷贝 /system/lib/ 中的 Android 系统 so 动态库 )
- 【Java 并发编程】线程池机制 ( 线程池状态分析 | 线程池状态转换 | RUNNING | SHUTDOWN | STOP | TIDYING | TERMINATED )
- 【Java 并发编程】线程池机制 ( 线程池执行任务细节分析 | 线程池执行 execute 源码分析 | 先创建核心线程 | 再放入阻塞队列 | 最后创建非核心线程 )