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_positonset_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. 游戏控制

关于操作的定义,我们已经在上文中说明了,但是在具体控制中,还有部分细节问题要处理

  1. 连续按同一个方向键,蛇加速
  2. 蛇不能立刻反向移动,比如蛇正在向左运动,即使你先按按下右移键,蛇也不会立刻右转
  3. 加速中的蛇方面变化后则回复正常移速
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语言贪吃蛇小游戏(二)相关推荐

  1. 再来一次的C语言贪吃蛇小游戏(三)

    8.游戏的不同界面 为了便于实现主要功能,之前我们所有的状态控制都是放在游戏中,但实际上我们应该把这些状态控制抽离出来,通过菜单来控制,以便在不同游戏界面间切换. 菜单界面 游戏界面 排行榜 游戏结束 ...

  2. 再来一次的C语言贪吃蛇小游戏(一)

    0. 写在开头 学习编程也有两三年时间了,中间也玩(学校安排学习)过很多东西 ,从汇编到C到Java和python.用Java和Python也就图一快,真要体会编程乐趣还得看我C语言(开玩笑 ) 为什 ...

  3. C语言小游戏大全,C语言贪吃蛇小游戏(附源码)

    一.C语言小游戏大全,C语言贪吃蛇小游戏(附源码) 贪吃蛇小游戏源码和更多C语言课设项目小游戏源码免 费 下 载 链 接 如下: c语言项目课设小游戏源码资料压缩包.zip-C文档类资源-CSDN下载 ...

  4. c语言贪吃蛇小游戏的简易实现

    命令行界面通过打印字符实现了贪吃蛇游戏的基本功能 代码仅130行 实现原理为,采用链式单调数列在矩阵地图中储存为蛇的坐标信息. //贪吃蛇的实现原理在于,将蛇在矩阵中的点表示为正数,并且由蛇尾到蛇头是 ...

  5. c语言--贪吃蛇小游戏

    第一步:下载visual 2019编程软件 和 Easyx 第二步:设置头文件: 编写代码: 首先得做一个看得过去的运行窗口: initgraph(680, 480); //设置背景颜色 setbkc ...

  6. 手把手教你做一个Java贪吃蛇小游戏

    大家好,我是孙不坚1208,这篇博客给大家分享一下:如何做一个贪吃蛇小游戏(Java版)的exe应用程序,希望能给需要帮助的朋友带来方便. 手把手教你做一个Java贪吃蛇小游戏的exe应用程序 一.J ...

  7. 基于C语言Ncurse库和链表的简单贪吃蛇小游戏

    参考:基于C语言Ncurse库和链表的简单贪吃蛇小游戏 作者:三速何时sub20 发布时间:2020-09-29 10:23:51 网址:https://blog.csdn.net/weixin_44 ...

  8. c语言小游戏 精简_一个简易的贪吃蛇小游戏C语言源码

    /* *程序名称:贪吃蛇v2.1 *程序描述:一个简易的贪吃蛇小游戏 *版本信息:v2.1 *v1.1版本更新:1:加入菜单选择项 *v1.2版本更新:1:修复菜单选择bug *v1.3班本更新:1: ...

  9. c语言贪吃蛇打包到桌面,C语言实现桌面贪吃蛇小游戏

    本篇写的是桌面贪吃蛇小游戏,大家自己看吧,感谢大家的支持,谢谢!O(∩_∩)O~~ #define _CRT_SECURE_NO_WARNINGS #include #include #include ...

最新文章

  1. Android AutoWrapTextView 解决中英文排版问题
  2. GNOME启动时激活NumLock
  3. 关于const 修饰的一些知识
  4. bzoj2194 快速傅里叶之二
  5. 恢复被CVS被Override and Update后的代码
  6. html语言书写注意事项,HTML注意事项(学习笔记)
  7. 工作162:选择日期自传父
  8. Flutter实战一Flutter聊天应用(三)
  9. linux设置usb选择性暂停,USB大容量存储设备无法启动的解决方法
  10. TCP全连接端口扫描器
  11. utc时间 单位换算_UTC转换本地时间
  12. 2016级移动应用开发在线测试13-Location、Sensor Network
  13. 标准交换机和分布式交换机区别
  14. 阿里巴巴2015校园招聘面试大礼包
  15. 利用Hudi Bootstrap转化现有Hive表的parquet/orc文件为Hudi表
  16. 金智塔CTO陈超超:构建产学研用价值闭环,持续探索隐私计算技术前沿 | 数据猿专访...
  17. 杰理下载器强制下载工具的使用介绍_AC695N696NAD14AD15全系列支持,看完此文就足够了
  18. 在h文件中函定义+ #pragma once 是什么意思?
  19. Ubuntu双系统+pytorch环境搭建日记
  20. windows10开机密码忘记了怎么解决

热门文章

  1. 约瑟夫问题的循环链表实现
  2. C语言经典例4-某一天是这一年的第几天
  3. 【Linux 内核】CFS 调度器 ④ ( 调度子系统组件模块 | 主调度器、周期性调度器 | 调度器类 )
  4. 【Linux 内核】进程管理 ( 进程相关系统调用源码分析 | fork() 源码 | vfork() 源码 | clone() 源码 | _do_fork() 源码 | do_fork() 源码 )
  5. 【Linux 内核】进程管理 task_struct 结构体 ④ ( comm 字段 | 进程优先级字段 | cpus_ptr 字段 | mm、active_mm 字段 | fs 字段 )
  6. 【数字信号处理】周期序列 ( 正弦序列特性 | 单个模拟周期采集 m 个数字样本 | Q 个模拟周期采集 P 个数字样本 | 非周期序列的情况 | 数字信号周期 )
  7. 【Windows 逆向】使用 CE 工具挖掘关键数据内存真实地址 ( 完整流程演示 | 查找临时内存地址 | 查找真实指针地址 )
  8. 【Android 逆向】Android 中常用的 so 动态库 ( 拷贝 /system/lib/ 中的 Android 系统 so 动态库 )
  9. 【Java 并发编程】线程池机制 ( 线程池状态分析 | 线程池状态转换 | RUNNING | SHUTDOWN | STOP | TIDYING | TERMINATED )
  10. 【Java 并发编程】线程池机制 ( 线程池执行任务细节分析 | 线程池执行 execute 源码分析 | 先创建核心线程 | 再放入阻塞队列 | 最后创建非核心线程 )