一步一步教你开发《松鼠推箱子》手机游戏

这类游戏大家肯定都玩过,一个很有趣味性的小游戏。操作简单,具有一定的逻辑性。很适合无聊的时候消遣时间。:)

首先简单介绍下手机游戏的一般性开发过程。首先需要策划出一个游戏方案,也就是要给出一个游戏的整体形象。当然是想象中的。比如:游

戏题材,背景,操作方法,人物,与奖励机制。然后对这个策划方案进行可行性分析,包括技术,市场,可用资源等。下一步就是编码阶段,

一般编码和制作资源可以同步进行。 等程序和资源都弄好了,一个游戏的雏形就大致出来了。后面就是测试与移植等等工作。


我们要做的是让一只小松鼠推箱子,操作方法就是通过四方向键对松鼠进行上,下,左,右的移动。在移动过程中可以把碰到的箱子推到任意

一个方向。当然在游戏中需要设置一些障碍物,让游戏具备可玩性。游戏采用过关制,当松鼠把所有箱子推到指定的位置就算过关。所以每关

的场景要不同。这需要对每关的地图进行编排。好了,我们的游戏策划就算完成,嘿嘿!简单吧,想象是多么美好啊。


下面就没这么轻松了,我们要进行技术分析。就是具体的代码如何来实现的。首先我们来确定一下开发难点。对松鼠的操作很简单,就是四方

向移动,松鼠移动,箱子也移动,所以对按键处理也比较简单些。当箱子到达某个位置时,就会产生游戏过关事件。需要一个逻辑判断。那么

我们仔细想一下,这些所有的事件都发生在一张地图中。这张地图就包括了 箱子的初始化位置,箱子最终放置的位置,和障碍等。每一关地图

都要更换。这些位置也要变。所以我们发现每关的地图数据是最关键的。它决定了每关的不同场景和物体位置。好。那么我们就重点分析一下

地图。

我们把地图想象成一个网格,每个格子就是松鼠每次移动的步长,也是箱子移动的距离,这样问题就简化多了。首先我们设计一个8*8的数据结

构。按照这样的框架来思考。 每个格子都会有哪些属性呢?首先就是格子的坐标,包括X,Y两个数值,还有一些地图的属性,比如这个格子是

否为障碍,是否为初始化的箱子位置,是否为箱子终点的位置。由于我们的数据结构是二维的,但是还有一维表示不出来,所以我们设计一个

三维数据:如下:

private static int[][][] map_data = new int[8][8][5]; 
//第一维  -  [8] 表示地图的高,由8个格子组成
               
//第二维 -  [8] 表示地图的宽,由8个格子组成
                

//第三维  - [0] 每个格子的X坐标
               
//        - [1] 每个格子的Y坐标
               
//        - [2] 是否为障碍 0表示空地 1表示障碍
               
//        - [3] 箱子的终点坐标,0不是终点坐标 1绿箱子 2 红箱子
               
//        - [4] 箱子的初始化位置 [0]不是初始化坐标  [1]绿箱子 2红箱子

地图大致就是这样的数据结构,我们设计两种颜色的箱子,这样增加一下游戏的难度。


好了,有了地图,我们的逻辑就可以实现了。利用一点时间,我把游戏的图片画好了,好在我是美术专业毕业,画这些小像素图还挺顺利。

^+^.

我们先把游戏的界面部分处理一下。界面就是菜单,进度条,还有游戏中的绘制。 菜单和进度条略过,因为这些可以自己设计的。

下面是画地图和松鼠.箱子以及指示终点位置的方法。

//画地图
private void drawMap(Graphics g)
{
  

 for(int m = 0 ; m < map_data.length ; m ++ ) // Y坐标
 {
  for(int n = 0 ; n < map_data[m].length; n++) // X坐标
  {
    
    
   if(map_data[n][m][2] == 1) //如果是空地
   {
    g.drawImage(map[0],map_data[n][m][0],map_data[n][m][1],0);
      

   }else if(map_data[n][m][2] == 0) //如果是障碍
   {
    g.drawImage(map[1],map_data[n][m][0],map_data[n][m][1],0);     

 
      
   }

   
  }
   
 }

}

