游戏开发,好大的一个命题哦。无论是游戏的剧情设计,还是游戏的美工制作,那都不是一两下子能够完成的事,而编程,只是其中的一小部分。但是,就算是这一小部分,我也不可能把它掌握得很透彻。这篇随笔,大部分都是些抄书的东西,主要是为了我的博客的完整性而存在,但是我依然会尽力把它写好,要让那些精通Java其它领域但是却没有机会做J2ME开发的高手们看看图片解解馋,让那些想做手机游戏开发的同仁们看看J2ME的Game API究竟为我们提供了哪些支持,让我们知道写游戏需要了解哪些概念。

  先来了解一下MIDP 2.0的游戏开发包,不用怕,这组API很简洁,只有区区5个类,它们都位于javax.microedition.lcdui.game包中。它们分别为GameCanvas类、Layer类、LayerManager类、Sprite类和TiledLayer类。下面分别来介绍一下各个类的用途。

   1> GameCanvas类

  GameCanvas类是Canvas的子类,它代表了游戏的基本界面,简单一点说,就是所有的游戏画面都是在GameCanvas上进行绘制的。那么GameCanvas和Canvas相比,究竟提供了些什么更高级的功能呢?主要有两点:1、实现了双缓冲功能;2、提供了轮询键盘输入事件的方法。

  双缓冲大家肯定很熟悉,就是我们绘图的时候先把图象绘制到一个缓冲区中,等图象绘制完成后,再一次性显示到屏幕上,这样,就可以有效消除闪烁和画面撕裂等现象。在GameCanvas中,我们可以通过getGraphics()方法取得缓冲区的Graphics对象实例,可以通过flushGraphics()方法来将缓冲区的内容显示到屏幕上。

  轮询键盘输入事件有什么用呢?要回答这个问题,我们首先要了解一些基本的游戏框架,在大部分游戏中,都存在一个主循环,这个主循环决定了我们的游戏以每秒钟多少步的频率运行,而在每一步中,先查询有无键盘输入事件,再运行游戏的逻辑运算,最后更新画面,然后再进入下一步。在Canvas中,键盘的输入事件都是通过回调的方式进行的,也就是说当有按键按下的时候,调用keyPressed()方法,这样的功能肯定不能够满足我们这样的每一步都要查询键盘输入的要求。通过调用getKeyStates()方法即可轮询键盘。

   2> Layer类

  这是一个抽象类,我们并不直接使用它,而是使用它的两个子类——Sprite类和TiledLayer类

   3> Sprite类

  Sprite是精灵的意思,这是一个游戏开发的专有名词,在我们游戏中的每一个对象,我们都可以称为一个精灵。Sprite类提供了画面的翻转、旋转及简单的碰撞检测等。要在GameCanvas上绘制一个精灵对象也很简单,只需要调用Sprite的paint方法,该方法需要一个Graphics类型的参数,我们把缓冲区的Graphics对象实例传递给它即可。

   4> TiledLayer类

  Tile也是一个二维游戏开发的经典词汇,是砖块的意思。这个类有什么用呢?主要是用来构建地图。这个道理一想也很容易明白,我们玩的游戏中,地图往往比屏幕窗口大很多,难道我们需要做这么大的图片吗?当然不是,我们只需要做几个小图片,它这些图片按照一定的顺序平铺,就可以得到一个相当大的地图。TiledLayer类就为我们提供了这样的功能。

   5> LayerManager类

  这个类主要是用来管理Layer,它可以在画布上分层次的绘制精灵和地图,这样,就可以比较方便的解决谁在前、谁在后、谁遮挡谁等问题。

  下面来看实例。刚才已经说过,游戏设计是一个很复杂的过程,没有专业的队伍是很难搞的。当然,我们也不是没有另类的搞法,那就是翻版。想一想我们从小时候到现在玩过哪些经典游戏?俄罗斯方块、超级玛丽、合金弹头、雷电、街霸、拳皇等等,还有前两年非常流行的“是男人系列”,这些游戏各有各的运行平台,后来又大部分移植到PC平台,现在,我们何不试试将它们移植到手机上呢?通过前面的介绍,不难看出,这些经典的2D游戏都可以使用上面的几个类来概括:飞机、子弹、敌人都是Sprite,大海、天空、森林、沙漠都是TileLayer,我们所面临的难题,就是搜集和制作图片素材而已。

  这里的实例是“是男人就下一百层”的手机翻版,下载地址: http://www.j2medev.com/Soft/src/game/200610/802.html,作者不详,反正不是我。下面是运行效果图:
  

  首先,我们看一看程序的起点,也就是我们的MIDlet类,在这个程序的LRunner类的构在函数中,创建了一个gameCanv对象,如下:

