首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > JAVA > J2ME开发 >

J2ME中3D场景遨游的实现

2012-09-14 
J2ME中3D场景漫游的实现???? 好吧,你该知道为什么我这叫跳票工厂,其实这玩意还没做完。不过高度图、场景漫游

J2ME中3D场景漫游的实现

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

??


J2ME中3D场景遨游的实现

运行截图
?

?

???? 几点要说明的:

???? 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);    }    }

?

?

?

热点排行