1 C语言100行代码实现推箱子

1.1 概述

 C语言是很好入门编程的一个语言,它拥有着很好的移植性,基本上所有的平台都支持C语言编程。有些C语言基础的你,是不是也很想做一个项目来检验一下自己的学习成果呢?那小游戏推箱子确实是一个很不错的选择。让我们一起动手,写一个属于自己的推箱子小游戏吧!

1.2 知识点

  本文读者可以学习到:

  • 多维数组
  • 基础指针
  • 简单函数
  • 循环结构

2 构思游戏结构

2.1 数字标签

  在推箱子游戏中,所出现的物体有 : 1. 墙 2. 人 3. 空气 4. 箱子 5. 目标 6. 箱子和目标重合 7. 人和目标重合 共7种物体。为了方便在程序中表达他们,我们需要给他们都打上数字标签。

#define AIR 0
#define BOX 1
#define MAN 2
#define WALL 3
#define AIM 4
#define BOX_AIM 5
#define MAN_AIM 6

  细心的读者不难发现,我们的数字标签不是随便打的,而是有一定规律的。BOX_AIM刚刚好就等于 BOX+AIM ; 同样的 MAN_AIM 也刚刚好就等于 MAN + AIM ; 而 AIR 也正好为 0 ,这使得无论什么加上AIR 都会等于那个东西 。这样可以给书写代码带来极大的方便,也可以让代码的运行效率带来质的飞跃。

2.2 地图

  在推箱子游戏中,丰富的地图给用户带来了不一样的游戏体验。我们需要一个容器来存放地图。而数组确实是一个不错的选择。推箱子是一个二维游戏,所以我们可以选择一个二维数组来存放我们游戏的地图。

#define _X 15
#define _Y 10
int map_[ _X ][ _Y ] = {{3,3,3,3,3,3,3,3,3,3},{3,0,0,0,0,0,0,0,4,3},{3,0,1,3,0,0,0,0,0,3},{3,0,0,3,0,0,0,0,3,3},{3,0,0,3,0,0,0,0,4,3},{3,0,0,0,4,3,0,0,3,3},{3,0,0,0,3,3,0,0,0,3},{3,0,0,0,0,3,0,0,0,3},{3,0,0,0,0,3,3,0,0,3},{3,0,0,3,0,0,0,0,0,3},{3,0,0,0,1,0,3,0,0,3},{3,0,0,0,0,3,3,0,0,3},{3,0,0,0,0,0,3,1,0,3},{3,2,0,0,0,0,3,0,0,3},{3,3,3,3,3,3,3,3,3,3},};

 因为地图在说有地方都会使用到,所以我们在这里将map_定义为全局变量,放在函数的外面,并且为了方便文档的修改,用了两个宏定义_X , _Y来定义地图的大小。

2.3 流程图

  现在所有需要的东西都差不多准备好了,我们应该在写程序之前,构思好流程图,以保证我们的思路清晰,书写程序不容易犯错。绘制流程图可以有效的避免在书写程序的时候出现一些不必要的麻烦。
绘制流程图 :

 流程图绘制完毕了,该写程序了!!!???总感觉少了什么,感觉这游戏有始没有终啊!所以经过略微的改进以后,得到了下面的流程图:

  这样游戏就 “有始有终” 了!

3 书写程序

  现在程序流程都差不多构建好了;我们该书写程序了

3.1 书写主函数与全局变量

  根据流程图,我们书写的主函数如下:

