?? life3d.java
字號:
/******************************************************************************//** * @file Life3D.java * @brief Implements a 3D version of Conway's "game of life". * * Copyright (C) 2004 Superscape plc * * This file is intended for use as a code example, and * may be used, modified, or distributed in source or * object code form, without restriction, as long as * this copyright notice is preserved. * * The code and information is provided "as-is" without * warranty of any kind, either expressed or implied. *//******************************************************************************/package com.superscape.m3g.wtksamples.life3d;import javax.microedition.midlet.MIDlet;import javax.microedition.midlet.MIDletStateChangeException;import javax.microedition.lcdui.*;import java.util.Random;import java.util.Timer;import java.util.TimerTask;import javax.microedition.m3g.*;/** * Life3D Midlet - implements a 3D version of Conway's "game of life". * * The "game of life" is an interesting system discovered by John Conway in 1968. * This is a generalization of that system to 3D. * * The game grid is divided into cubical cells, each of which can be alive or dead. * A live cell is indicated by a cube at that position; a dead cell is vacant. * * At each generation, the number of living neighbours for each cell is counted, and * the state of the cell in the next generation is determined by that count. Each has * 26 possible neighbors (including ones that are diagonal in one or more directions), * and if a live cell has fewer than 4 live neighbors, it dies of loneliness. If it has * more than 5 live neighbors, it dies of overcrowding. A vacant cell will have a new * cell created inside it if it has exactly 4 live neighbors. * * This apparently simple rule, when applied to all the cells simultaneously, leads * to surprisingly complex behavior. Certain patterns of cells are stable, some die * out, some flip between two or more configurations, and some actually progress through * the grid in an ordered manner (these are called "gliders"). * * Although the grid starts in a random state, we have added the most interesting patterns * we have found to a pattern library, which can be accessed from the keyboard. * * Keys: * 0: Pause generation * 1: Full speed (1 generation per frame) * 2: Increase generation speed * 3: Decrease generation speed * 4: Load previous pattern in library * 5: Load next pattern in library * *: Load random pattern * * If a pattern dies out completely, then the game restarts with a random pattern again. */public class Life3D extends MIDlet implements CommandListener{ /** * Default constructor. * * This just sets up a canvas and attaches it to the display. The actual * initialization happens in startApp. */ public Life3D() { // Set up the user interface. myDisplay = Display.getDisplay(this); myCanvas = new CallbackCanvas(this); myCanvas.setCommandListener(this); myCanvas.addCommand(exitCommand); } /** * This initializes the game state, and generates a M3G world programmatically. */ public void startApp() throws MIDletStateChangeException { // Catch excpetions here before they go too far. try { // Create a new M3G world. myWorld = new World(); // In this world, we have a root group which will contain everything else // and which is tilted 15 degrees towards the camera. Group rootGroup2 = new Group(); myWorld.addChild(rootGroup2); rootGroup2.setOrientation(15.0f,1.0f,0.0f,0.0f); // Under this, we have a second group which will be the one we rotate // to get an all-round view of the game grid. rootGroup = new Group(); rootGroup2.addChild(rootGroup); // We now create a parallel camera - parallel projection is faster than // perspective, and since we are rendering 512 separate objects that's a // saving worth having. Camera myCamera = new Camera(); myWorld.addChild(myCamera); myWorld.setActiveCamera(myCamera); myCamera.setParallel(CUBESIZE*1.5f, 1.0f, -CUBESIZE, CUBESIZE); // This is geometry data for the shape that represents a single cell - a cube. // It consists of 6 triangle strips, one for each face, each of which // has 2 triangles (and therefore 4 vertices). We will set the vertex // colors so that the colors of the sides are different from each other. // This data is shared by all the cells in the grid, rather than each having // its own copy. This keeps memory overhead down. int[][] aaStripLengths = {{4}, {4}, {4}, {4}, {4}, {4}}; // These are the vertex positions short[] aPos = { // Front -1, -1, 1, // B 1, -1, 1, // C -1, 1, 1, // A 1, 1, 1, // D // Bottom -1, -1, -1, // F 1, -1, -1, // G -1, -1, 1, // B 1, -1, 1, // C // Top -1, 1, 1, // A 1, 1, 1, // D -1, 1, -1, // E 1, 1, -1, // H // Right 1, 1, 1, // D 1, -1, 1, // C 1, 1, -1, // H 1, -1, -1, // G // Left -1, -1, 1, // B -1, 1, 1, // A -1, -1, -1, // F -1, 1, -1, // E // Back 1, -1, -1, // G -1, -1, -1, // F 1, 1, -1, // H -1, 1, -1 // E }; // These are the colors for the vertices byte[] aCol = { // Front -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, // Bottom 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, // Top 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, // Right -1, -1, 0, -1, -1, 0, -1, -1, 0, -1, -1, 0, // Left -1, 0, -1, -1, 0, -1, -1, 0, -1, -1, 0, -1, // Back 0, -1, -1, 0, -1, -1, 0, -1, -1, 0, -1, -1, }; // Calculate the number of submeshes and vertices directly from the sizes // of the arrays. This prevents us getting a mismatch if we decide to change // the cells to a different shape later. int cSubmeshes = aaStripLengths.length; int cVertices = aPos.length/3; // We will share a default appearance between all the faces on the cube. Each // face is a separate "submesh" - it can have a separate appearance if we wish. Appearance app = new Appearance(); // We need to specify an appearance and the submesh data for each face. Appearance[] appa = new Appearance[cSubmeshes]; IndexBuffer[] iba = new IndexBuffer[cSubmeshes]; int startIndex=0; for(int i=0;i<cSubmeshes;i++) { // We use the same apppearance for each. appa[i]=app; // And we create a new triangle strip array for each submesh. // The start index for each one just follows on from previous submeshes. iba[i] = new TriangleStripArray(startIndex, aaStripLengths[i]); for(int j=0; j<aaStripLengths[i].length;j++) startIndex+=aaStripLengths[i][j]; } // Now we create a new vertex buffer that contains all the above information VertexBuffer vertexBuffer = new VertexBuffer(); vertexBuffer.setDefaultColor(0xFFFFFFFF); // white { // Copy the vertex positions into a VertexArray object VertexArray vaPos = new VertexArray(cVertices, 3, 2); vaPos.set(0, cVertices, aPos); vertexBuffer.setPositions(vaPos, 0.40f, null); } { // Copy the vertex colors into a VertexArray object VertexArray vaCols = new VertexArray(cVertices, 3, 1); vaCols.set(0, cVertices, aCol); vertexBuffer.setColors(vaCols); } // Create all the cells, in a random state. // The X, Y and Z positions of the cells range from -CUBESIZE/2 to +CUBESIZE/2 units. // They are all children of the rootGroup object. cells = new Mesh[NUMCELLS]; nextState = new byte[NUMCELLS]; currentState = new byte[NUMCELLS]; rand = new Random(); int index = 0; for(int i=0; i<CUBESIZE; i++) { float x = (i*2 - CUBESIZE) * 0.5f; for(int j=0; j<CUBESIZE; j++) { float y = (j*2 - CUBESIZE) * 0.5f; for(int k=0; k<CUBESIZE; k++) { float z = (k*2 - CUBESIZE) * 0.5f; Mesh m = new Mesh(vertexBuffer, iba, appa); m.setTranslation(x,y,z); rootGroup.addChild(m); // This test gives a 1 in 4 chance of being alive at the start currentState[index]=(rand.nextInt()>0x40000000)?(byte)1:(byte)0; cells[index++] = m; } } } // Attach to display myDisplay.setCurrent(myCanvas); // Force a repaint so that we get the update loop started. myCanvas.repaint(); } catch(Exception e) { e.printStackTrace(); } } /** * If cell[i] is alive, this increments the "live neighbor" count * on all the neighboring cells. This is more efficient than counting * the neighboring cells for each cell, because there are likely to be * fewer live cells than dead cells. * * The cube wraps around, so that the neighbor to the right of a cell * in the last row is in fact in the first row. The same happens with * columns and planes. We speed things up here by using bit operations * to effect this wrapping (which is why CUBESIZE must be a power of 2) * and we also unroll the loop. */ public void updateNeighbours(int i) { if(currentState[i]!=0) { int ix0 = (i-STEPX)&MASKX; int iy0 = (i-STEPY)&MASKY; int iz0 = (i-STEPZ)&MASKZ; int ix1 = (i)&MASKX; int iy1 = (i)&MASKY; int iz1 = (i)&MASKZ; int ix2 = (i+STEPX)&MASKX; int iy2 = (i+STEPY)&MASKY; int iz2 = (i+STEPZ)&MASKZ; ++nextState[ix0|iy0|iz0]; ++nextState[ix0|iy0|iz1]; ++nextState[ix0|iy0|iz2]; ++nextState[ix0|iy1|iz0]; ++nextState[ix0|iy1|iz1]; ++nextState[ix0|iy1|iz2]; ++nextState[ix0|iy2|iz0]; ++nextState[ix0|iy2|iz1]; ++nextState[ix0|iy2|iz2]; ++nextState[ix1|iy0|iz0]; ++nextState[ix1|iy0|iz1]; ++nextState[ix1|iy0|iz2]; ++nextState[ix1|iy1|iz0]; //! ++nextState[ix1|iy1|iz1]; ++nextState[ix1|iy1|iz2]; ++nextState[ix1|iy2|iz0]; ++nextState[ix1|iy2|iz1]; ++nextState[ix1|iy2|iz2]; ++nextState[ix2|iy0|iz0]; ++nextState[ix2|iy0|iz1]; ++nextState[ix2|iy0|iz2]; ++nextState[ix2|iy1|iz0]; ++nextState[ix2|iy1|iz1]; ++nextState[ix2|iy1|iz2]; ++nextState[ix2|iy2|iz0]; ++nextState[ix2|iy2|iz1]; ++nextState[ix2|iy2|iz2]; } } /** * Works out current alive/dead state based on neighbour count. * If a cell is alive, it will die of loneliness if it has at fewer than * minSurvive neighbors, but if it has more than maxSurvive neighbors it * will die of overcrowding. If a cell has between minBirth and maxBirth * neighbours, and it is currently dead, a new cell is born in that position. */ public void updateState(int i) { byte count = nextState[i]; nextState[i]=0; if(currentState[i]==0) { currentState[i]=(count>=minBirth && count<=maxBirth)?(byte)1:(byte)0; } else { currentState[i]=(count>=minSurvive && count<=maxSurvive)?(byte)1:(byte)0; } // After calculating the new state, set the appropriate rendering enable for the // cell object in the world, so we can see it. We take advantage of this test to // count the current live population, too. if(currentState[i]!=0) { cells[i].setRenderingEnable(true); ++population; } else cells[i].setRenderingEnable(false); } /** * On pause, simply shut everything down. */ public void pauseApp() { myRefreshTask.cancel(); myRefreshTask = null; // Release resources. myWorld = null; } /** * On exit, simply shut everything down */ public void destroyApp(boolean unconditional) throws MIDletStateChangeException { myRefreshTimer.cancel(); myRefreshTimer = null; myRefreshTask = null; // Release resources. myWorld = null; myCanvas = null; } /** * MIDlet paint method. * * This is called back from the inner Canvas class. It renders the current state of the * cells, then updates them and schedules another update. */ public void paint(Graphics g) {
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -