你好,亲爱的朋友们,你好!
好久没用C语言写小游戏了。今天,我无事可做。我们开始吧。过程相当流畅,代码也相对简单。今天分享给大家~
一、介绍
开发语言:C语言
开发工具:VS2022/2019,VScode,Dev-C++都有(如果没有VS,可以在文末获取)
不多说多余的话,先来看看效果图:
游戏中的人物、箱子、墙壁、球都是由人物组成的。通过wasd键移动,规则就是推箱子的规则,就不多说了。
二、代码实现
就代码而言,我尽量做到详细。希望大家能理解~
不想动的好兄弟也可以直接获取源代码(但不建议),你可以在文末获取!
(1)方法列表
//主函数
void main();
//初始化一些数据
initData();
//在控制台上打印地图
draw map();
//上移
move up();
//向左移动
向左移动()
//向下移动
下移()
//向右移动
moveRight();
这些方法都是顾名思义,用意非常明确。只是initData可能不知道具体用途,但是没有大问题。唯一的问题是上下左右的顺序可能会害死几个强迫症患者,哈哈。
(2)参数表
为了方便起见,我将include和宏定义放在了参数列表中。
//导入函数库
# include & ltstdio.h & gt
# include & ltstdlib.h & gt
# include & ltconio.h & gt
//宏定义
#定义宽度8
#定义高度8
//定义映射数组。二维数组有二维,地图也是二维矩形。
int map[HEIGHT][WIDTH] = {
{0, 0, 1, 1, 1, 0, 0, 0},
{0, 0, 1, 4, 1, 0, 0, 0},
{0, 0, 1, 0, 1, 1, 1, 1},
{1, 1, 1, 3, 0, 3, 4, 1},
{1, 4, 0, 3, 2, 1, 1, 1},
{1, 1, 1, 1, 3, 1, 0, 0},
{0, 0, 0, 1, 4, 1, 0, 0},
{0, 0, 0, 1, 1, 1, 0, 0}
};
//人的位置,在二维地图中,我们可以用坐标来表示一个人的位置,就像经纬度一样。
int x,y;
//箱子的数量,推箱子的时候一定要有箱子。
int boxs
这里参数不多,其中横坐标为X,纵坐标为y,另外,这里还有一些关于map的事情:
/**
* 0表示空
* 1表示墙壁。
* 2表示人
* 3表示一个盒子。
* 4表示目的地(球)
* 5表示完成的盒子。
*/
(3)功能的具体分析
接下来,让我们一次分析一个函数。
1.主要功能
int main(int argc,char *argv[]) {
充电方向;//存储键盘按下的方向。
initData();//初始化一些数据
//开始游戏的循环。这是一个无止境的循环,每按一次按钮就会循环一次。
while(1){
//在每个周期开始时清除屏幕
系统(& # 34;cls & # 34);
//画一张地图
draw map();
//判断,当盒数为0时!0为真,然后跳出循环(结束游戏)
如果(!方框){
打破;
}
//键盘输入方向,这里用getch,因为getch读取的字符不会显示在屏幕上。
direction = getch();
//使用开关判断用户输入的方向
开关(方向){
案例& # 39;w & # 39:
//按下W时,调用move up函数。
move up();
打破;
案例& # 39;一& # 39;:
//按A时,调用左移函数。
move left();
打破;
案例& # 39;s & # 39:
move down();
打破;
案例& # 39;d & # 39:
moveRight();
打破;
}
}
//跳出循环后,运行这条语句,游戏就结束了。
printf(& # 34;祝贺你完成游戏!※");
返回0;
}
我来说说过程。循环之外没什么特别的。InitData()只是一些简单数据的初始化,不用太在意。循环的一般过程如下:
清除屏幕
画一张地图
判断游戏是否结束。
反馈用户按下的按钮。
进入循环,先清屏,再画地图,然后判断游戏是否结束。可能大家都不是很理解这个顺序,所以这里先不考虑判定游戏结束的问题。我们把清屏和绘制地图结合在一起,简称为“重绘地图”,没有先考虑游戏结束的判断,所以把流程简化为“重绘地图+响应用户操作”。简单来说,用户按下按钮,我改变地图。
2、initData()
void initData(){
int i,j;
//加载数据时,让用户等待。一般来说,加载数据更快。
printf(& # 34;游戏正在加载,请稍候…");
//遍历地图中的数据
for(I = 0;我& lt身高;i++){
for(j = 0;j & lt宽度;j++){
//当遍历到2(人)时,记录人的坐标。x和y是前面定义的全局变量。
if(map[i][j] == 2){
x = j;
y = I;
}
//当遍历到3时,盒数增加。Boxs是前面定义的全局变量。
if(map[i][j] == 3){
box s++;
}
}
}
}
这个方法很简单,就是遍历地图,然后初始化人的位置和箱子的数量。这里需要注意的一点是,内环是宽度还是外环是宽度。
如图,遍历过程中。外部循环控制行数,即高度。那么内环应该是宽度。
3、绘制地图()
void drawMap(){
int i,j;
for(I = 0;我& lt宽度;i++){
for(j = 0;j & lt身高;j++){
开关(映射[i][j]){
案例0:
printf(& # 34;");
打破;
案例1:
printf(& # 34;■");
打破;
案例二:
printf(& # 34;♀");
打破;
案例三:
printf(& # 34;◆");
打破;
案例4:
printf(& # 34;●");
打破;
案例5:
printf(& # 34;★");
打破;
}
}
printf(& # 34;\ n & # 34);
}
}
这里也很简单,在map中可变元素,然后通过switch判断应该输出什么。然后内部循环在每次结束时循环。
4、moveUp()
这个函数的内容很多,所以我想说一下大概的思路:
晋升有两种方式。
1.正面是空白色
这种情况下有两个步骤。
(1)将一个人的当前位置设置为空 white (0),
(2)假设人前面的位置被设置为人(2)
2.前面是一个盒子
前面是盒子有三种情况。
1.盒子正面是空白色。
移动人和箱子有三个步骤。
(1)将人的当前位置设置为空(0)
(2)将框位置设置为person (2)
(3)将盒子的正面设置为盒子(3)
2.盒子的前面是一面墙
在这种情况下,不需要做任何事情。
3.盒子的前面是终点
这种情况下有四个步骤。
(1)将人的位置设置为空(0)
(2)将方框的位置设置为一个人(2)
(3)将结束位置设置为★ (5)
(4)盒箱数量减少一个。
3.前面是一堵墙
这种情况是最简单的,什么都不需要做。
4.前面是终点
在这里我不会想太多,所以这种情况我什么都不做。(如果更改地图,可能需要修改代码)
具体代码如下,我都写在评论里了:
void moveUp(){
//定义存储在变量中的字符上方的坐标。
int ux,uy;
//上面没有元素的时候直接返回(其实人不可能在边上)
if(y == 0){
返回;
}
//记录上面的坐标,其中X为水平,Y为垂直,所有ux = x,uy = Y-1;
UX = x;
uy = y-1;
//完成的盒子在上面。
if(map[uy][ux] == 5){
返回;
}
//如果上面有墙,直接返回。这个可以和上面的判断结合起来,这里为了清楚起见单独写出来。
if(map[uy][ux] == 1){
返回;
}
//假设上面有一个盒子。
if(map[uy][ux] == 3){
//判断盒子上方是否有墙。
if(map[uy – 1][ux] == 1){
返回;
}
//判断盒子顶部是否为终点。
if(map[uy – 1][ux] == 4){
//将框中的内容赋给5★
map[uy-1][UX]= 5;
map[uy][UX]= 0;
//盒子数减1。
方框-;
}否则{
//移动盒子
map[uy-1][UX]= 3;
}
}
//以上回报情况都没有遇到的时候,人肯定会动的。移动操作如下。
map[y][x]= 0;
map[uy][UX]= 2;
//更新人的坐标
y = uy
}
这是一个方向,其他方向要考虑的问题和之前一样,就不赘述了。
6、向左移动()
这里大致和上面一样,就是记录左坐标的时候,应该是lx = x-1。
void moveLeft(){
//定义变量来存储字符左侧的坐标。
int lx,ly;
//左边没有元素时,直接返回。
if(x == 0){
返回;
}
//记录左边的坐标
LX = x-1;
ly = y;
//左边是完成的方框。
if(map[ly][lx] == 5){
返回;
}
//假设左边是墙,直接返回。
if(map[ly][lx] == 1){
返回;
}
//假设左边有一个盒子。
if(map[ly][lx] == 3){
//判断盒子左侧是否是墙。
if(map[ly][lx – 1] == 1){
返回;
}
//判断盒子左边是否是球。
if(map[ly][lx – 1] == 4){
//将框的左边内容赋给5★
map[ly][LX-1]= 5;
map[ly][LX]= 0;
//盒子数减1。
方框-;
}否则{
//移动盒子
map[ly][LX-1]= 3;
}
}
map[y][x]= 0;
map[ly][LX]= 2;
x = lx
}
7、下移()
这里判断边界的时候,判断是y == HEIGHT-1。
void moveDown(){
//定义存储变量的字符下方的坐标。
int dx,dy;
//下面没有元素时,直接返回。
if(y == HEIGHT – 1){
返回;
}
//记录下面的坐标
dx = x;
dy = y+1;
//下面是完成的方框。
if(map[dy][dx] == 5){
返回;
}
//假设下面有墙,直接返回。
if(map[dy][dx] == 1){
返回;
}
//假设下面有一个盒子。
if(map[dy][dx] == 3){
//判断盒子下面是否有墙。
if(map[dy + 1][dx] == 1){
返回;
}
//判断盒子下面是否有球。
if(map[dy + 1][dx] == 4){
//将框下面的内容赋值为5★
map[dy+1][dx]= 5;
map[dy][dx]= 0;
//盒子数减1。
方框-;
}否则{
//移动盒子
map[dy+1][dx]= 3;
}
}
map[y][x]= 0;
map[dy][dx]= 2;
y = dy
}
8、moveRight()
这里没什么特别要说的:
void moveRight(){
//定义变量来存储字符右侧的坐标。
int rx,ry;
//当右边没有元素时,直接返回。
if(x == WIDTH – 1){
返回;
}
//记录右边的坐标
rx = x+1;
ry = y;
//完成的框在右边。
if(map[ry][rx] == 5){
返回;
}
//假设右边是墙,直接返回。
if(map[ry][rx] == 1){
返回;
}
//假设盒子在右边。
if(map[ry][rx] == 3){
//判断盒子右侧是否为墙壁。
if(map[ry][rx + 1] == 1){
返回;
}
//判断盒子左边是否是球。
if(map[ry][rx + 1] == 4){
//将框右侧的内容赋值为5★
map[ry][rx+1]= 5;
map[ry][rx]= 0;
//盒子数减1。
方框-;
}否则{
//移动盒子
map[ry][rx+1]= 3;
}
}
map[y][x]= 0;
map[ry][rx]= 2;
x = rx
}
三、总结
现在我们来回顾一下最初的操作步骤。
清除屏幕
画一张地图
判断游戏是否结束。
反馈用户按下的按钮。
这里把判断游戏是否结束放在重绘图像的后面,因为反馈给用户的只是地图中的数据发生了变化,实际上最后一个推到最后的盒子的图像还没有显示出来,所以重绘后需要判断是否结束游戏。
代码有很多冗余,一方面是想让大家更好的理解,另一方面也是我懒。哈哈,代码运行的时候没有问题。我会上传源代码和源程序。有兴趣可以下载,或者直接复制代码运行也没问题。
需要完整源代码对比的同学可以在文末获取!
推箱子教程到此结束。赶紧来试试吧!
源码素材获取通道:
可以关注边肖,后台私信我:【编程交流】哦!
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。