public LRunner() {
   dp=Display.getDisplay(this);
   gc=new gameCanv();
   dp.setCurrent(gc);
}

  然后,在commandAction()函数中,这样启动游戏:

public void commandAction(Command c,Displayable d)
    {
        if(c==cmd_exit)
        {
            try
            {
                destroyApp(false);
            }catch(Exception e){}
            notifyDestroyed();
        }
        if((c==cmd_start)&(!gc.getIsOnGame()))
        {
            gc.reStart();
        }
}

  于是,游戏的主要逻辑全部交给gameCanv类了,这个类,就是我们前面讲到的GameCanvas类的子类,在这个类中,它实现了Runnable接口,并创建一个新的线程,以实现游戏的主循环,如下:

public void run() //主线程
    {
        isOnGame=true;
        while(isOnGame)
        {
            gameCount++;
            gameLevelUp(gameCount);
            try
            {
                Thread.sleep(gameSpeed);
            }catch(Exception e1){}

            addGameObj();
            for(int j=0;j<objList.size();j++) //游戏人物和台阶的处理
            {
                gfo=(gameFlatObj)objList.elementAt(j);
                gfo.up();
                if(gco==null)
                {
                    gco=new gameCharObj(gfo.getX(),gfo.getY());
                }
                if(gfo.collidesWith(gco,false))
                {
                    isOnFlat=true;
                    gfo.doOnChar(gco);
                }
                else
                {
                    gfo.resetTime();
                }
                if(gfo.getProperties()>1)
                {
                    gfo.nextFrame();
                }
                gfo.paint(g); 
            }
            
            if(gfyo!=null)
            {
                if(gfyo.collidesWith(gco,false))
                {
                    gfyo.doOnChar(gco);
                    gfyo=null;
                }
                else
                {
                    gfyo.go();
                    gfyo.nextFrame();
                    gfyo.paint(g);
                    if(gfyo.getIsBottom())
                    {
                        gfyo=null;
                    }
                }
            }
            
            keyPressed();
            if(!isOnFlat)
            {
                gco.go(); 
            }
            isOnFlat=false;
            
            //***************
            if(gameBgCount<0)
            {
                gameBgCount++;
            }
            else
            {
                gameBgCount=-20;
            }            
            //***************
            
            gco.nextFrame(); 
            gco.paint(g);
            
            //***************
            drawMenu(g);
            //***************
            
            g.drawImage(gameTeeth,0,20,0); 
            
            this.flushGraphics();
            chkObjIsTop();
            if(gco.isDead())
            {
                setOnGame(false);
            }
        }
  
        this.flushGraphics();
}

  从以上的代码中可以看出,这是一个典型的主循环,它通过调用addGameObj()来随机创建物体,而这些物体中,横板、翻板、传送带、钉板等物体作者将之称为gfo,对应的类为gameFlatObj,而从天而降的圆球称为gfyo,对应的类为gameFlyObj,而这几个类,当然是Sprite类的子类了。在这个主循环中,不难看到gfo.paint(g)和this.flushGraphics()这样的代码,正好和我们前面所说的GameCanvas类实现了双缓冲是对应的。

  怎样轮询键盘输入呢?从上面的代码可以看出,作者将轮询键盘事件的代码放到了keyPressed()函数中,我们再来看看这个函数:

private void keyPressed()
    {
        int keyState=this.getKeyStates();
        if((keyState&GameCanvas.LEFT_PRESSED)!=0)
        {
            if(gco!=null)
            {
                gco.setCharAct(0,isOnFlat);
                gco.left(isOnFlat);
            }
            return;
        }
        if((keyState&GameCanvas.RIGHT_PRESSED)!=0)
        {
            if(gco!=null)
            {
                gco.setCharAct(1,isOnFlat);
                gco.right(isOnFlat);
            }
            return;
        }
        if(gco!=null)
        {
            gco.setCharAct(2,isOnFlat);
        }
}

  和我们前面的介绍也是刚好一一对应。

  下面我们再来看看Sprite类怎么使用。这里,gfo、gfyo等等精灵我就不讲了,只看看gco,这个精灵是我们游戏的主角,也就是那个跑来跑去跳上跳下的那个小人儿。该精灵对应的素材图片如下:
  
  这个图片的文件名为char.png,不难看出,第一帧图象是站立不动时的效果,第2-5帧为向左跑动的效果,第6-9帧为向右跑动的效果,10-13帧为上下跳动的效果。Sprite类有一个构造函数Sprite(Image img,int x, int y),其中第一个参数就是该素材图片,后面的两个参数为每一帧图象的宽和高,至于总共有多少帧图象,Sprite类会自己计算。

  gco精灵所对应的类为gameCharObj,从它的构造函数中,我们可以看出它正是以这个图片作为参数的,如下:

public class gameCharObj extends Sprite 
{
   static
    {
        try
        {
            img=Image.createImage("/char.png");
        }
        catch(Exception e){}
    }

    public gameCharObj(int X ,int Y)
    {
        super(img,16,17);
        this.defineCollisionRectangle(1,16,15,1);
        this.x=X;
        this.y=Y-17;
        this.setFrameSequence(stand);
        this.setPosition(this.x,this.y);
    }
}

  在Sprite中,我们通过指定不同的帧序列,就可以实现动画,比如下面定义的四个数组,分别代表了站立时、向左跑时、向右跑时和跳起时的动画序列:

private int[] stand={0};
private int[] w_left={1,1,2,2,3,3,4,4};
private int[] w_right={5,5,6,6,7,7,8,8};
private int[] jump={9,9,10,10,11,11,12,12};

  当我们需要改变精灵的动画序列的时候,只需要调用Sprite.setFrameSequence()函数即可,如下面这段代码所示:

public void setCharAct(int i,boolean b) //改变人物绘图
    {
        switch(i)
        {
        case 0:
            if(state!=i)
            {
                this.setFrameSequence(w_left);
                state=i;
            }            
            break;
        case 1:
            if(state!=i)
            {
                this.setFrameSequence(w_right);
                state=i;
            }            
            break;
        case 2:
            if((state!=i)||(face!=b))
            {
                this.setFrameSequence(b?stand:jump);
                state=i;
                face=b;
            }            
            break;
        }
    }

  设置完动画序列后,只有调用Sprite.nextFrame(),精灵才会真正的动起来,这一个调用是在主线程中完成的,大家回到开头就不难看到gco.nextFrame()这样的代码了。

  遗憾的是,这个游戏中没有使用到TiledLayer和LayerManager这两个类,所以没有办法让大家看到实例了。当然,要想全面了解游戏开发的各个细节,最好还是找一些专业的书来读。最后,给大家介绍一个非常好的J2ME开发网站: www.j2medev.com,这这里,大家可以找到很多文章和资源。

