0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

C语言实现推箱子游戏!代码思路+源码分享

C语言编程学习基地 来源:C语言编程学习基地 作者:童晶 2022-11-29 16:22 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

经典的推箱子是一个很古老的游戏了,相信大家都不陌生。其目的是在训练我们的逻辑思考能力。在一个狭小的仓库中,要求把木箱放到指定的位置,稍不小心就会出现箱子无法移动或者通道被堵住的情况,所以需要巧妙的利用有限的空间和通道,合理安排移动的次序和位置,才能顺利的完成任务。

我们将编写推箱子游戏,玩家键盘控制游戏角色将所有黄色箱子推到白色方块处,效果如图所示:

da8b0848-6fbd-11ed-8abf-dac502259ad0.png

操作方法:方向键↑↓←→控制移动推箱子,将箱子推到对应位置。

首先学习字符串与字符数组的概念,并应用字符数组初始化关卡数据;然后利用键盘控制游戏角色移动,实现地图元素更新和游戏胜利的判断;接着利用三维字符数组,实现多关卡的游戏;最后学习基于文件的关卡数据读取,利用枚举类型改进游戏代码。

源码:

#include   
#include 
#include 
#define B_SIZE 60 // 方块大小
#define B_NUM 8 // 方块个数,一共8*8个方块


struct Player // 结构体,用于记录玩家位置
{
  int i;
  int j;
};
Player player; // 玩家全局变量


enum Element // 定义枚举类型,小方块所有的可能的种类
{
  wall,target,box,empty,achieved,role
};


// 用于存储地图数据,用枚举类型实现
Element level[B_NUM][B_NUM] = 
{{wall,wall,wall,wall,wall,wall,wall,wall},
{wall,wall,wall,target,box,empty,empty,wall},
{wall,empty,empty,empty,empty,empty,empty,wall},
{wall,empty,empty,empty,empty,empty,empty,wall},
{wall,empty,empty,empty,empty,empty,empty,wall},
{wall,role,empty,box,target,wall,wall,wall},
{wall,empty,empty,empty,empty,wall,wall,wall},
{wall,wall,wall,wall,wall,wall,wall,wall}};


int targetNum,achievedNum; // 目标位置个数、完成目标个数


