`
leecong1p
  • 浏览: 147919 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

J2ME中3D场景漫游的实现

    博客分类:
  • J2ME
阅读更多

     好吧,你该知道为什么我这叫跳票工厂,其实这玩意还没做完。不过高度图、场景漫游、公告板技术都有,就差碰撞了,希望给需要的朋友提供一点借鉴价值,手机游戏有这么点东西也够做游戏了。话说M3G现在不太吃香,所以果断停止钻研,打算投奔更底层的OpenGL怀抱。

  


运行截图
 

 

     几点要说明的:

     1)公告板技术(如果你还不了解什么是公告板就去百度一下),一个面就能做一棵树,并且看上去效果还不错。优点是一个面,非常节省资源,缺点是你必须对所有的面进行对齐处理,但往往有的时候很难去判断哪些面在可视范围以内而需要对齐,其中就可能涉及到复杂的判定,况且对齐操作也需要一点点开销。所以大部分3D手机游戏用两个交叉面来做一棵树,开销稍微多了一点,但这样不管在X、Z轴的哪个方向看效果都不错,也比一个面的树更立体一些,更大的优点就是这棵树放到场景里就行,不需要进行对齐操作。

 

     2)高度图场景的漫游,漫游的原理主要是根据摄像机当前位置,得到在高度图中对应位置的高度值,然后根据所在面四个顶点的Y值,插值法求出当前点的高度。但是插值法是有误差的,当四边形尺寸越大时误差越明显。本程序中使用的算法还有一点问题,研究的朋友自己解决一下吧。:)

 

     主要代码在下面贴出,感兴趣的朋友也可以加我一起讨论。

     游戏开发讨论群:50184572

     我的QQ:350751373

 

   创建高度图的代码

import java.io.IOException;

import javax.microedition.lcdui.Image;
import javax.microedition.m3g.Appearance;
import javax.microedition.m3g.CompositingMode;
import javax.microedition.m3g.Group;
import javax.microedition.m3g.Image2D;
import javax.microedition.m3g.IndexBuffer;
import javax.microedition.m3g.Loader;
import javax.microedition.m3g.Mesh;
import javax.microedition.m3g.PolygonMode;
import javax.microedition.m3g.Texture2D;
import javax.microedition.m3g.Transform;
import javax.microedition.m3g.TriangleStripArray;
import javax.microedition.m3g.VertexArray;
import javax.microedition.m3g.VertexBuffer;

public class HeightMap
{
    //高度信息数组
    private short[] heightMap;
    
    //地图数据尺寸
    private int mapWidth;
    
    private int mapHeight;
    
    //保存地图所有的多边形
    private Mesh[][] map;
    
    //地图编码纹理
    private Texture2D landTexture;
    
    //高度图扩大到地图尺寸的比例(正常0.20 or 0.10)
    private float resolution = 0.1f;
    
    //高度比例
    private short heightResolution = 10;
    
    //每块地图数据代表的3D单位距离
    private float perUnit = 1f;
    
    public HeightMap(String heightMapImgSrc, String textureImgSrc, float perUnit)
            throws IOException
    {
        if (resolution <= 0.0001f || resolution > 1.0f)
            throw new IllegalArgumentException("Resolution too small or too large");
        
        this.perUnit = perUnit;
        
        // 加载文件
        Image img = Image.createImage(heightMapImgSrc);
        
        //加载并解析高度图
        loadMapImage(img);
        
        //创建地图纹理
        landTexture = createTexture(textureImgSrc);
        
        //根据高度图创建整个地图多边形
        createQuads();
        
        //按设置比例对地图所有面进行缩放、平移操作
        setTransform();
    }
    
    public HeightMap(short[] heightMap, int mapWidth, int mapHeight, String textureImgSrc,
            float perUnit) throws IOException
    {
        if (resolution <= 0.0001f || resolution > 1.0f)
            throw new IllegalArgumentException("Resolution too small or too large");
        
        this.heightMap = heightMap;
        this.mapWidth = mapWidth;
        this.mapHeight = mapHeight;
        this.perUnit = perUnit;
        
        for (int i = 0; i < heightMap.length; i++)
        {
            heightMap[i] = (short) (heightMap[i] * heightResolution);
        }
        
        //创建地图纹理
        landTexture = createTexture(textureImgSrc);
        
        //根据深度图创建整个地图多边形
        createQuads();
        
        //按设置比例对地图所有面进行缩放、平移操作
        setTransform();
    }
    
    private Mesh[][] getQuads()
    {
        return map;
        
    }
    
    public Group getMeshGroup()
    {
        Group group = null;
        try
        {
            
            group = new Group();
            Mesh[][] map = getQuads();
            for (int x = 0; x < getMapWidth() - 1; x++)
            {
                for (int y = 0; y < getMapHeight() - 1; y++)
                {
                    group.addChild(map[x][y]);
                }
            }
            
            return group;
        }
        catch (Exception e)
        {
            System.out.println("Heightmap error: " + e.getMessage());
            e.printStackTrace();
            
        }
        return group;
    }
    
    public int getMapWidth()
    {
        return mapWidth;
    }
    
    public int getMapHeight()
    {
        return mapHeight;
    }
    
    public float getPositionHeight(float x, float z)
    {
        int col0 = (int) (x / perUnit);
        int row0 = (int) (z / perUnit);
        int col1 = col0 + 1;
        int row1 = row0 + 1;
        if (col1 > mapWidth)
            col1 = 0;
        if (row1 > mapHeight)
            row1 = 0;
        
        System.out.println(col0+","+row0+"  "+col1+","+row1);
        
        float height00 = heightMap[col0 + row0 * mapWidth];
        float height01 = heightMap[col1 + row0 * mapWidth + 1];
        float height11 = heightMap[col1 + row1 * mapWidth];
        float height10 = heightMap[col0 + row1 * mapWidth + 1];
        
        float tx = x / perUnit - col0;
        float ty = z / perUnit - row0;
        float txty = tx * ty;
        float height = height00 * (1.0f + txty - tx - ty) + height01 * (tx - txty) + height11
                * txty + height10 * (tx - txty);
        
        return height/510;
    }
    
    /**
     * 根据地图数据和高度数据创建整个地图所有的四边形
     */
    private void createQuads()
    {
        short[] heights = new short[4];
        map = new Mesh[mapWidth][mapHeight];
        
        for (int x = 0; x < (mapWidth - 1); x++)
        {
            for (int y = 0; y < (mapHeight - 1); y++)
            {
                //读取四个顶点高度
                heights[0] = heightMap[x + y * mapWidth];
                heights[1] = heightMap[x + y * mapWidth + 1];
                heights[3] = heightMap[x + (y + 1) * mapWidth];
                heights[2] = heightMap[x + (y + 1) * mapWidth + 1];
                
                //根据四个顶点的高度图创造面
                map[x][y] = createQuad(heights);
            }
        }
        
    }
    
    /**
     * 加载高度图,并解析出高度图的高度数据
     * @param img 高度图图片
     */
    private void loadMapImage(Image img)
    {
        //根据图片尺寸创建像素信息数组
        int[] data = new int[img.getWidth() * img.getHeight()];
        
        //获得像素信息
        img.getRGB(data, 0, img.getWidth(), 0, 0, img.getWidth(), img.getHeight());
        
        int imgw = img.getWidth();
        int imgh = img.getHeight();
        
        //根据加载图片计算地图尺寸
        mapWidth = (int) (resolution * imgw);
        mapHeight = (int) (resolution * imgh);
        
        //初始化高度图
        heightMap = new short[mapWidth * mapHeight];
        
        // Calculate height and width offset into image
        int xoff = imgw / mapWidth;
        int yoff = imgh / mapHeight;
        
        //设置网面每个顶点高度
        for (int y = 0; y < mapHeight; y++)
        {
            for (int x = 0; x < mapWidth; x++)
            {
                heightMap[x + y * mapWidth] = (short) ((data[x * xoff + y * yoff * imgw] & 0x000000ff) * heightResolution);
            }
        }
        
        // Clear data
        data = null;
        img = null;
        System.gc();
    }
    
    /**
     * 按设置比例对地图所有面进行缩放、平移操作
     */
    private void setTransform()
    {
        Transform localTransform = new Transform();
        
        float scaleTimes = perUnit / 510;//地图拉伸倍数(createQuad创建的四边形边长是510)
        
        for (int x = 0; x < map.length - 1; x++)
        {
            for (int y = 0; y < map[x].length - 1; y++)
            {
                //归一化单位矩阵
                localTransform.setIdentity();
                //每个面平移到指定位置 
                localTransform.postTranslate(x * perUnit, 0.0f, y * perUnit);
                //localTransform.postTranslate(x * perUnit, 0.0f, (mapHeight - y) * -perUnit);
                //缩小网格
                localTransform.postScale(scaleTimes, scaleTimes, scaleTimes);
                //                localTransform.postMultiply(t);
                map[x][y].setTransform(localTransform);
            }
        }
        
    }
    
    /**
     * 根据高度图创建四边形
     * @param heights 四个定点的高度
     */
    private Mesh createQuad(short[] heights)
    {
        //创建顶点数组
        short[] POINTS = { -255, heights[0], -255, 255, heights[1], -255, 255, heights[2], 255,
                -255, heights[3], 255 };
        VertexArray POSITION_ARRAY = new VertexArray(POINTS.length / 3, 3, 2);
        POSITION_ARRAY.set(0, POINTS.length / 3, POINTS);
        VertexBuffer vertexBuffer = new VertexBuffer();
        vertexBuffer.setPositions(POSITION_ARRAY, 1.0f, null);
        
        //创建索引缓冲
        int INDICES[] = new int[] { 0, 1, 3, 2 };
        int[] LENGTHS = new int[] { 4 };
        IndexBuffer indexBuffer = new TriangleStripArray(INDICES, LENGTHS);
        
        //创建外观模式
        Appearance appearance = new Appearance();
        
        //设置颜色融合模式:纹理替代,不融合
        CompositingMode compositingMode = new CompositingMode();
        compositingMode.setBlending(CompositingMode.REPLACE);
        appearance.setCompositingMode(compositingMode);
        
        //多边形拾选模式:只显示正面、平滑渲染、透视矫正
        PolygonMode polygonmode = new PolygonMode();
        polygonmode.setCulling(PolygonMode.CULL_FRONT);
        polygonmode.setPerspectiveCorrectionEnable(true);
        polygonmode.setShading(PolygonMode.SHADE_SMOOTH);
        appearance.setPolygonMode(polygonmode);
        
        //纹理映射
        short[] TEXCOORDS = { 0, 0, 1, 0, 1, 1, 0, 1 };
        VertexArray TEXCOORD_ARRAY = new VertexArray(TEXCOORDS.length / 2, 2, 2);
        TEXCOORD_ARRAY.set(0, TEXCOORDS.length / 2, TEXCOORDS);
        vertexBuffer.setTexCoords(0, TEXCOORD_ARRAY, 1.0f, null);
        
        //纹理贴图
        appearance.setTexture(0, landTexture);
        
        Mesh mesh = new Mesh(vertexBuffer, indexBuffer, appearance);
        return mesh;
    }
    
    /*
     * 纹理贴图
     */
    private Texture2D createTexture(String textureImgSrc)
    {
        Image2D img = null;
        try
        {
            img = (Image2D) Loader.load(textureImgSrc)[0];
        }
        catch (Exception e)
        {
        }
        
        Texture2D texture = new Texture2D(img);
        texture.setFiltering(Texture2D.FILTER_NEAREST, Texture2D.FILTER_NEAREST);
        //设置纹理重复
        texture.setWrapping(Texture2D.WRAP_REPEAT, Texture2D.WRAP_REPEAT);
        return texture;
    }
}

 

创建基于公告板技术树的代码

import javax.microedition.m3g.Appearance;
import javax.microedition.m3g.CompositingMode;
import javax.microedition.m3g.Group;
import javax.microedition.m3g.Image2D;
import javax.microedition.m3g.IndexBuffer;
import javax.microedition.m3g.Mesh;
import javax.microedition.m3g.Node;
import javax.microedition.m3g.PolygonMode;
import javax.microedition.m3g.Texture2D;
import javax.microedition.m3g.TriangleStripArray;
import javax.microedition.m3g.VertexArray;
import javax.microedition.m3g.VertexBuffer;

public class Tree
{
    private Mesh tree;
    
    private Group cameraGroup;
    
    // the billboard aligns itself with this camera position
    
    public Tree(Image2D image2D, Group camGroup, float x, float z, float size)
    {
        cameraGroup = camGroup;
        
        //构造顶点缓冲
        VertexBuffer vertexBuffer = makeGeometry();
        
        //构造索引缓冲
        int[] indicies = { 1, 2, 0, 3 }; // the billboard is one quad
        int[] stripLens = { 4 };
        //三角带索引缓冲
        IndexBuffer indexBuffer = new TriangleStripArray(indicies, stripLens);
        
        Appearance appearance = makeAppearance(image2D);
        
        tree = new Mesh(vertexBuffer, indexBuffer, appearance);
        
        float size2 = size * 0.5f;
        /* The mesh is 2-by-2 in size, and so the extra 0.5 factor
           in the scaling reduces it to 1-by-1. */
        tree.scale(size2, size2, size2);
        tree.setTranslation(x, size2, z);
        
        tree.setAlignment(cameraGroup, Node.Z_AXIS, null, Node.NONE);
        /* The billboard alignment will be along its z-axis only,
           no y-axis alignment is employed. */
    }
    
    /*  The geometry defines a square resting on top of the XZ plane,
     *  centered at (0,0), with sides of length 2.
     *  There are no normals, but there are texture coords.
     */
    private VertexBuffer makeGeometry()
    {
        /* Create vertices, starting at the bottom left and going 
           counter-clockwise. */
        short[] POINTS = { -1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0 };
        VertexArray POSITION_ARRAY = new VertexArray(POINTS.length / 3, 3, 2);
        POSITION_ARRAY.set(0, POINTS.length / 3, POINTS);
        
        // create texture coordinates using the same order as the vertices
        short[] TEXCOORDS = { 0, 1, 1, 1, 1, 0, 0, 0 };
        VertexArray TEXCOORD_ARRAY = new VertexArray(TEXCOORDS.length / 2, 2, 2);
        TEXCOORD_ARRAY.set(0, TEXCOORDS.length / 2, TEXCOORDS);
        
        VertexBuffer vertexBuffer = new VertexBuffer();
        vertexBuffer.setPositions(POSITION_ARRAY, 1.0f, null); // no size, bias
        vertexBuffer.setTexCoords(0, TEXCOORD_ARRAY, 1.0f, null);
        
        return vertexBuffer;
    }
    
    /* The image's alpha component will cause the square's
     * surface to be invisible at those locations.
     */
    private Appearance makeAppearance(Image2D image2D)
    {
        Appearance appearance = new Appearance();
        
        //使用透明的颜色融合
        CompositingMode compositingMode = new CompositingMode();
        compositingMode.setBlending(CompositingMode.ALPHA);
        appearance.setCompositingMode(compositingMode);
        
        //设置多边形模式:只显示背面,允许透视修正
        PolygonMode polygonMode = new PolygonMode();
        polygonMode.setPerspectiveCorrectionEnable(true);
        polygonMode.setCulling(PolygonMode.CULL_BACK);
        appearance.setPolygonMode(polygonMode);
        
        if (image2D != null)
        {
            Texture2D texture2D = new Texture2D(image2D);
            texture2D.setFiltering(Texture2D.FILTER_NEAREST, Texture2D.FILTER_NEAREST);
            texture2D.setWrapping(Texture2D.WRAP_CLAMP, Texture2D.WRAP_CLAMP);
            texture2D.setBlending(Texture2D.FUNC_REPLACE);
            // the texture with replace the surface
            
            appearance.setTexture(0, texture2D);
        }
        return appearance;
    }
    
    public Mesh getMesh()
    {
        return tree;
    }
    
    // align the board's z-axis with the current position of the cameraGroup node
    public void align()
    {
        tree.align(cameraGroup);
    }
    
}

 

 

 

  • 大小: 66.1 KB
分享到:
评论

相关推荐

    J2ME的3D开发教程

    教程将详细介绍JSR-184的基本原理、核心功能以及实际应用案例,帮助读者掌握其编程技巧,实现高质量的3D画面呈现。 ##### Mascot Capsule v3:高性能3D引擎 Mascot Capsule v3是一款专为移动设备优化的3D引擎,...

    j2me 3d场景设计

    在本文中,我们将深入探讨如何使用Java 2 Micro Edition(J2ME)平台进行3D场景设计,特别是创建和操作3D五角星。J2ME是Java的一个轻量级版本,主要用于移动设备和嵌入式系统,它提供了一种在这些受限环境中实现3D...

    j2me实现3D效果

    文件“065-066 J2ME3D编程.ppt”很可能包含关于这些主题的详细讲解,包括实例代码和演示,这对于深入理解J2ME中的3D编程至关重要。学习这些内容将帮助开发者克服J2ME平台上的3D图形挑战,创造出引人入胜的移动游戏和...

    J2ME中3D实例讲义.rar

    在J2ME 3D应用中,可以通过更新模型的位置、旋转和缩放来实现动态效果。同时,需要处理用户输入,如触摸屏事件,以实现交互式3D体验。 7. **性能优化** 由于J2ME平台的资源限制,性能优化是3D应用开发中的重要...

    一个J2ME的3D菜单

    标题中的“一个J2ME的3D菜单”指的是在Java 2 Micro Edition(J2ME)平台上实现的一种具有三维效果的菜单系统。J2ME是Java的一个子集,主要用于开发移动设备、嵌入式系统等有限计算资源的平台,如早期的手机游戏和...

    Sonyericsson.Mobile.Java.3D.rar_j2me_j2me 3d_java 3d_mobile

    3. CANVAS组件:MIDP中的CANVAS类是实现3D图形的关键,它提供了自定义绘图的画布,开发者可以通过重写其draw()方法来绘制3D场景。 二、J2ME 3D核心技术 1. 3D模型:3D模型通常由顶点、法线和纹理坐标构成,通过JSR...

    j2me 3D

    在 J2ME 中实现 3D 图形通常涉及到以下几个关键知识点: 1. **MIDP (Mobile Information Device Profile)**:这是 J2ME 为移动设备定义的一种配置,它提供了基础的用户界面组件、网络连接和文件存储等功能,是开发 ...

    J2ME 手机3D游戏设计

    为了实现3D迷宫游戏,文章中采用了“立即模式”(Immediate Mode)的渲染方法。这种模式下,开发者可以直接控制图形的绘制,而不是依赖于图形库的缓存机制,从而在资源有限的环境下优化性能。同时,利用深度优先遍历...

    J2ME3D手机游戏开发详解(随书源码)

    通过研究这本书的随书源码,开发者不仅可以学习到J2ME3D游戏开发的基本概念和技术,还能掌握实际项目中的最佳实践,提升自己的编程能力。这些知识点对于想要进入移动游戏开发领域的程序员来说,是一份宝贵的资源。

    基于j2me的3d游戏

    在本文中,我们将深入探讨基于J2ME的3D游戏开发,以及如何利用J2ME实现各种不同类型的游戏。 **1. J2ME游戏框架** 在J2ME中开发3D游戏,通常会使用如MIDP(Mobile Information Device Profile)和CLDC(Connected ...

    J2ME的3d开发教程.doc

    教程结构包括示例代码和详细的解释,帮助读者理解如何使用这些API来创建3D对象、管理场景、实现交互和动画效果。每篇文章都配有简单的示例,以便读者能够直观地看到每个方法的应用和结果。这种实践导向的学习方式有...

    J2ME 3D手机游戏开发详解

    在J2ME中开发3D游戏,需要理解其核心组件CLDC (Connected Limited Device Configuration) 和MIDP (Mobile Information Device Profile),以及如何利用它们来创建3D环境。 起步篇首先介绍了J2ME的基本概念,包括CLDC...

    J2ME中文输入Demo

    本"J2ME中文输入Demo"旨在为开发者展示如何在J2ME环境中实现基本的中文输入功能。虽然这个Demo可能不完美,但其核心概念和方法对于理解和构建自己的中文输入解决方案是非常有价值的。 1. **TextBox与TextField** ...

    J2ME 3D魔方游戏

    J2ME并不直接支持复杂的3D图形处理,但开发者可以通过Java 2D API来模拟3D效果,如使用二维图像(精灵)和视图变换来构建三维场景。在这个游戏中,开发者可能利用了旋转、平移和缩放等2D变换来实现魔方的转动和视角...

    J2ME源码 3d_menu(代码)

    【J2ME源码 3d_menu(代码)】是一个基于Java 2 Micro Edition (J2ME) 平台开发的项目,它提供了一个三维菜单的实现。J2ME是Java的一个子集,专为资源有限的移动设备如手机和嵌入式系统设计,用于开发和部署应用程序...

    J2ME 3D手机游戏开发详解 part2

    J2ME 3D手机游戏开发详解 完整版 分为3部分

    J2ME 3D手机游戏开发详解代码和书籍

    由于移动设备的资源限制,性能优化在J2ME 3D游戏开发中尤为重要。这可能包括减少多边形数量、优化纹理贴图、使用适当的缓存策略以及避免不必要的计算。 **书籍资源** 提供的书籍很可能是关于J2ME 3D游戏开发的教程...

    基于j2me的手机3D赛车游戏源码

    通过对【GameCarTerrain】这个文件的分析,我们可以深入学习如何在j2ME环境中实现3D场景的构造,如何创建动态的赛车运动,以及如何处理游戏中的碰撞检测和物理模拟。此外,通过查看源码,还可以学习到错误处理、资源...

    j2me 开发手机3D图形程序源代码

    在 J2ME 中,3D 图形的实现主要依赖于 OpenGL ES(Embedded Systems)的简化版本,称为 JSR-184 或者 M3G (Mobile 3D Graphics API)。这个API提供了创建、管理和渲染3D模型的基本功能,包括顶点坐标、纹理映射、光照...

Global site tag (gtag.js) - Google Analytics