#define MAN 2
#define WALL 3
#define BOX 1
#define AIR 0
#define AIM 4
#define MAN_AIM 6
#define BOX_AIM 5
#define _X 15
#define _Y 10int map_[ _X ][ _Y ] = {{3,3,3,3,3,3,3,3,3,3},{3,0,0,0,0,0,0,0,4,3},{3,0,1,3,0,0,0,0,0,3},{3,0,0,3,0,0,0,0,3,3},{3,0,0,3,0,0,0,0,4,3},{3,0,0,0,4,3,0,0,3,3},{3,0,0,0,3,3,0,0,0,3},{3,0,0,0,0,3,0,0,0,3},{3,0,0,0,0,3,3,0,0,3},{3,0,0,3,0,0,0,0,0,3},{3,0,0,0,1,0,3,0,0,3},{3,0,0,0,0,3,3,0,0,3},{3,0,0,0,0,0,3,1,0,3},{3,2,0,0,0,0,3,0,0,3},{3,3,3,3,3,3,3,3,3,3},};
int *place_man;     // 人的位置
int all_aim = 0;   // 目标的数量void Display(); // 更新屏幕
void Key();     // 获取用户操作并更新地图
void Init();        // 初始化
char Win();     // 判断现在是否胜利int main(){Init();while ( Win() ) {Display();Key();} return 0;
}

  让我们来仔细分析一下这段代码,全局变量和宏定义就不过多的解释 , 让我们把目光转义到main函数。这个函数由各个子函数组成, 只把核心流程包括在内,其他功能的实现都使用子函数来实现,不会显得主函数长,阅读困难。阅读起来越简单,修改起Bug也会更简单;主函数以Init()函数开始,以Win()函数结尾。没有一行代码是多余的。可读性相对较高。主函数写完了,我们该去逐一实现其他的函数 ;

3.2 子函数的书写

3.2.1 Display()

Display()函数的作用是更新屏幕,清屏并打印地图。首先我们得知道这个函数要完成的任务实际上有两个 :

  1. 清屏
  2. 打印地图

  所涉及知识点:

  1. cmd 命令在 C语言的实现
  2. 用循环遍历多维数值

  清屏的实现

  清屏的实现实际上很多新手都很懵逼,清屏怎么搞啊?实际上如果有些cmd基础的同学就知道控制台清屏有一个命令叫做cls 。我们最简单的实现方式就是直接调用控制台命令cls就可以了。但是cmd 的命令怎么在C语言使用呢?实际上C语言在Windows平台上有一个接口在头文件windows.h里面 ,函数是 system(const char *_Command)。这样子清屏就可以使用 system("cls");实现了。

  打印地图的实现

  打印屏幕的话我们只需要遍历整个地图,并将他们逐个打印出来就可以了,因为这个地图是二维的,所以我们打印地图的时候就可以使用两个循环来便利整个地图;

  Display(); 函数代码如下:

void Display(){system("cls");for(int i = 0; i < _X; i ++){for(int j = 0; j < _Y; j ++){printf( "%d", map_[i][j] );}printf("\n");}
}

  这样就轻松实现了清屏并打印新的地图的功能;

3.2.2 Key()

Key()函数的作用是获取用户操作并根据操作更新地图。首先我们得知道这个函数要完成的任务实际上也有两个 :

  1. 获取用户操作
  2. 根据用户操作更新地图

  所涉及知识点:

  1. 指针
  2. swtich语句

 获取用户按键的实现

  我们要获取用户的操作,如按w代表上,按s代表下,按a代表左,按d代表右。如果是用scanf()函数的话,那么输入完成后就需要按回车来确定。在游戏中,这样子的操作必然是一件愚蠢的事。所以我们必须要有一个方法来获取用户的输入并且不用按回车。是的,C语言有这样的方法:getch()。这个函数是一个不回显函数,当用户按下某个字符时,函数自动读取,无需按回车,有的C语言命令行程序会用到此函数做游戏,但是这个函数并非标准函数,要注意移植性! 这个函数在conio.h 这个文件中。

 根据用户操作更新地图的实现

  我们获取到了用户所要进行的操作,我们就需要根据用户的操作来更新地图了,这一个功能的实现可以说是比较难的,同学们可得认真理解了。用户可以有4种不同的操作,每种操作都对应着不同的效果。而这种情况switch无疑是一个很好的选择。还记得全局变量中有一个前面还没有用到的变量吗?int *place_man; // 人的位置 是的,就是这一句place_man是一个int类型的指针,这个指针指向了角色,也就是人。玩家的操作就是控制这个人的,所以我们需要一个指针保存下这个人的位置,而根据玩家操作进行更新地图。如果玩家要像右走的话,我们首先得获取到人右边的东西是什么,所以这里还需要一个指针,这个指针指向玩家需要移动的位置。如果这个位置是空气、目标的话,人就可以走过去。如果是墙的话人就不能过去。如果是箱子或者是和目标重合的箱子的话就复杂一些了,我们需要获取箱子将要移动的地方的位置。如果箱子要移动地方的位置上是空气或者目标的话,就可以人推着箱子过去,如果是箱子、墙或者目标和箱子重合的话,人就不能推着箱子过去。
  我们整理一下,所以说我们除了需要有一个指针指向人以外,还需要两个指针,分别指向人要移动方向的两个位置。并且根据这两个位置对包括人在内的三个位置进行操作。