使用NetBeans进行J2ME开发(五):揭开游戏开发的神秘面纱相关推荐

  1. 用VS2005开发BHO揭开IE插件的神秘面纱

    IE插件早已不是个新东西了,在IE4时代就有了,但是随着恶意插件的层出不穷,又一次被人重视起来.当前很多网站在无意识或有意识的情况下被挂马,让用户下载.cab自动安装,让用户中恶意IE插件.或者当用户 ...

  2. 区块链游戏开发颠覆传统游戏开发的 5 种方式

    区块链技术已在许多行业中使用,但它尤其扰乱了游戏行业.区块链游戏开发并不是一个新概念,但还是比较新的.公司现在正在寻找在他们的游戏中使用区块链技术的方法. 区块链游戏开发颠覆传统游戏开发的一些方式 包 ...

  3. @程序员,为你揭开直播技术的神秘面纱!

    作者 | 阿文,责编 | 郭芮 头图 | CSDN 下载自视觉中国 出品 | CSDN(ID:CSDNnews) 随着Web 2.0 的普及以及移动互联网技术的发展,各种视频分享.流媒体直播类型的服务 ...

  4. 揭开人类语言的神秘面纱:从理解到处理自然语言

    https://www.toutiao.com/a6709740042509615619/ 随着人工智能的进步和技术变得越来越复杂,我们希望现有的概念能够接受这种变化或者改变自己.同样,在自然语言的计 ...

  5. 揭开Java 泛型类型擦除神秘面纱

    转载自   揭开Java 泛型类型擦除神秘面纱 泛型,一个孤独的守门者. 大家可能会有疑问,我为什么叫做泛型是一个守门者.这其实是我个人的看法而已,我的意思是说泛型没有其看起来那么深不可测,它并不神秘 ...

  6. 揭开均线系统的神秘面纱_揭开依赖注入的神秘面纱,并通过此快速介绍了解它的实际应用...

    揭开均线系统的神秘面纱 by Sankalp Bhatia 通过Sankalp Bhatia 揭开依赖注入的神秘面纱,并通过此快速介绍了解它的实际应用 (Demystify Dependency In ...

  7. 揭开均线系统的神秘面纱_在应用程式审查API中揭开新玩法的神秘面纱

    揭开均线系统的神秘面纱 During the #11WeeksOfAndroid the new Play In-App Review API was announced. This was a lo ...

  8. html5实现单点登录,图文并茂,为你揭开“单点登录“的神秘面纱

    概念 单点登录( Single Sign On ,简称 SSO),是目前比较流行的企业业务整合的解决方案之一,用于多个应用系统间,用户只需要登录一次就可以访问所有相互信任的应用系统. 前置介绍 同源策 ...

  9. 揭开 Growth Hacking 的神秘面纱(番外篇)+ 大结局

    揭开 Growth Hacking 的神秘面纱(番外篇)+ 大结局 覃超帝国兴亡史  12月11日 11:45 FACEBOOK  互联网  分类 :互联网 阅读:1527 抢沙发 Growth Ha ...

  10. 揭开IL代码的神秘面纱--进阶篇(一)

    系列文章目录 揭开IL代码的神秘面纱--基础篇(一) 揭开IL代码的神秘面纱--基础篇(二) 揭开IL代码的神秘面纱--进阶篇(一) 持续更新中...... 工具 IL指令大全 IL指令分类 IL代码 ...

最新文章

  1. Matlab与线性代数 -- 稀疏矩阵的创建
  2. python iocp_记对协程增加IOCP支持时候踩过的一些坑
  3. IIS 权限错误(401.1 401.2 401.3)解决办法
  4. mysql 一对多 关联一条最新的数据_不得不会的mysql锁
  5. Foxmail怎么诊断邮件的状态
  6. 12020.硬件电路
  7. 记录配置fast rcnn(caffe)仅CPU遇到的问题
  8. 一加8T真机渲染图泄露:后置四摄+双闪光灯
  9. 11 月全球 Web 服务器调查报告:nginx 表现最佳
  10. w3c标准的网页内嵌播放器代码
  11. 【C语言】23-typedef
  12. SNARK性能及安全——Verifier篇
  13. 完美解决idea Maven Cannot reconnect
  14. java swing实现的简单音乐播放器源码附带视频指导教程
  15. 安国论—富国篇:金融战争与美元霸权(1)
  16. 指针式仪表自动读数与识别(二):仪表图像预处理
  17. 万维考试系统题库答案python_万维题库与试卷管理系统
  18. 数据封装与解封装过程
  19. [CentOS8+gitlab-ce本地配置手顺]
  20. 大数据来袭-玩微博共享也可以赚不少钱

热门文章

  1. HNUCM 1366 绿地装饰解题报告 (模拟)
  2. 思齐什么意思_思齐的寓意
  3. Java实现3DES加密解密(DESede/ECB/PKCS5Padding使用)
  4. 如何快速成为Python工程师?
  5. chi2inv函数 matlab_matlab函数列表(A~Z)【转】
  6. Matlab中矩阵的右上角有一撇表示什么意思
  7. 全面认识当前市面99%的大数据技术框架(附:各大厂大数据技术应用文章)
  8. 鸿蒙系统敏感应用,鸿蒙系统特性“揭晓”!一次开发灵活使用,生态构建难题被解决?...
  9. 入驻爱采购选择苏州本地服务商@江苏一网推
  10. 虚拟盒子下装linux系统,eUnoBox(虚拟盒子) v3.14免费版