map数组是只有两个元素的图片数组,里面有两幅图,分别为空地和障碍。


//画松鼠
private void drawSqu(Graphics g)
{
 g.drawImage(squirrel[sdir],map_data[( splace[1] )][( splace[0] )][0],map_data[( splace[1] )][( splace[0] )][1],0);
}


这里的splace[] 是松鼠的初始化位置,这个随每关的不同,也会有变化。
sdir 表示松鼠的方向,对应着squirre[] 数组中的松鼠图片

//画箱子
private void drawBoxs(Graphics g)
{
 for(int i = 0; i < nplace.length; i++)
 {
  g.drawImage(nut[(nplace[i][2]-1)],map_data[(nplace[i][1])][(nplace[i][0])][0],map_data[(nplace[i][1])]

[(nplace[i][0])][1],0);
  
 }
}

nplace[][]  是箱子的初始化位置坐标,第一维表示有几个箱子,第二维表示 X,Y坐标 和 颜色 属性
nut[] 是箱子图片数组

//画箱子终点位置的指示
private void drawNutDest(Graphics g)
{
 for(int i = 0; i < ndplace.length; i++)
 {
  g.drawImage(nut[(ndplace[i][2]+1)],map_data[(ndplace[i][1])][(ndplace[i][0])][0],map_data[(ndplace[i][1])]

[(ndplace[i][0])][1],0);
 }
}

我们对箱子重点的指示也采用图片,这样使画面效果更美观一些。
同样 ndplace[][]  和 nplace[][]  的属性都一样。 只不过表示终点坐标
nut[]数组的另外元素表示 指示重点坐标的图片。

 

以上就是我们的游戏主界面。看着挺简单的吧。呵呵。下面要实现操作控制方法与判断输赢的逻辑了。嘿嘿,要有耐心。

前面我们提到,当移动松鼠时,箱子就跟着动,那么我们就分别写两个方法,来移动他们。

/*移动松鼠*/
private static final void moveSqu()
{ 
 

 /*表示在松鼠的移动方向是否有箱子*/
 boolean hit = false ;
  
 /*指示一个箱子可以被移动*/
 boolean mf = false ;
  
 /*当为向上移动时*/
 if( moveS[0] )
 { 
  
  /*当向上移动时,调整图片为第二张松鼠图*/
  sdir = 2 ;  
   
  /*将可移动表示设置为假*/
  moveS[0] = false;
   
   
  /*查找是否有箱子在松鼠傍边*/
  for(int r=0; r < nplace.length; r++) 
  {
   /*如果有箱子*/
   if( ((splace[1] - 1) == nplace[r][1]) && (splace[0]  == nplace[r][0] )){
     
    /*移动箱子,并返回一个判断,表示是否可以继续被移动*/
    mf = moveBoxs(0,r);
    
     
    /*设置碰撞标志*/
    hit = true ;
   }
    
  }
  
  /*如果傍边没箱子,是空地*/
  if(  (map_data[(splace[1] - 1)][(splace[0])][2] == 0 ) && (!hit ) ) 
  {  
    /*移动松鼠*/
    splace[1] -= 1 ;
     
  }
  /*如果碰到箱子*/
  if(hit)
  {
   /*并且这个箱子可以被推动*/
   if(mf){
    /*移动松鼠*/
    splace[1] -= 1 ;
    /*设置碰撞为否*/
    hit = false ;
   }
  }
   
 }
 /*向下方向*/
 if( moveS[1] )
 {
  //debug("down");
  sdir = 3;
  moveS[1] = false;
   
  for(int r=0; r < nplace.length; r++)
  {
   if( ((splace[1] + 1) == nplace[r][1] ) && ( splace[0]  == nplace[r][0] )){
    mf = moveBoxs(1,r);
    hit = true ;      
   }
    
  }
  if( (map_data[(splace[1] + 1)][(splace[0])][2] == 0 ) && (!hit))
  {
   splace[1] += 1 ; 
    
  }
  if(hit)
  {
   if(mf)
   {
    splace[1]+=1;
    hit = false ;
   }
  }
   
   
   
   
 }
 /*向左方向*/
 if( moveS[2] )
 {
  //debug("left");
  sdir = 0;
  moveS[2] = false;
   
  for(int r=0; r < nplace.length; r++)
  {
   if( ((splace[0] - 1) == nplace[r][0]) && ( splace[1] == nplace[r][1] )){
    mf = moveBoxs(2,r);
    hit = true ;      
   }    
    
  }
  if( (map_data[(splace[1])][(splace[0] - 1)][2] == 0 ) && (!hit)){
   splace[0] -= 1 ;
    
  }
  if(hit)
  {
   if(mf)
   {
    splace[0] -= 1 ;
    hit = false;
   }
  }
   
   
 }
 /*向右方向*/
 if( moveS[3] )
 {
  //debug("right");
  sdir = 1 ;
  moveS[3] = false;
  for(int r=0; r < nplace.length; r++)
  {
   if( ((splace[0] + 1) == nplace[r][0] ) && ( splace[1] == nplace[r][1] )){
    mf = moveBoxs(3,r);
    hit = true ;
   }
    
  }
  if( (map_data[(splace[1])][(splace[0] + 1)][2] == 0) && (!hit)) {
   splace[0] += 1 ;
    
  }
  if(hit)
  {
   if(mf)
   {
    splace[0] += 1 ;
    hit = false ;
   }
  }
  
   
 }

  

}