绘制流程图如下:

  Key(); 函数代码如下:

void Key(){char key;int *ft;int *sd;key = getch();switch (key) {case 'w':case 'W':ft=place_man - _Y;sd=place_man - 2 * _Y;break;case 's':case 'S':ft=place_man + _Y;sd=place_man + 2 * _Y;break;case 'a':case 'A':ft=place_man - 1;sd=place_man - 2;break;case 'd':case 'D':ft=place_man + 1;sd=place_man + 2;break;default :return;}switch (*ft) {case WALL:return;break;case AIR:case AIM:*ft += MAN;*place_man = *place_man - MAN;place_man = ft;return;break;default :break;}switch (*sd) {case BOX:case BOX_AIM:case WALL:return;break;case AIR:case AIM:*ft -= BOX;*ft += MAN;*sd += BOX;*place_man -= MAN;place_man = ft;return;break;default :return;}
}

3.2.3 Win()

Win()函数的功能就比较简单了,只需要实现判断箱子是否都在目标上BOX_AIM 的数量是否与"all_aim"相等即可。

  所涉及知识点:

  1. 函数

  Win(); 函数代码如下:

char Win(){int boxAimNum=0;for(int i=0; i < _X; i++){for(int j=0; j < _Y; j++){if(BOX_AIM == map_[i][j]){boxAimNum++;}}}if(boxAimNum == all_aim){printf("YOU ARE WINNING");return 1;} else {return 0 ;}
}

3.2.4 Init()

Init()函数的功能是为整个程序做准备,我们有两个目标:1.获取到人的位置 2. 获取到共有多少个目标

  所涉及知识点:

  1. 没有什么知识点,都讲过了,注意不要遗漏就好了。

  Init(); 函数代码如下:

void Init(){for(int i=0;i<_X;i++){for(int j=0;j<_Y;j++){if( map_[i][j] == MAN || map_[i][j] == MAN_AIM ){place_man = &map_[i][j];}if( map_[i][j] == AIM || map_[i][j] == BOX_AIM || map_[i][j] == MAN_AIM){all_aim ++;}}}
}

3.3 整理代码

现在推箱子功能就实现了,我们把代码整理好并编译,就可以开始玩了。
整理代码如下 :