void startup()  // 初始化函数
{
  initgraph(B_NUM*B_SIZE,B_NUM*B_SIZE); // 新开一个画面
  setbkcolor(RGB(150,150,150)); // 灰色背景
  BeginBatchDraw(); // 开始批量绘图
  int i,j;
  targetNum = 0; // 目标个数,初始为0
  // 对二维数组遍历
  for (i=0;i
    for (j=0;j
    {
      if (level[i][j]==role) // 找到地图中player位置
      {
        player.i = i; // 设定player位置
        player.j = j; // 
        level[i][j]=empty; // 把地图元素变成空白empty
      }
      else if (level[i][j]==target || level[i][j]==achieved ) // 如果元素是target或achieved
        targetNum++; // 目标个数+1
    }
}


void show() // 绘制函数
{
  int i,j;
  cleardevice(); // 以背景颜色清空屏幕
  // 遍历关卡二维数组数据
  for (i=0;i
  {
    for (j=0;j
    {
      if (level[i][j]==empty) // empty 元素是空白区域
      {
        setfillcolor(RGB(150,150,150)); // 绘制灰色地面
        setlinecolor(RGB(150,150,150));
        fillrectangle(j*B_SIZE,i*B_SIZE,(j+1)*B_SIZE,(i+1)*B_SIZE);
      }
      else if (level[i][j]==wall)  // wall 元素是墙
      {
        setfillcolor(RGB(155,0,0));
        setlinecolor(RGB(150,150,150)); // 绘制淡红色、灰色线的方框
        fillrectangle(j*B_SIZE,i*B_SIZE,(j+1)*B_SIZE,(i+1)*B_SIZE);
      }
      else if (level[i][j]==box) // box 元素是可移动的箱子
      {
        setfillcolor(RGB(255,255,0)); // 绘制一个黄色的方块
        setlinecolor(RGB(150,150,150));
        fillrectangle(j*B_SIZE,i*B_SIZE,(j+1)*B_SIZE,(i+1)*B_SIZE);
      }
      else if (level[i][j]==target) // target 元素是目标
      {
        setfillcolor(RGB(250,250,250)); // 绘制一个白色的小方块
        fillrectangle((j+0.3)*B_SIZE,(i+0.3)*B_SIZE,
          (j+0.7)*B_SIZE,(i+0.7)*B_SIZE);
      }
      else if (level[i][j]==achieved) // achieved 元素是已完成目标
      {
        setlinecolor(RGB(150,150,150));
        setfillcolor(RGB(255,255,0)); // 绘制一个黄色的方块
        fillrectangle(j*B_SIZE,i*B_SIZE,(j+1)*B_SIZE,(i+1)*B_SIZE);
        setfillcolor(RGB(250,250,250)); // 绘制一个白色的小方块
        fillrectangle((j+0.3)*B_SIZE,(i+0.3)*B_SIZE,
          (j+0.7)*B_SIZE,(i+0.7)*B_SIZE);
      }    
    }
  }
  // 以下绘制玩家,绘制一个人脸图案
  i = player.i; 
  j = player.j;
  setfillcolor(RGB(255,0,0));
  fillcircle((j+0.5)*B_SIZE,(i+0.5)*B_SIZE,0.4*B_SIZE);//一个红色圆脸
  setfillcolor(RGB(80,80,80));
  setlinecolor(RGB(80,80,80));
  fillcircle((j+0.3)*B_SIZE,(i+0.45)*B_SIZE,0.08*B_SIZE);//两个黑色眼睛
  fillcircle((j+0.7)*B_SIZE,(i+0.45)*B_SIZE,0.08*B_SIZE); 
  setlinestyle(PS_SOLID,3);
  line((j+0.35)*B_SIZE,(i+0.7)*B_SIZE,(j+0.65)*B_SIZE,(i+0.7)*B_SIZE);//深灰色嘴巴
  setlinestyle(PS_SOLID,1);


  if (achievedNum==targetNum) // 如完成目标个数==目标个数
  {
    setbkmode(TRANSPARENT); // 透明显示文字
    settextcolor(RGB(0,255,255)); // 设置字体颜色
    settextstyle(80, 0, _T("宋体")); // 设置字体大小、样式
    outtextxy(80,200,_T("游戏胜利")); // 显示游戏胜利文字
  }
  FlushBatchDraw(); // 开始批量绘制
}


void update()  // 每帧更新运行
{
  if(kbhit() && (achievedNum// 如果按键,并且游戏没有胜利
  {
    char input = getch(); // 获取按键
    if (input=='a' || input=='s' || input=='d' || input=='w') // 如果是有效按键
    {
      int goal_i = player.i; // 移动的目标位置
      int goal_j = player.j;
      int goalNext_i = goal_i; // 目标位置再向前的一个位置
      int goalNext_j = goal_j;
      // 根据用户的不同按键输入,获得目标位置、再向前的一个位置
      if (input=='a') // 向左
      {
        goal_j = player.j -1 ; // 目标位置在玩家位置的左边
        goalNext_j = goal_j-1; // 目标的下一个位置,在其再左边
      }
      else if (input=='d') // 向右
      {
        goal_j = player.j +1 ; // 目标位置在玩家位置的右边 
        goalNext_j = goal_j+1; // 目标的下一个位置,在其再右边
      }
      else if (input=='s') // 向下
      {
        goal_i = player.i+1; // 目标位置在玩家位置的下边 
        goalNext_i = goal_i+1; // 目标的下一个位置,在其再下边 
      }
      else if (input=='w') // 向上
      {
        goal_i = player.i-1; // 目标位置在玩家位置的上边 
        goalNext_i = goal_i-1; // 目标的下一个位置,在其再上边 
      }


      // 根据不同地图元素的情况,判断如何移动角色和更新地图元素  
      if (level[goal_i][goal_j]==empty || level[goal_i][goal_j]==target ) 
      {  // 如果目标位置是empty,或者target
        player.i = goal_i; // 玩家移动到目标位置
        player.j = goal_j;        
      }
      else if (level[goal_i][goal_j]==box && level[goalNext_i][goalNext_j]==empty ) 
      {  // 如果目标位置是box,再前面一个是empty
        player.i = goal_i; // 玩家移动到目标位置
        player.j = goal_j;    
        level[goal_i][goal_j]=empty;  // 目标位置变成empty
        level[goalNext_i][goalNext_j]=box;   // 再前面变成box
      }
      else if (level[goal_i][goal_j]==box && level[goalNext_i][goalNext_j]==target) 
      {  // 如果目标位置是box,再前面一个是target
        player.i = goal_i; // 玩家移动到目标位置
        player.j = goal_j;  
        level[goal_i][goal_j] = empty;  // 目标位置变成empty 
        level[goalNext_i][goalNext_j] = achieved;  // 再前面变成achieved
      }
      else if (level[goal_i][goal_j]==achieved && level[goalNext_i][goalNext_j]== empty) 
      {  // 如果目标位置是achieved,再前面一个是empty
        player.i = goal_i; // 玩家移动到目标位置
        player.j = goal_j;    
        level[goal_i][goal_j] = target;  // 目标位置变成target 
        level[goalNext_i][goalNext_j] = box; // 再前面变成box   
      }
      else if (level[goal_i][goal_j]==achieved && level[goalNext_i][goalNext_j]== target) 
      {  // 如果目标位置是achieved,再前面一个是target
        player.i = goal_i; // 玩家移动到目标位置
        player.j = goal_j;
        level[goal_i][goal_j] = target;  // 目标位置变成target  
        level[goalNext_i][goalNext_j] = achieved; // 再前面变成achieved    
      }
      else // 其他情况都推不动
        return; // 不做任何处理,函数直接返回
    }
    achievedNum = 0; // 完成目标个数,初始为0
    int i,j;
    for (i=0;i// 对二维数组遍历
      for (j=0;j// 
        if (level[i][j]==achieved) // 如果元素是achieved
          achievedNum++; // 完成目标个数+1
  }
}


int main() // 主函数
{
  startup();  // 初始化  
  while (1)  // 游戏主循环
  {
    show();    // 绘制
    update();   // 更新
  }
  return 0;
}

主要讲解了字符串与字符数组、文件读写、枚举类型等语法知识,实现了推箱子游戏。读者可以尝试在本章代码基础上继续改进:

1、实现多关卡的选择界面;

2、实现某一步移动的撤销功能(类似于下棋游戏中的悔棋功能);

3、实现按'h'键后进行提示,播放正确步骤动画的功能;

4、实现一个图形编辑器,并将设计的关卡信息保存为txt文件。

审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C语言
    +关注

    关注

    183

    文章

    7642

    浏览量

    144636
  • 编辑器
    +关注

    关注

    1

    文章

    826

    浏览量

    32653

原文标题:C语言实现推箱子游戏!(超简单详细)代码思路+源码分享

文章出处:【微信号:cyuyanxuexi,微信公众号:C语言编程学习基地】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    为什么单片机还在用C语言编程?

    的存储空间非常有限,我们使用者需要靠精打细算来设计程序,根本经不起高级语言臃肿的代码体积。高级语言也无法实现精确的时序控制。 三、C
    发表于 11-28 07:37

    C语言的编程技巧

    一个成员是一个未知大小的数组,适用于动态分配内存并关联一个可变长度的数组。‌ ‌3、匿名结构体和联合体‌:C语言允许在结构体或联合体中定义不带标签的内部结构体或联合体,简化代码结构。 ‌4
    发表于 11-27 06:46

    C语言特性

    1、高效性:直接操作硬件 C 语言代码的执行效率极高,这是其最为显著的优势之一。它能够直接访问硬件资源,与底层硬件进行紧密交互,充分发挥硬件的性能潜力。在嵌入式开发中,硬件资源往往十分有限,对程序
    发表于 11-24 07:01

    C语言和单片机C语言有什么差异

    的目标代码短、运行速度高、存储空间小、符合C语言的ANSI标准,生成的代码遵循Intel目标文件格式,而且可与A51汇编语言PL/M51
    发表于 11-14 07:55

    自写计算IP思路以及源码

    、16位、32位并行加法器,以32位加法器实现串行64位加法器再用于乘法器中,以下是1位并行加法器源码。 PS.在组合加法器和乘法器实现乘加器功能时要注意乘法器的流水线结构,即对输入的加数
    发表于 10-30 06:15

    【EASY EAI Orin Nano开发板试用体验】使用stream代码和WEB服务器代码实现在客户端网页上查看摄像头图像

    本帖最后由 donatello1996 于 2025-8-13 20:32 编辑 【EASY EAI Orin Nano开发板试用体验】使用stream代码和WEB服务器代码实现
    发表于 08-11 23:15

    【经验分享】玩转FPGA串口通信:从“幻觉调试”到代码解析

    FPGA开发,思路先行!玩FPGA板子,读代码是基本功!尤其对从C语言转战FPGA的“宝贝们”来说,适应流水线(pipeline)编程可能需要点时间。上篇点灯
    的头像 发表于 06-05 08:05 886次阅读
    【经验分享】玩转FPGA串口通信:从“幻觉调试”到<b class='flag-5'>代码</b>解析

    单片机C语言实例(350+例)

    350+单片机C语言实例! 纯分享帖,需要者可点击附件免费获取完整资料~~~【免责声明】本文系网络转载,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请第一时间告知,删除内容!
    发表于 05-22 21:47

    深入理解C语言C语言循环控制

    C语言编程中,循环结构是至关重要的,它可以让程序重复执行特定的代码块,从而提高编程效率。然而,为了避免程序进入无限循环,C语言提供了多种循
    的头像 发表于 04-29 18:49 1740次阅读
    深入理解<b class='flag-5'>C</b><b class='flag-5'>语言</b>:<b class='flag-5'>C</b><b class='flag-5'>语言</b>循环控制

    单片机C语言编程宝典大全,初学必备【强推下载!】

    资料介绍:包含 1. 51单片机入门教程 2. 初学者适用:单片机C语言实例100例(代码及释义) 3. 51单片机C语言快速上手 4. 5
    发表于 03-14 11:03

    限时免积分下载:增量式与位置式PID算法的C语言实现分享

    前面咱们有分享对PID算法离散化和增量式PID算法原理进行来探索,之后又使用Matlab进行了仿真实验,对PID三个参数又有了更深入的认识,接下来我们来使用C语言进行PID算法实现,并且结合控制电机的项目来深入学习。 今天限时免
    发表于 03-05 18:32

    代码加密、源代码防泄漏c/c++与git服务器开发环境

    代码加密对于很多研发性单位来说是至关重要的,当然每家企业的业务需求不同所用的开发环境及开发语言也不尽相同,今天主要来讲一下c++及git开发环境的源代码防泄密保护方案。企业源
    的头像 发表于 02-12 15:26 879次阅读
    源<b class='flag-5'>代码</b>加密、源<b class='flag-5'>代码</b>防泄漏<b class='flag-5'>c</b>/<b class='flag-5'>c</b>++与git服务器开发环境

    分析C语言代码结构的设计问题

    来分析一个C语言代码结构的设计问题。 这段代码,使用了两次malloc,分别给 p1 和 p2 申请了内存。用完后,内存释放,防止内存泄漏。 大家觉得,这样的
    的头像 发表于 02-11 09:31 678次阅读

    AKI跨语言调用库神助攻C/C++代码迁移至HarmonyOS NEXT

    )开发框架。它极大地简化了JS与C/C++之间的跨语言访问,为开发者提供了一种边界性编程体验友好的解决方案。通过AKI,开发者可以使用让代码更易读的语法糖,
    发表于 01-02 17:08

    基于FPGA实现图像直方图设计

    直方图统计的原理 直方图统计从数学上来说,是对图像中的像素点进行统计。图像直方图统计常用于统计灰度图像,表示图像中各个灰度级出现的次数或者概率。统计直方图的实现采用C/C++或者其他高级语言实
    的头像 发表于 12-24 10:24 1210次阅读
    基于FPGA<b class='flag-5'>实现</b>图像直方图设计