上面这个方法看起来比较长,其实逻辑比较简单,就是分别对应四个方向的移动来判断。可以看向上方向移动的注释。moveS[]是个blooean数

组,用来表示松鼠的移动方向,这个数组会在keyPressed()被赋值,当某方向键按下时对应方向的标志就设置为真。下面的moveBoxs() 方法,是

判断某个箱子在被推动的方向上是否还有其它箱子阻碍,如果有就返回true.否则false.并且移动这个箱子。

 

/*
*
*方法功能:移动箱子
*
*参数: dir:被推动的方向 . nm: 表示哪个箱子
*
*返回值: 该箱子是否可以被推动
*/
private static final boolean moveNuts(int dir,int nm)
{
 /*指示另外的箱子在该箱子傍边*/
 boolean hit = false;
  
 /*临时变量*/
 boolean mf = false ;
 
 switch(dir)
 {
  case 0: //'up'

   /*对所有箱子坐标遍历*/
   for(int i = 0 ; i < nplace.length ; i++)
   {
    /*使用坐标判断,如果当前箱子要移动的方向上有其他箱子那么就标志上*/
    if( ((nplace[nm][1] - 1) == nplace[i][1] ) && ( nplace[nm][0] == nplace[i][0]))
     hit = true ;

   }
   /*如果为空地*/
   if( (map_data[(nplace[nm][1] - 1)][(nplace[nm][0])][2] == 0 ) && (!hit) )
   {
    /*移动这个箱子,并且将返回值设置为可以移动*/
    nplace[nm][1] -= 1 ;
    mf = true ;
   }
     
  break;
  case 1: //'down'
   for(int i = 0 ; i < nplace.length ; i++)
   {
    if( ((nplace[nm][1] + 1) == nplace[i][1] ) && ( nplace[nm][0] == nplace[i][0]))
     hit = true ;

   }
   if( (map_data[(nplace[nm][1] + 1)][(nplace[nm][0])][2] == 0) && (!hit) )
   {
    nplace[nm][1] += 1 ;      
    mf = true ;
   }
  break;
  case 2: //'left'
   for(int i = 0 ; i < nplace.length ; i++)
   {
    if( ((nplace[nm][1]) == nplace[i][1] ) && ( (nplace[nm][0] - 1) == nplace[i][0]))
     hit = true ;
   }
   if( (map_data[(nplace[nm][1])][(nplace[nm][0] - 1)][2] == 0) && (!hit))
   {
     nplace[nm][0] -= 1 ;
     mf = true ;
   }
  break;
  case 3: //'right'
   for(int i = 0 ; i < nplace.length ; i++)
   {
    if( ((nplace[nm][1]) == nplace[i][1] ) && ( (nplace[nm][0] + 1) == nplace[i][0]))
     hit = true ;

   }
     
   if( (map_data[(nplace[nm][1])][(nplace[nm][0] + 1)][2] == 0) && (!hit) )
   {
    nplace[nm][0] += 1 ;      
    mf = true ;
   }
  break;
 }

 return mf ;
}