#include <stdio.h>
#include <conio.h>
#include <windows.h>#define MAN 2
#define WALL 3
#define BOX 1
#define AIR 0
#define AIM 4
#define MAN_AIM 6
#define BOX_AIM 5
#define _X 15
#define _Y 10int map_[ _X ][ _Y ] = {{3,3,3,3,3,3,3,3,3,3},{3,0,0,0,0,0,0,0,4,3},{3,0,1,3,0,0,0,0,0,3},{3,0,0,3,0,0,0,0,3,3},{3,0,0,3,0,0,0,0,4,3},{3,0,0,0,4,3,0,0,3,3},{3,0,0,0,3,3,0,0,0,3},{3,0,0,0,0,3,0,0,0,3},{3,0,0,0,0,3,3,0,0,3},{3,0,0,3,0,0,0,0,0,3},{3,0,0,0,1,0,3,0,0,3},{3,0,0,0,0,3,3,0,0,3},{3,0,0,0,0,0,3,1,0,3},{3,2,0,0,0,0,3,0,0,3},{3,3,3,3,3,3,3,3,3,3},};
int *place_man;
int all_aim = 0;void Display();
void Key();
void Init();
char Win();int main(){Init();while(1){Display();Win();Key();}}void Init(){for(int i=0;i<_X;i++){for(int j=0;j<_Y;j++){if( map_[i][j] == MAN || map_[i][j] == MAN_AIM ){place_man = &map_[i][j];}if( map_[i][j] == AIM || map_[i][j] == BOX_AIM || map_[i][j] == MAN_AIM){all_aim ++;}}}
}void Key(){char key;int *ft;int *sd;key = getch();switch (key) {case 'w':case 'W':ft=place_man - _Y;sd=place_man - 2 * _Y;break;case 's':case 'S':ft=place_man + _Y;sd=place_man + 2 * _Y;break;case 'a':case 'A':ft=place_man - 1;sd=place_man - 2;break;case 'd':case 'D':ft=place_man + 1;sd=place_man + 2;break;default :return;}switch (*ft) {case WALL:return;break;case AIR:case AIM:*ft += MAN;*place_man = *place_man - MAN;place_man = ft;return;break;default :break;}switch (*sd) {case BOX:case BOX_AIM:case WALL:return;break;case AIR:case AIM:*ft -= BOX;*ft += MAN;*sd += BOX;*place_man -= MAN;place_man = ft;return;break;default :return;}}void Display(){system("cls");for(int i=0;i<_X;i++){for(int j=0;j<_Y;j++){printf( "%d",map_[i][j] );}printf("\n");}
}char Win(){int boxAimNum=0;for(int i=0; i < _X; i++){for(int j=0; j < _Y; j++){if(BOX_AIM == map_[i][j]){boxAimNum++;}}}if(boxAimNum == all_aim){printf("YOU ARE WINNING");return 1;} else {return 0 ;}
}

3.4 小小美化

  我们可以通过修改 Display 函数以达到美化的目的
  美化后Display():

void Display(){system("cls");for(int i=0;i<_X;i++){for(int j=0;j<_Y;j++){switch( map_[i][j] ){case WALL:printf("M  ");break;case MAN:printf("!  ");break;case AIR:printf("   ");break;case AIM:printf("*  ");break;case BOX:printf("X  ");break;case MAN_AIM:printf("!  ");break;case BOX_AIM:printf("$  ");break;default :printf("   ")break;}}printf("\n");}
}

4联系作者

本文作者为 > 【谢玄.】 Mr-XieXuan < 于 2022/7/7/3:00 发布于 CSDN 。

