`
deaboway
  • 浏览: 57457 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

【贪吃蛇—Java程序员写Android游戏】系列 3. 用J2ME实现Android的Snake Sample详解

阅读更多

本次会详细讲解将Android的Snake Sample移植到J2ME上,从而比较二者的区别和联系。

在《1.Android SDK Sample-Snake详解 》中,我们已经详细介绍了Android实现的Snake项目结构,现在我们要将这个项目用J2ME实现。

一、 J2ME vs. Android

Android的UI实用、方便,而且很美观,基本无需改动且定制方便。而J2ME的高级用户界面比较鸡肋,在现在大多数的应用里都看不到,多数稍微复杂点的界面都是手工画,或是用一些开源的高级UI库。接下来我们简单比较下二者的区别,为Snake项目从Android到J2ME的移植做准备。

1. 平台

J2ME :

开发平台

Android :

操作系统

2. 工程结构

J2ME :

res:资源文件

src:源代码

Android :

src:源代码

res\drawable:图片

res\raw:声音

res\values:字符串

assets:数据文件

3. 安装包

J2ME :

jad,jar

Android :

apk

4. 代码结构

J2ME :

MIDlet,Canvas,采用继承的方式,只有一个MIDlet,一般只有一个Canvas

Android :

Activity,View,采用继承的方式,只有一个Activity,一般只有一个View

5. 入口程序

J2ME :

MIDlet类

Android :

Activity类

6. 主程序结构

J2ME :

    package com.deaboway.j2me;  
    import javax.microedition.midlet.MIDlet;  
    import javax.microedition.midlet.MIDletStateChangeException;  
    public class MyMidlet extends MIDlet {  
    protected void destroyApp(boolean arg0) throws MIDletStateChangeException {  
    // TODO Auto-generated method stub  
    }  
    protected void pauseApp() {  
    // TODO Auto-generated method stub  
    }  
    protected void startApp() throws MIDletStateChangeException {  
    // TODO Auto-generated method stub  
    }  
    }  



Android :

    package com.deaboway.android;  
    import android.app.Activity;  
    import android.os.Bundle;  
    public class myActivity extends Activity {  
    /** Called when the activity is first created. */  
    @Override  
    public void onCreate(Bundle icicle) {  
    super.onCreate(icicle);  
    setContentView(R.layout.main);  
    }  
    }  



7. 生命周期- 开始

J2ME :

startApp(),活动状态,启动时调用,初始化。

Android :

onCreate(),返回时也会调用此方法。

onCreate()后调用onStart(),onStart()后调用onResume(),此时Activity进入运行状态。

8. 生命周期- 暂停

J2ME :

pauseApp(),暂停状态,如来电时,调用该接口。

Android :

onPause()。

9. 生命周期- 销毁

J2ME :

destroyApp(),销毁状态,退出时调用。

Android :

onStop(),程序不可见时调用onDestroy(),程序销毁时调用。

10. 刷新

J2ME :

高级UI组件由内部刷新实现。低级UI,canvas中通过调用线程结合repaint()来刷新,让线程不断循环。低级UI架构可以用MVC方式来实现,建议使用二级缓存。

Android :

高级UIHandler类通过消息的机制刷新。onDraw()刷新接口,低级UI开发者用线程控制更新,在lockCanvas()和unlockCanvasAndPost()方法之间绘制。

如果去读API,我们可以发现J2ME中Canvas的repaint()与Android中View的invalidate()/postInvalidate()方法实现了相同的功能(连说明文字几乎都一样…),但是invalidate()/postInvalidate()两者却有着区别:invalidate() 只能在UI这个线程里通过调用onDraw(Canvas canvas)来update屏幕显示,而postInvalidate()是要在non-UI线程里做同样的事情的。这就要求我们做判断,哪个调用是本 线程的,哪个不是,这在做多线程callback的时候尤为重要。而在J2ME中,不管怎样直接调用repaint()就好了。

11. 绘画

J2ME :

Displayable类。J2me中所有可显示的组件都是直接或间接的继承了Displayable,直接的是Canvas和Screen。不同的继承导致了低级 UI和高级UI的区别。J2me中现成的UI组件都是直接或者间接继承了Screen。只要调用Display.getDisplay(MIDLet instan).setCurrrent(Displayable disp),就可以把组件显示到手机界面上。切换界面的时候也可以使用该接口。

Android :

View类。可见的组件直接或者间接继承了android.view.View。通过 Activity.setContentView(View view)就可以显示在android手机界面上,切换界面的时候也可以使用该接口。如果是直接继承了View而不是Android自带的UI组件,那么 还要自己去实现它的刷新,类似J2me的低级UI组件。

12. 画笔

J2ME :

高级UI组件由内部刷新实现。低级UI,canvas中通过调用线程结合repaint()来刷新,让线程不断循环。低级UI架构可以用MVC方式来实现,建议使用二级缓存。

Android :

Canvas类,Android绘 制的时候会传入一个参数Paint。该对象表示绘制的风格,比如颜色,字体大小,字体格式等。Android的Canvas不同于J2ME的Canvas,它更像于J2ME的Graphics,用来绘制。

13. 全屏

J2ME :

Canvas中SetFullScreenMode()。

Android :

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);requestWindowFeature(Window.FEATURE_NO_TITLE);

14. 获得屏幕尺寸

J2ME :

Canvas类的getHeight()和getWidth()

Android :

Display d = getWindowManager().getDefaultDisplay();

screenWidth = d.getWidth();

screenHeight = d.getHeight();



15. 可绘区域

J2ME :

int clipX = g.getClipX();

int clipY = g.getClipY();

int clipWidth = g.getClipWidth();

int clipHeight = g.getClipHeight();

g.clipRect(x, y, width, height);

g.setClip(clipX, clipY, clipWidth, clipHeight);//释放当前状态



Android :

canvas.save();//保存当前状态

canvas.clipRect(x,y, x+width, y+height)

cavnas.resave();//释放当前状态



16. 清屏操作

J2ME :

g.setColor(Color.WHITE);

g.fillRect(0,0,getWidth(),getHeight());



Android :

// 首先定义paint
Paint paint = new Paint();

// 绘制矩形区域-实心矩形
// 设置颜色
paint.setColor(Color.WHITE);
// 设置样式-填充
paint.setStyle(Style.FILL);
// 绘制一个矩形
canvas.drawRect(new Rect(0, 0, getWidth(), getHeight()), paint);



17. 双缓冲

J2ME :

Image bufImage=Image.createImage(bufWidth, bufHeight);
Graphics bufGraphics=bufImage.getGraphics();



Android :

Bitmap carBuffer = Bitmap.createBitmap(bufWidth, bufHeight, Bitmap.Config.ARGB_4444);
Canvas carGp = new Canvas(carBuffer);



18. 图片类

J2ME :

Image类,Image.createImage(path);

Android :

BitMap类,BitmapFactory.decodeResource(getResources(),R.drawable.map0);

19. 绘制矩形

J2ME :

drawRect的后两个参数为宽度和高度

Android :

drawRect的后两个参数为结束点的坐标

20. 按键

J2ME :

keyPressed()

keyRepeated()

keyReleased()

Android :

onKeyDown()

onKeyUp()

onTracKballEvent()

21. 键值

J 2ME :

Canvas.LEFT…

Android :

KeyEvent.KEYCODE_DPAD_LEFT…

22. 触笔

J2ME :

pointerPressed(),pointerReleased(),pointerDragged()

Android :

onTouchEvent()

23. 数据存储

J2ME :

Record Management System (RMS)

Android :

SQLite数据库,SharedPreferences类

24. 连接

J2ME :

从Connector打开,可以直接在Connector.Open时设置连接是否可读写,以及超时设置

Android :

从URL对象打开,必须使用setDoInput(boolean)和setDoOutput(boolean)方法设置,使用setConnectTimeout(int)不仅可以对连接超时进行设置,还能设置超时时间,参数为0时忽略连接超时

25. 游戏开发包

J2ME :

javax.microedition.lcdui.game.*;

GameCanvas/Layer/LayerManager/ Sprite/TiledLayer

Android :

无专门针对游戏的开发包。

26. 音效

J2ME :

Player s=Manager.createPlayer(InputStream);
s.prepare();//创建
s.start();//播放
s.stop();//暂停
s.stop();//关闭
s.release();//释放


Android :

MediaPlayer类处理背景音乐

SoundPool类处理一些简单的音效

27. 显示文本

J2ME :

String

Android :

TextView类

28. 打印信息

J2ME :

System.out.println()

Android :

Log类

二、 迁移关键点

1. 基础 类和结构

J2ME程序的主体从Activity改变为MIDlet,TileView从View改变为Canvas,相关的方法也需要进行调整,但是主体结构和逻辑还是一致的。此外,有些J2ME不支持的类,需要做对应处理。

资源获取,从xml 改为自行获取:

    private void initSnakeView() {  
        // 获取图片资源  
        try {  
            imageRED_STAR = Image.createImage("/redstar.png");  
            imageRED_STAR = Utils.zoomImage(imageRED_STAR,mTileSize,mTileSize);  
            imageYELLOW_STAR = Image.createImage("/yellowstar.png");  
            imageYELLOW_STAR = Utils.zoomImage(imageYELLOW_STAR,mTileSize,mTileSize);  
            imageGREEN_STAR = Image.createImage("/greenstar.png");  
            imageGREEN_STAR = Utils.zoomImage(imageGREEN_STAR,mTileSize,mTileSize);  
        } catch(Exception e) {  
            Log.info("Create Images: "+e);  
        }  
        // 设置贴片图片数组  
        resetTiles(4);  
        // 把三种图片存到Bitmap对象数组  
        loadTile(RED_STAR, imageRED_STAR);  
        loadTile(YELLOW_STAR, imageYELLOW_STAR);  
        loadTile(GREEN_STAR, imageGREEN_STAR);  
    }  



ArrayList ,用Vector 替换掉:

    // 坐标数组转整数数组,把Coordinate对象的x y放到一个int数组中——用来保存状态  
    private String coordVectorToString(Vector cvec) {  
        int count = cvec.size();  
        StringBuffer rawArray = new StringBuffer();  
        for (int index = 0; index < count; index++) {  
            Coordinate c = (Coordinate) cvec.elementAt(index);  
            rawArray.append(c.x+",");  
            rawArray.append(c.y+",");  
            Log.info("coordVectorToString(), c.x="+c.x+",c.y="+c.y);  
        }  
        Log.info("coordVectorToString(), rawArray.toString="+rawArray);  
        return rawArray.toString();  
    }  
    // 整数数组转坐标数组,把一个int数组中的x y放到Coordinate对象数组中——用来恢复状态  
    // @J2ME 还是用Vector替换ArrayList  
    private Vector coordStringToVector(String raw) {  
        Vector coordArrayList = new Vector();  
        Log.info("coordStringToVector(), raw="+raw);  
        String[] rawArray = Utils.splitUtil(raw,",");  
        Log.info("coordStringToVector(), rawArray.length="+rawArray.length);  
        int coordCount = rawArray.length;  
        for (int index = 0; index < coordCount; index += 2) {  
            Coordinate c = new Coordinate(Integer.parseInt(rawArray[index]), Integer.parseInt(rawArray[index + 1]));  
            coordArrayList.addElement(c);  
        }  
        return coordArrayList;  
    }  



Bundle ,用RMS 实现:

    /** 
     * <p>Title: Snake</p> 
     * <p>Copyright: (C) 2011 Gavin's Snake project. Licensed under the Apache License, Version 2.0 (the "License")</p> 
     * @author Gavin 
     */  
    package com.deaboway.snake.util;  
    import java.io.ByteArrayInputStream;  
    import java.io.ByteArrayOutputStream;  
    import java.io.DataInputStream;  
    import java.io.DataOutputStream;  
    import javax.microedition.rms.RecordStore;  
    public class Bundle extends BaseRMS {  
        private static String[] SECTION = {  
            "\"AL\":","\"DT\":",  
            "\"ND\":","\"MD\":",  
            "\"SC\":","\"ST\":"};  
        private static int LEN = SECTION.length;  
          
        private static boolean inited = false;  
        private static Bundle INSTANCE;  
        public static void INIT(){  
            if(inited)return;  
            inited = true;  
            INSTANCE = new Bundle();  
            INSTANCE.loadBundles();  
        }  
          
        public static Bundle getInstance(){  
            return INSTANCE;  
        }  
        private String[] CONTENT = new String[LEN];  
        private Bundle() {  
            super("snake-view");  
        }  
        public void loadBundles() {  
            try {  
                this.open();  
                this.close();  
            } catch (Exception e) {  
                try {  
                    this.close();  
                    RecordStore.deleteRecordStore(this.getRMSName());  
                    this.open();  
                    this.close();  
                } catch (Exception ex) {  
                }  
            }  
        }  
        public void resetBundles() {  
                try {  
                    this.close();  
                    RecordStore.deleteRecordStore(this.getRMSName());  
                    this.open();  
                    this.close();  
                } catch (Exception ex) {  
                }  
        }  
        public void updateBundles() throws Exception {  
            try {  
                this.openonly();  
                updateData();  
                if (this.getRecordStore() != null)  
                    this.close();  
            } catch (Exception e) {  
                throw new Exception(this.getRMSName() + "::updateBundles::" + e);  
            }  
        }  
        protected void loadData() throws Exception {  
            try {  
                byte[] record = this.getRecordStore().getRecord(1);  
                DataInputStream istream = new DataInputStream(  
                        new ByteArrayInputStream(record, 0, record.length));  
                String content = istream.readUTF();  
                int[] start = new int[LEN+1];  
                for(int i =0;i<LEN;i++){  
                    start[i] = content.indexOf(SECTION[i]);  
                }  
                start[LEN]=content.length();  
                for(int i =0;i<LEN;i++){  
                    CONTENT[i] = content.substring(start[i]+5,start[i+1]);  
                    Log.info("CONTENT["+i+"]="+CONTENT[i]);  
                }  
            } catch (Exception e) {  
                throw new Exception(this.getRMSName() + "::loadData::" + e);  
            }  
        }  
        protected void createDefaultData() throws Exception {  
            try {  
                ByteArrayOutputStream bstream = new ByteArrayOutputStream(12);  
                DataOutputStream ostream = new DataOutputStream(bstream);  
                  
                CONTENT[0] = "9,20,9,7";  
                CONTENT[1] = "1";  
                CONTENT[2] = "1";  
                CONTENT[3] = "600";  
                CONTENT[4] = "0";  
                CONTENT[5] = "7,7,6,7,5,7,4,7,3,7,2,7";  
                  
                StringBuffer sb = new StringBuffer();  
                for(int i=0;i<LEN;i++){  
                    sb.append(SECTION[i]);  
                    sb.append(CONTENT[i]);  
                }  
                  
                ostream.writeUTF( sb.toString());  
                ostream.flush();  
                ostream.close();  
                byte[] record = bstream.toByteArray();  
                this.getRecordStore().addRecord(record, 0, record.length);  
            } catch (Exception e) {  
                throw new Exception(this.getRMSName() + "::createDefaultData::" + e);  
            }  
        }  
        protected void updateData() throws Exception {  
            try {  
                ByteArrayOutputStream bstream = new ByteArrayOutputStream(12);  
                DataOutputStream ostream = new DataOutputStream(bstream);  
                StringBuffer sb = new StringBuffer();  
                for(int i=0;i<LEN;i++){  
                    sb.append(SECTION[i]);  
                    sb.append(CONTENT[i]);  
                }  
                ostream.writeUTF(sb.toString());  
                ostream.flush();  
                ostream.close();  
                byte[] record = bstream.toByteArray();  
                this.getRecordStore().setRecord(1, record, 0, record.length);  
            } catch (Exception e) {  
                throw new Exception(this.getRMSName() + "::updateData::" + e);  
            }  
        }  
          
        public String getValue(int key) {  
            return CONTENT[key];  
        }  
        public void setValue(int key, String value) {  
            CONTENT[key] = value;  
        }  
    }  



Log ,自己实现Log 系统:

    /** 
     * <p>Title: Snake</p> 
     * <p>Copyright: (C) 2011 Gavin's Snake project. Licensed under the Apache License, Version 2.0 (the "License")</p> 
     * @author Gavin 
     */  
    package com.deaboway.snake.util;  
    public class Log{  
        private static final int FATAL = 0;  
        private static final int ERROR = 1;  
        private static final int WARN =  2;  
        private static final int INFO =  3;  
        private static final int DEBUG = 4;  
              
        private static int LOG_LEVEL = INFO;  
          
        public static void info(String string){  
            if(LOG_LEVEL >= INFO){  
                System.out.println("[Deaboway][ INFO] " + string);  
            }  
        }  
        public static void debug(String string){  
            if(LOG_LEVEL >= DEBUG){  
                System.out.println("[Deaboway][DEBUG] " + string);  
            }  
        }  
        public static void warn(String string){  
            if(LOG_LEVEL >= WARN){  
                System.out.println("[Deaboway][ WARN] " + string);  
            }  
        }  
        public static void error(String string){  
            if(LOG_LEVEL >= ERROR){  
                System.out.println("[Deaboway][ERROR] " + string);  
            }  
        }  
        public static void fatal(String string){  
            if(LOG_LEVEL >= FATAL){  
                System.out.println("[Deaboway][FATAL] " + string);  
            }  
        }  
    }  



2. TileView 类

(1)用Image替换BitMap,“private Image[] mTileArray;”

(2)private final Paint mPaint = new Paint();不再需要了。直接在Graphics中drawImage即可

(3)onSizeChanged() 不会被自动调用,需要在构造函数中主动调用,以实现对应功能。onSizeChanged(this.getWidth(),this.getHeight());

(4)最重要的,用paint替换onDraw,呵呵,Canvas的核心啊!失去它你伤不起!!!咱也试试咆哮体!!!!!!

3. SnakeView 类

(1)J2ME 没有Handler,直接用Thread定期repaint()就OK。这里要啰嗦几句。

熟悉Windows编程的朋友可能知道Windows程序是消息驱动的,并且有全局的消息循环系统。而Android应用程序也是消息驱动的,按道理来说也应该提供消息循环机制。实际上Android中也实现了类似Windows的消息循环机制,它通过Looper、Handler来实现消息循环机制,Android消息循环是针对线程的(每个线程都可以有自己的消息队列和消息循环)。

Android系统中Looper负责管理线程的消息队列和消息循环。Handler的作用是把消息加入特定的(Looper)消息队列中,并分发和处理该消息队列中的消息。构造Handler的时候可以指定一个Looper对象,如果不指定则利用当前线程的Looper创建。

一个Activity中可以创建多个工作线程或者其他的组件,如果这些线程或者组件把他们的消息放入Activity的主线程消息队列,那么该消息就会在主线程中处理了。因为主线程一般负责界面的更新操作,并且Android系统中的weget不是线程安全的,所以这种方式可以很好的实现Android界面更新。在Android系统中这种方式有着广泛的运用。

如果另外一个线程要把消息放入主线程的消息队列,就需要通过Handle对象,只要Handler对象以主线程的Looper创建,那么调用Handler的sendMessage等接口,将会把消息放入队列都将是放入主线程的消息队列。并且将会在Handler主线程中调用该handler的handleMessage接口来处理消息。

之所以Android有这些处理,是因为Android平台来说UI控件都没有设计成为线程安全类型,所以需要引入一些同步的机制来使其刷新。而对于J2ME来说,Thread比较简单,直接匿名创建重写run方法,调用start方法执行即可。或者,也可以从Runnable接口继承。实现如下:

    class RefreshHandler extends Thread {  
        public void run() {  
            while (true) {  
                try {  
                    //delay一个延迟时间单位  
                    Thread.sleep(mMoveDelay);  
                } catch (Exception e) {  
                    e.printStackTrace();  
                }  
                // 更新View对象  
                SnakeView.this.update();  
                // 强制重绘  
                SnakeView.this.repaint();  
            }  
        }  
    };  



(2)直接使用String代替TextView类,在Canvas的paint()中直接绘制各种提示信息。

(3)在一些地方需要主动调用repaint()进行强制重绘。

其它具体参考源代码。

4. Snake 类

本类就比较简单了,直接把源代码贴出来如下:

    /** 
     * <p>Title: Snake</p> 
     * <p>Copyright: (C) 2011 Gavin's Snake project. Licensed under the Apache License, Version 2.0 (the "License")</p> 
     * @author Gavin 
     */  
    package com.deaboway.snake;  
    import javax.microedition.lcdui.Display;  
    import javax.microedition.midlet.MIDlet;  
    import javax.microedition.midlet.MIDletStateChangeException;  
    import com.deaboway.snake.util.BaseRMS;  
    import com.deaboway.snake.util.Bundle;  
    import com.deaboway.snake.util.Log;  
    /** 
     * Snake: a simple game that everyone can enjoy. 
     *  
     * 贪吃蛇: 经典游戏,在一个花园中找苹果吃,吃了苹果会变长,速度变快。碰到自己和墙就挂掉 
     *  
     */  
    public class Snake extends MIDlet {  
        public static Display display;  
        public static SnakeView mSnakeView;  
        public static MIDlet SNAKE;  
          
        public Snake() {  
            Bundle.INIT();  
            display=Display.getDisplay(this);  
            SNAKE = this;  
            mSnakeView = new SnakeView();  
            mSnakeView.setTextView("");  
            mSnakeView.setMode(SnakeView.READY);  
            display.setCurrent(mSnakeView);  
        }  
        protected void destroyApp(boolean arg0) throws MIDletStateChangeException {  
            mSnakeView.saveState();  
        }  
        protected void pauseApp() {  
            mSnakeView.setMode(SnakeView.PAUSE);  
        }  
        protected void startApp() throws MIDletStateChangeException {  
            // 检查存贮状态以确定是重新开始还是恢复状态  
            Log.debug("startApp(), BaseRMS.FIRST="+BaseRMS.FIRST);  
            if (BaseRMS.FIRST) {  
                // 存储状态为空,说明刚启动可以切换到准备状态  
                mSnakeView.setMode(SnakeView.READY);  
            } else {  
                // 已经保存过,那么就去恢复原有状态  
                // 恢复状态  
                if (!mSnakeView.restoreState()) {  
                    // 恢复状态不成功,设置状态为暂停  
                    mSnakeView.setMode(SnakeView.PAUSE);  
                }  
            }  
        }  
    }  



本次就大概介绍这么多,源代码将在下次放出。下次主要讲解源代码的存储和维护,敬请期待。

 

分享到:
评论
3 楼 luozhong915127 2012-02-10  
呵呵额,有点功夫。
2 楼 deaboway 2011-04-26  
archangelwin 写道
期待中  



已经放出来了,参考:http://www.iteye.com/topic/1013918
1 楼 archangelwin 2011-04-26  
期待中  

相关推荐

    贪吃蛇—Java程序员写Android游戏

    ### 贪吃蛇——Java程序员开发Android游戏详解 #### 一、项目背景与目标 贪吃蛇是一款经典的休闲游戏,曾经风靡于诺基亚蓝色屏幕手机时代,成为其标志性游戏之一。随着移动设备的发展,贪吃蛇也登陆了Android平台...

    java 贪吃蛇游戏.zip

    java 贪吃蛇游戏.zipjava 贪吃蛇游戏.zipjava 贪吃蛇游戏.zipjava 贪吃蛇游戏.zip java 贪吃蛇游戏.zipjava 贪吃蛇游戏.zipjava 贪吃蛇游戏.zipjava 贪吃蛇游戏.zip java 贪吃蛇游戏.zipjava 贪吃蛇游戏.zipjava ...

    java贪吃蛇游戏.zip

    java贪吃蛇游戏.zipjava贪吃蛇游戏.zipjava贪吃蛇游戏.zipjava贪吃蛇游戏.zip java贪吃蛇游戏.zipjava贪吃蛇游戏.zipjava贪吃蛇游戏.zipjava贪吃蛇游戏.zip java贪吃蛇游戏.zipjava贪吃蛇游戏.zipjava贪吃蛇游戏.zip...

    Java版贪吃蛇游戏.zip

    Java版贪吃蛇游戏.zipJava版贪吃蛇游戏.zipJava版贪吃蛇游戏.zip Java版贪吃蛇游戏.zipJava版贪吃蛇游戏.zipJava版贪吃蛇游戏.zip Java版贪吃蛇游戏.zipJava版贪吃蛇游戏.zipJava版贪吃蛇游戏.zip Java版贪吃蛇游戏....

    java swing 贪吃蛇游戏.zip

    java swing 贪吃蛇游戏.zipjava swing 贪吃蛇游戏.zipjava swing 贪吃蛇游戏.zip java swing 贪吃蛇游戏.zipjava swing 贪吃蛇游戏.zipjava swing 贪吃蛇游戏.zip java swing 贪吃蛇游戏.zipjava swing 贪吃蛇游戏....

    Java实现贪吃蛇小游戏.zip

    Java实现贪吃蛇小游戏.zipJava实现贪吃蛇小游戏.zipJava实现贪吃蛇小游戏.zip Java实现贪吃蛇小游戏.zipJava实现贪吃蛇小游戏.zipJava实现贪吃蛇小游戏.zip Java实现贪吃蛇小游戏.zipJava实现贪吃蛇小游戏.zipJava...

    java实现的贪吃蛇小游戏.zip

    java实现的贪吃蛇小游戏.zipjava实现的贪吃蛇小游戏.zipjava实现的贪吃蛇小游戏.zip java实现的贪吃蛇小游戏.zipjava实现的贪吃蛇小游戏.zipjava实现的贪吃蛇小游戏.zip java实现的贪吃蛇小游戏.zipjava实现的贪吃蛇...

    Snake(j2me).rar_j2me_j2me snake_j2me 游戏 源码_snake

    "Snake(j2me).rar"这个压缩包中包含了使用J2ME编写的贪吃蛇游戏源码,对于想要了解或深入学习J2ME游戏开发的程序员来说,这是一个非常宝贵的资源。 J2ME是Java平台的一个子集,专为资源有限的嵌入式设备设计,如...

    JAVA贪吃蛇游戏毕业设计(源代码).zip

    JAVA贪吃蛇游戏毕业设计(源代码).zipJAVA贪吃蛇游戏毕业设计(源代码).zipJAVA贪吃蛇游戏毕业设计(源代码).zipJAVA贪吃蛇游戏毕业设计(源代码).zipJAVA贪吃蛇游戏毕业设计(源代码).zipJAVA贪吃蛇游戏毕业设计(源代码)...

    java图形界面,贪吃蛇游戏练习.zip

    java图形界面,贪吃蛇游戏练习.zipjava图形界面,贪吃蛇游戏练习.zip java图形界面,贪吃蛇游戏练习.zipjava图形界面,贪吃蛇游戏练习.zip java图形界面,贪吃蛇游戏练习.zipjava图形界面,贪吃蛇游戏练习.zip java图形...

    [Android游戏源码]-简单的贪吃蛇源码.rar_Android游戏源码_android_android 贪吃蛇_贪吃蛇_贪

    在Android平台上,我们可以找到许多实现贪吃蛇的游戏源码,这为我们理解和学习移动游戏开发提供了宝贵的学习资源。这里我们将深入分析一款名为"简单的贪吃蛇源码"的Android游戏源码,探讨其设计思路、主要技术和实现...

    java-小游戏贪吃蛇实验报告.zip

    java-小游戏贪吃蛇实验报告.zipjava-小游戏贪吃蛇实验报告.zip java-小游戏贪吃蛇实验报告.zipjava-小游戏贪吃蛇实验报告.zip java-小游戏贪吃蛇实验报告.zipjava-小游戏贪吃蛇实验报告.zip java-小游戏贪吃蛇实验...

Global site tag (gtag.js) - Google Analytics