这个方法跟判断松鼠是否可以移动原理一样,其实完全可以和移动松鼠的方法结合一起来处理,而且可以优化很多东西,但为了逻辑更清晰,

就分开来做。


/*判断过关*/
private static final boolean isWin()
{
 boolean temp = false;
 int nn = 0;
 for(int r = 0 ; r < nplace.length ; r++)
 {
  for(int m = 0 ; m < ndplace.length ; m++)
  {
   if( (nplace[r][0] == ndplace[m][0] ) && (nplace[r][1] == ndplace[m][1]))
    nn++;
  }
 }

 if(nn == nplace.length)
  temp = true;
 else
  temp = false;

 return temp;
}

判断过关的逻辑很简单,就是对目的地坐标与当前箱子坐标一一对照,如果所有箱子都已经被正确推到目的地,就算过关。


到现在,我们的游戏界面,和游戏逻辑实现就都完成了,感觉是不是很简单。呵呵,当然要完成整个游戏,还要写一些固定的方法。比如

keyPressed();获得按键值  paint() 来画图  线程run() 来实现刷屏幕等等。由于篇幅有限就不一一介绍了。下面附上一幅游戏完成的截图。

看看效果吧。

代码与图片下载:moveBox.rar 

《松鼠推箱子》游戏截图

程序设计与图片制作:关文柏       Email:k7sem_88@hotmail.com

 

 

[新手必备]Unity推箱子小游戏C#代码详解(第一篇-代码部分) 完整项目请参考博客:https://blog.youkuaiyun.com/qq_41676090/article/details/96300302 本文为推箱子小游戏C#代码详解第一篇的代码部分,主要讲解 System.IO(数据存储) MapGenerator(地图创建) 另外一篇文章将讲解这些代码怎么在Unity中使用 首先,给大家提个问题,什么是IO? IO就是Input... 阅读详情

相关推荐

推箱子C#源代码

推箱子 C# 很好玩的游戏和贪吃色 俄罗斯方块 一样的代码思想 希望对你有所帮助

JAVA 实现《推箱子》游戏

JAVA 实现《推箱子》游戏

毅慎的博客 2万+

Unity3D-打箱子游戏源码

基于Unity3D开发的一个简单的打箱子游戏,适合新手学习。

松鼠推箱子(BFS) C++实现

一格附近如果有大于3个格子(除了目标点和起始点),都可以填掉,可以大大减少时间复杂度。我们会发现松鼠拓展的点之间有重复部分,所以需要把每一次拓展记录,减小拓展树。由松鼠拓展出第一层,第二层,......宽搜部分不细讲了,不懂得可以观看。

BCSP_6的博客 730

unity推箱子完整项目源码

以前自己用Flash做的小游戏重新用Unity复刻了一次,加强了美术效果以及优化了关卡 游戏名:「推箱子 3D」「Sokoban 3D 开发引擎:Unity

算法实现题 推箱子问题

´问题描述: 码头仓库是划分为n×m个格子的矩形阵列。有公共边的格子是相邻格子。当前仓库中 有的格子是空闲的;有的格子则已经堆放了沉重的货物。由于堆放的货物很重,单凭仓库管 理员的力量是无法移动的。仓库管理员有一项任务,要将一个小箱子推到指定的格子上去。 管理员可以在仓库中移动,但不能跨过已经堆放了货物的格子。管理员站在与箱子相对的空 闲格子上时,可以做一次推动,把箱子推到另一相邻的空闲格子。推箱时只能向管理员的对 面方向推。由于要推动的箱子很重,仓库管理员想尽量减少推箱子的次数。 ´编程任务: 对于给定的仓库布局,以及仓库管理员在仓库中的位置和箱子的开始位置和目标位置, 设计一个解推箱子问题的分支限界法, 计算出仓库管理员将箱子从开始位置推到目标位置所 需的最少推动次数。 ´数据输入: 由文件input.txt提供输入数据。输入文件第 1 行有 2个正整数 n和 m(1<=n,m<=100) , 表示仓库是n×m个格子的矩形阵列。接下来有 n行,每行有 m个字符,表示格子的状态。 S 表示格子上放了不可移动的沉重货物; w 表示格子空闲; M 表示仓库管理员的初始位置; P 表示箱子的初始位置; K 表示箱子的目标位置。 ´结果输出: 将计算出的最少推动次数输出到文件 output.txt。如果仓库管理员无法将箱子从开始位 置推到目标位置则输出“No solution!” 。 输入文件示例 输出文件示例 input.txt output.txt

推箱子游戏C++实现原理

通过WSAD控制箱子上下左右移动,Q退出程序。  { '*',' ','O','#','@' }分别表示墙,空白,方块,目标地,人,可以根据自己喜欢重新设置 地图为7行10例,可以根据自己喜欢调整地图形式: char map[7][11] = { "**********",                         "**     ***",                      ...

SingWeek 4646

Unity和C#-游戏开发-3d推箱子+源代码工程

Unity和C#-游戏开发-3d推箱子+源代码工程

Unity游戏源码分享-3d机器人推箱子游戏

Unity游戏源码分享-3d机器人推箱子游戏。一个非常意思的3D游戏。

蒙双眼看世界 818

Unity和C#-游戏开发-2d推箱子+源代码工程

Unity和C#-游戏开发-2d推箱子+源代码工程

CPU系统级验证——测试激励——imperas公司riscvOVPsimPlus文件分析

本分析重点关注该repo向量扩展部分。 1. repo简介 该repo是指令集验证的仿真环境包括,大部分是指令集的测试用例,也包含了一些模拟器,如riscvOVPsimPlus。 主要介绍的是riscvOVPsimPlus指令集模拟器,riscvOVPsimPlus(下面统称ISS)是imperas公司在riscvOVPsim基础上扩充了向量扩展的指令集模拟器,该repo于今年9月份部分开源 什么叫部分开源呢? 首先该repo必须在imperas官网注册下载,而且必须是企业用户(免费邮箱人家瞧不上.

知识,既贵,又便宜。 4460

2006年6月"每日一帖"获奖名单

2006年6月29日[每日一帖]获奖文章:【SUN】使用Java实现数据报通讯过程Blog网址:http://blog.youkuaiyun.com/sunyycxy/2006年6月28日[每日一帖]获奖文章:【pwlazy】深入分析dwr中的DWRUtil.$ Blog网址:http://blog.youkuaiyun.com/pwlazy/2006年6月27日[每日一帖]获奖文章:【absurd】 大内高手—

每日一帖编辑部 1562

3U 与 SV630A 伺服实现 CANLINK 通讯

SD410这组参数特别重要,这个32位特别重要,是防止数值乱码的关键。

weixin_42714122的博客 2705

请用unity写一个推箱子的代码

首先,您需要创建一个Unity项目并在场景中创建一个平面。接着,创建一个箱子游戏对象,并将其设置为静态物体。随后,您可以创建一个玩家对象,并为其添加移动脚本。 以下是代码示例: using UnityEngine; public class PlayerController : MonoBehaviour { public float speed = 10.0f; private...

weixin_42601134的博客 487

[LaTeX] 调整参考文献的格式(References),包括作者名缩写,行距,字体,引用顺序等等

在此记录我对LaTeX中参考文献references的尝试,主要涵盖如题几个方面。

the Blog of 等不到天亮丶等时光 4万+

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

版权声明:本文为博主原创文章,转载请附上原文出处链接。 文章目录前言一、硬件设计1.PWM脉冲宽度调制介绍2.STC8A8K64S4A12系列单片机PWM介绍3.PWM配置步骤二、软件设计1.PWM寄存器汇集2.寄存器解析2.1.PWM配置寄存器PWMCFG2.2.PWM控制寄存器PWMCR2.3.PWM中断标志寄存器2.4.PWM外部异常控制寄存器2.5.PWM时钟选择寄存器2.6.PWM触发ADC计数器寄存器2.7.PWM2翻转寄存器2.8.PWMn控制寄存器2.9.PWMn电平保持控制寄存器3.PW

qq_36139768的博客 5219

一个不难的期末设计--推箱子游戏

推箱子小游戏是一款经典的益智游戏,它的目标是在二维网格中移动箱子,将其推至指定位置。游戏的目标是将所有箱子推至目标位置,一旦所有箱子都被推到位,游戏即告胜利。玩家可以通过键盘控制玩家的移动方向,推动箱子,直到所有的箱子都被推至目标位置,即可通关。如果角色前方有箱子,会尝试推动箱子,同时检查箱子后面是否有墙体或另一个箱子,以防止箱子重叠或推不动。推箱子游戏的交互设计主要是指玩家如何与游戏进行互动,包括玩家的移动方式、推动箱子的方式、输入设备的响应等。本程序开发环境为Visual Studio 2022。

weixin_73743190的博客 1106

Java 大视界 -- Java+Spark MLlib 构建智能推荐系统:协同过滤算法实战与优化(441)

Java+Spark MLlib构建智能推荐系统实战 本文详细介绍了基于Java和Spark MLlib构建协同过滤推荐系统的全流程实践方案。主要内容包括: 架构设计:采用离线批处理为主、近实时更新为辅的架构,包含数据采集层、处理层、模型层、推荐生成层、存储层和应用层六大模块,可支撑千万级用户推荐需求。 技术实现: 使用Spark MLlib的ALS算法进行协同过滤模型训练 提供数据清洗、模型训练、推荐生成等核心代码实现 采用MySQL存储推荐结果,支持批量插入和事务保障 生产级优化: 版本化模型存储规避覆

【青云交】华为云云享专家 | 阿里云开发者社区专家博主 技术圈个人影响力前 9 | 博客之星 TOP12 优快云 首位四榜(原力榜 / 作者周榜 / 领军人物 / 综合热榜)榜首,破平台纪录! 苏州地区全榜霸榜,感恩全网十多万粉丝同行! 2554

java游戏设计文档模板_[内附完整源码和文档] 基于Java的推箱子游戏设计与实现...

摘 要社会在进步,人们生活质量也在日益提高。高强度的压力也接踵而来。社会中急需出现新的有效方式来缓解人们的压力。此次设计符合了社会需求,Java推箱子游戏可以让人们在闲暇之余,体验游戏的乐趣。具有操作简单,易于上手的特点。推箱子游戏的玩法十分简单——控制人物绕过障碍物,到达目的地。此次毕业设计基于Java语言。将游戏地图做成通用的关卡设计,关卡用二维数组实现,在二维数组中设置值域来表示不同对象,运...

weixin_42152298的博客 524

C#利用最小二乘法拟合任意次函数曲线

给出一组离散点,确定一个函数逼近原函数,插值是这样的一种手段。在实际中,数据不可避免的会有误差,插值函数会将这些误差也包括在内。因此,我们需要一种新的逼近原函数的手段: ①不要求过所有的点(可以消除误差影响);②尽可能表现数据的趋势,靠近这些点。利用最小二乘法可以简便地求得未知的数据,并使得这些求得的数据与实际数据之间误差的平方和为最小 [1],其他一些优化问题也可通过最小化能量或最大化熵用最小二乘法来表达,具体可以查阅相关资料。最小二乘法(又称最小平方法)是一种数学技术。

qq_30725967的博客 3756

UMI——斯坦福刷盘机器人:通过手持夹爪革新数据收集方式,且使用视觉SLAM和Diffusion Policy预测动作

没想到今年年初的斯坦福mobilealoha的热度刚过,而到今年2月的下旬,斯坦福另一个团队又推出了UMI刷盘机器人,且这两个团队还互相认识、还在一块共同切磋(顺带小小感叹一下,斯坦福的氛围是真好而且真高产)斯坦福UMI刷盘机器人其与mobile aloha(以及AirExo: Low-Cost Exoskeletons for Learning Whole-Arm Manipulation in the Wild)最大的不同在于其收集数据处理的非真实的机器人,而是一个手持夹持器(从而大幅降低成本)

u013250861的博客 1357
上一篇: 北戴河之行(2005年9月23日)
下一篇: 成功并不像你想像的那么难
java2000_test
博客等级 码龄19年 10粉丝 320原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值