E-mail: [ Mr_Xie_@outlook.com ]
GitHub: [ https://github.com/MR-XieXuan }
个人私站: [ https://main.mrxie.xyz/ ]

 如果本文对您有帮助的话,可以给本文点一个赞

C语言100行代码实现推箱子相关推荐

  1. 100行代码搞定实时视频人脸表情识别(附代码)

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达本文转自|OpenCV学堂 好就没有写点OpenCV4 + Open ...

  2. SAP系统和微信集成的系列教程之八:100行代码在微信公众号里集成地图搜索功能

    本系列的英文版Jerry写作于2017年,这个教程总共包含十篇文章,发表在SAP社区上. 系列目录 (1) 微信开发环境的搭建 (2) 如何通过微信公众号消费API (3) 微信用户关注公众号之后,自 ...

  3. 100行代码让您学会JavaScript原生的Proxy设计模式

    面向对象设计里的设计模式之Proxy(代理)模式,相信很多朋友已经很熟悉了. 其实和Java一样,JavaScript从语言层面来讲,也提供了对代理这个设计模式的原生支持.我们用一个不到100行代码的 ...

  4. 用python画苹果的logo_简单几步,100行代码用Python画一个蝙蝠侠的logo

    转自:菜鸟学Python 简单几步,100行代码用Python画一个蝙蝠侠的logo-1.jpg (35.33 KB, 下载次数: 0) 2020-7-30 12:04 上传 蝙蝠侠作为DC漫画的核心 ...

  5. python画画100行代码_简单几步,100行代码用Python画一个蝙蝠侠的logo

    原标题:简单几步,100行代码用Python画一个蝙蝠侠的logo 转自:菜鸟学Python 蝙蝠侠作为DC漫画的核心人物之一,一直都受到广大粉丝的喜爱,而笔者作为DC的铁杆粉丝,自然也是老爷(粉丝对 ...

  6. Android鬼点子 100行代码,搞定柱状图!

    最近,项目中遇到一个地方,要用到柱状图.所以这篇文章主要讲怎么搞一个柱子. 100行代码,搞定柱状图! 我的印象中柱子是这样的. 恩,简单,一个View直接放到xml,搞定! 但,设计师给的柱子是这样 ...

  7. react hooks使用_我如何使用React Hooks在约100行代码中构建异步表单验证库

    react hooks使用 by Austin Malerba 奥斯汀·马勒巴(Austin Malerba) 我如何使用React Hooks在约100行代码中构建异步表单验证库 (How I bu ...

  8. 100行代码实现最简单的基于FFMPEG+SDL的视频播放器

    简介 FFMPEG工程浩大,可以参考的书籍又不是很多,因此很多刚学习FFMPEG的人常常感觉到无从下手.我刚接触FFMPEG的时候也感觉不知从何学起. 因此我把自己做项目过程中实现的一个非常简单的视频 ...

  9. c语言300行代码大作业,C语言300行代码

    <C语言300行代码>由会员分享,可在线阅读,更多相关<C语言300行代码(3页珍藏版)>请在人人文库网上搜索. 1.include #include #include #in ...

最新文章

  1. pccs色卡_NCS色彩体系与PCCS色彩体系如何关联使用
  2. sklearn自学指南(part61)--调整估计器的超参数
  3. Python数据库访问之SQLite3、Mysql
  4. 【收集】常用网络上的Webservice地址
  5. LeetCode刷题——字符串转换整数
  6. jsp页面 字体颜色 白色_CSS 文本字体颜色(CSS color)
  7. Android学习—7种形式的Android Dialog使用举例
  8. [MS-SHLLINK]: Shell Link (.LNK) Binary File Format
  9. 最爱的文本编辑器_VS Code——插件推荐整理
  10. Centos7下载文件恢复工具 extundelete
  11. http测试工具:httpbin
  12. 威联通 ※ 群晖 虚拟机性能对比 我可能要碰瓷 eSir
  13. 图像坐标球面投影_C/C++ 图像处理(7)------图像の球面投影算法
  14. Python:RSA秘钥生成与加密解密整理
  15. 认证 (authentication) 和授权 (authorization) 的区别
  16. 冲突声明(conflicting declaration)解决
  17. Node.js 读取图片
  18. Selenium的PO模式(Page Object Model)|(Selenium Webdriver For Python)
  19. jQuery搜索高亮显示
  20. java win10窗口启动假死_win10桌面假死如何解决

热门文章

  1. 【网络安全】SQL注入专题讲解
  2. 数据库存储boolean类型数据格式
  3. ArcGIS SDE要素桌面软件无法编辑问题
  4. 〖编程初学者的自我修养 - 优质简历篇②〗- 面试官所青睐的优秀简历是什么样的?
  5. 原码、补码、反码的关系
  6. 红帽linux7如何加载驱动,在RHEL7安装过程中加载驱动程序
  7. VSAM让大型机存储性能重获新生
  8. Dijkstra算法时间复杂度分析
  9. 猫书 如何轻松拥猫入怀
  10. div 隐藏和显示方式