📄 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 java.util.Random;import java.util.Timer;import java.util.TimerTask;import javax.microedition.lcdui.*;import javax.microedition.m3g.*;import javax.microedition.midlet.MIDlet;import javax.microedition.midlet.MIDletStateChangeException;/** * 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 { // Survival and birth parameters for individual cells. private static final int minSurvive = 4; private static final int maxSurvive = 5; private static final int minBirth = 5; private static final int maxBirth = 5; // The cube size must be a power of 2 for the optimized wrapping // code to work properly, so to ensure this, we specify the exponent // here. 2 to the 3 is 8, so the cube is 8x8x8. private static final int CUBESIZE_BITS = 3; // The number of cells along each edge of the cube private static final int CUBESIZE = (1 << CUBESIZE_BITS); // The total number of cells in the cube private static final int NUMCELLS = (CUBESIZE * CUBESIZE * CUBESIZE); // Various masks and values, used in the optimized update routine private static final int STEPX = (CUBESIZE * CUBESIZE); private static final int STEPY = (CUBESIZE); private static final int STEPZ = 1; private static final int MASKX = STEPX * (CUBESIZE - 1); private static final int MASKY = STEPY * (CUBESIZE - 1); private static final int MASKZ = STEPZ * (CUBESIZE - 1); private static final int SHIFTX = CUBESIZE_BITS * 2; private static final int SHIFTY = CUBESIZE_BITS; private static final int SHIFTZ = 0; // Variables. // Reference to the MIDlet's display private Display myDisplay = null; // Reference to the current canvas. private CallbackCanvas myCanvas = null; // Reference to a refresh timer, which is used to schedule screen updates private Timer myRefreshTimer = new Timer(); // Reference to a TimerTask-derived object that calls back to the MIDlet private TimerTask myRefreshTask = null; // A command which is used to exit from the MIDlet. private Command exitCommand = new Command("Exit", Command.ITEM, 1); // Reference to the world which contains the 3D cells. private World myWorld = null; // The root group, which is the one we rotate slowly. private Group rootGroup = null; // References to the 3D objects that represent the cells, // one for each cell in the grid. private Mesh[] cells; // Array of neighbor counts for calculating the next state, // one for each cell in the grid. private byte[] nextState; // Array of cell states (1=alive, 0=dead), one for each cell // in the grid. This is not held as a boolean, so that we can // then use this value directly to update the number of live // neighbors. private byte[] currentState; // Reference to a random number generator. private Random rand = null; // Current viewing angle. This rotates slowly. private float angle = 0.0f; // Delay (number of frames) between one generation and the next. private int delay = 1; // Delay until next generation. This counts down from "delay" to 0, // and a new generation is calculated when it hits 0. private int delayCount = 0; // Count of number of live cells. private int population = 0; // Number of generations calculated since last reload. private int generations = 0; // Current pattern number from library. private int pattern = -1; // Current pattern name from library. private String patternName = "Random"; // Pattern library - names and state strings for a number of interesting // patterns - feel free to add your own! private String[][] patternLibrary = { { "L block ", "433434443533534543", }, { "Skew L block ", "333334343344433444", }, { "Opposed L block ", "254263264353354363", }, { "Beehive ", "353344364355453444464455", }, { "Skew beehive ", "323333412421423431433442521531", }, { "Broken beehive ", "434443454535546555645646343344", }, { "Skew beehive plus", "254264345354356364366375445456466", }, { "Beehive var 75 ", "515516524525605606634635715716724725", }, { "Ascend+descend ", "034044133143145243246256344345354355", }, { "Battered ring ", "235244245325336354355425434446455535545", }, { "Ring ", "243253334335346343356353364365434435446456464465", }, { "Cage ", "334345455454424435433554534545543", }, { "Skew cage ", "413422424433513514532533623", }, { "Ball ", "326335337346425427445447526535537546", }, { "Ball 1 cap ", "326335337346425427445447526535537546235", }, { "Ball 2 caps ", "326335337346425427445447526535537546235635", }, { "Ball 2 caps hole ", "445447456416425427546526537346326335", }, { "Pinwheel ", "213214223224303315322334413414423424", }, { "Columns ", "445447457546556245255344346354", }, { "Columns ext ", "445447457546556245255344346354144243043053142152", }, { "Staircase ", "462472540541561563572640641650652", }, { "Cupped hands ", "035045134136144226234236244325335", }, { "New 1 ", "013022114113125122134133225234", }, { "New 2 ", "422423432522523544543634644643", }, { "New 3 ", "344455433435443446554555534543546", }, { "New 4 ", "111112202210213222301302310321411", }, { "New 5 ", "343353442454452545542555533644643654", }, { "New 6 ", "440450452461541550241251252340342362", }, { "New 7 ", "427434444447535536545546326335336337", }, { "New 8 ", "241250252261340341360362372450461471", }, { "New 9 ", "414415435433524525234233344343325323", }, { "New 10 ", "456466165166175176257264267274357365367", }, { "New 11 ", "156165166255265266472502562571573601661663762771772", }, { "New 12 ", "124125134136224226233237246336343347354356444446454455", }, { "Compass ", "333432434423443533", }, { "P2 Wonky Compass ", "344454434443445554534", }, { "P2 Double Compass", "461462552553572573661662", }, { "P2 Blinker ", "151161241250252260262271351361", }, { "P2 Repeater ", "445446534536544555645646", }, { "P2 Propellor ", "444424425436545535345334336", }, { "P2 Magic Basket ", "344345354334446443424425436546543524525533644645655635", }, { "P4 Boat ", "333334423424432435443444", }, { "P4 Rotor ", "225235316325327335337346426436", }, { "P4 Beehive ", "353344364355365453444464455465", }, { "P4 Shuffler ", "356445447455466545547555566656", }, { "P4 Popper ", "333334343344433435445454534544", }, { "P4 Glider ", "111112121122013023010020001002", }, }; /** * 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 exceptions 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 appearance 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++) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -