Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 332|回复: 0

[J2ME学习]一步一步教你开发《松鼠推箱子》手机游戏

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-10-11 03:00:40 | 显示全部楼层 |阅读模式
    这类游戏大家肯定都玩过,一个很有趣味性的小游戏。操作简单,具有一定的逻辑性。很适合无聊的时候消遣时间。:)      首先简单介绍下手机游戏的一般性开发过程。首先需要策划出一个游戏方案,也就是要给出一个游戏的整体形象。当然是想象中的。比如:游戏题材,背景,操作方法,人物,与奖励机制。然后对这个策划方案进行可行性分析,包括技术,市场,可用资源等。下一步就是编码阶段,一般编码和制作资源可以同步进行。 等程序和资源都弄好了,一个游戏的雏形就大致出来了。后面就是测试与移植等等工作。

          我们要做的是让一只小松鼠推箱子,操作方法就是通过四方向键对松鼠进行上,下,左,右的移动。在移动过程中可以把碰到的箱子推到任意一个方向。当然在游戏中需要设置一些障碍物,让游戏具备可玩性。游戏采用过关制,当松鼠把所有箱子推到指定的位置就算过关。所以每关的场景要不同。这需要对每关的地图进行编排。好了,我们的游戏策划就算完成,嘿嘿!简单吧,想象是多么美好啊。
       
      
       
       
       

       
      
          下面就没这么轻松了,我们要进行技术分析。就是具体的代码如何来实现的。首先我们来确定一下开发难点。对松鼠的操作很简单,就是四方向移动,松鼠移动,箱子也移动,所以对按键处理也比较简单些。当箱子到达某个位置时,就会产生游戏过关事件。需要一个逻辑判断。那么我们仔细想一下,这些所有的事件都发生在一张地图中。这张地图就包括了 箱子的初始化位置,箱子最终放置的位置,和障碍等。每一关地图都要更换。这些位置也要变。所以我们发现每关的地图数据是最关键的。它决定了每关的不同场景和物体位置。好。那么我们就重点分析一下地图。       我们把地图想象成一个网格,每个格子就是松鼠每次移动的步长,也是箱子移动的距离,这样问题就简化多了。首先我们设计一个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[2]-1)],map_data[(nplace[1])][(nplace[0])][0],map_data[(nplace[1])] [(nplace[0])][1],0);
       
      }
    } nplace[][]  是箱子的初始化位置坐标,第一维表示有几个箱子,第二维表示 X,Y坐标 和 颜色 属性
    nut[] 是箱子图片数组 //画箱子终点位置的指示
    private void drawNutDest(Graphics g)
    {
      for(int i = 0; i < ndplace.length; i++)
      {
       g.drawImage(nut[(ndplace[2]+1)],map_data[(ndplace[1])][(ndplace[0])][0],map_data[(ndplace[1])] [(ndplace[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[1] ) && ( nplace[nm][0] == nplace[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[1] ) && ( nplace[nm][0] == nplace[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[1] ) && ( (nplace[nm][0] - 1) == nplace[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[1] ) && ( (nplace[nm][0] + 1) == nplace[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() 来实现刷屏幕等等。由于篇幅有限就不一一介绍了。下面附上一幅游戏完成的截图。看看效果吧。 《松鼠推箱子》游戏截图 程序设计与图片制作:关文柏       Email:k7sem_88@hotmail.com   

      
      
       
       

         
       

         
       
      
      

    源码下载:http://file.javaxxz.com/2014/10/11/030039859.zip
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-2 13:55 , Processed in 0.410051 second(s), 34 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表