📄 life3d.java
字号:
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) { // We are not fully initialised yet; just return. if ((myCanvas == null) || (myWorld == null)) { return; } // If this was a scheduled update, cancel the timer task that caused it. if (myRefreshTask != null) { myRefreshTask.cancel(); myRefreshTask = null; } // Render the world to our Graphics (the screen) using the m3g class Graphics3D // Note the use of try/finally to ensure that the Graphics3D always releases // the target no matter what happens. Graphics3D myGraphics3D = Graphics3D.getInstance(); myGraphics3D.bindTarget(g); try { myGraphics3D.render(myWorld); } finally { myGraphics3D.releaseTarget(); } // Draw information about the pattern: its name, the number of generations, // and the number of live cells. g.setColor(0xFFFFFFFF); g.drawString(generations+" : "+population, 0, Font.SIZE_LARGE, Graphics.LEFT|Graphics.TOP); g.drawString(patternName, 0, 0, Graphics.LEFT|Graphics.TOP); // Always rotate slowly. We set the orientation of rootGroup to achieve this, // rotating it about its local Y (vertical) axis. angle += 1.0f; rootGroup.setOrientation(angle, 0.0f, 1.0f, 0.0f); // This deals with the speed setting. We have a countdown that is set to // an initial value of "delay", and when it reaches 0, we update the cells. // This allows us to keep rotating the view, even if generation is paused. if (--delayCount <= 0) { delayCount = delay; ++generations; // Garbage collect regularly System.gc(); // Now calculate next frame // Update all the cells for (int i = 0; i < NUMCELLS; i++) updateNeighbours(i); population = 0; for (int i = 0; i < NUMCELLS; i++) updateState(i); // If all the cells have died out, restart with a random state. if (population == 0) { loadRandomState(); } } // And schedule another repaint. This uses the Java timers to schedule // another repaint in 50 milliseconds. This is more efficient than doing // it immediately, and limits the frame rate to 20 frames/second. It also // allows other applications some time! myRefreshTask = new RefreshTask(); myRefreshTimer.schedule(myRefreshTask, 50); } /** * This loads the cell state from a string stored in the pattern library. * Each live cell is represented in the string using a triplet of characters * showing its x, y and z position. For example, the string "000" represents * a single live cell at the extreme bottom front right corner. */ private void loadStateFromString(String positions) { clear(); for (int i = 0; i < positions.length(); i += 3) { int xx = (positions.charAt(i + 0) - '0') & (CUBESIZE - 1); int yy = (positions.charAt(i + 1) - '0') & (CUBESIZE - 1); int zz = (positions.charAt(i + 2) - '0') & (CUBESIZE - 1); currentState[(((xx * CUBESIZE) + yy) * CUBESIZE) + zz] = 1; } } /** * Clears the cell array to all dead. */ private void clear() { for (int i = 0; i < NUMCELLS; i++) currentState[i] = 0; generations = 0; } /** * Loads the cell array with a random state, with approximately 25% * of the cells alive. */ private void loadRandomState() { patternName = "Random"; for (int i = 0; i < NUMCELLS; i++) currentState[i] = (rand.nextInt() > 0x40000000) ? (byte)1 : (byte)0; generations = 0; } /** * Handle commands. * Currently, the only command enabled is "Exit" - this just * destroys the MIDlet immediately. */ public void commandAction(Command cmd, Displayable disp) { if (cmd == exitCommand) { try { destroyApp(false); notifyDestroyed(); } catch (Exception e) { e.printStackTrace(); } } } /** * Handles key presses. * This is called back from the inner Canvas class, and implements * the behavior of the various keys outlined in the class summary. */ public void keyPressed(int keyCode) { switch (keyCode) { // 0,1,2,3 are speed keys. // 0: Pause - set a very long delay until the next update case Canvas.KEY_NUM0: delay = 1000000; delayCount = 1000000; break; // 1: Full speed - set no delay until next update case Canvas.KEY_NUM1: delay = 0; delayCount = 0; break; // 2: Speed up - if not already at full speed, decrease delay case Canvas.KEY_NUM2: if (delay > 0) { delay--; } delayCount = 0; break; // 3: Slow down - if not already at minimum speed, increase delay case Canvas.KEY_NUM3: if (delay < 20) { delay++; } delayCount = 0; break; // 4,5 are pattern keys // 4: Select and load previous pattern in library. If at the // start of the library, or using a random pattern, will start // again at the last pattern. case Canvas.KEY_NUM4: pattern--; if (pattern < 0) { pattern = patternLibrary.length - 1; } patternName = patternLibrary[pattern][0]; loadStateFromString(patternLibrary[pattern][1]); break; // 5: Select and load next pattern in library. If at the // end of the library, or using a random pattern, will start // again at the first pattern. case Canvas.KEY_NUM5: pattern++; if (pattern >= patternLibrary.length) { pattern = 0; } patternName = patternLibrary[pattern][0]; loadStateFromString(patternLibrary[pattern][1]); break; // *: Loads a random state case Canvas.KEY_STAR: loadRandomState(); break; } } /** * Inner TimerTask-derived class for refreshing the view. */ private class RefreshTask extends TimerTask { public void run() { // Get the canvas to repaint itself. myCanvas.repaint(); } } /** * Inner Canvas-derived class for handling canvas events. */ private class CallbackCanvas extends Canvas { Life3D mymidlet; /** * Construct a new canvas */ CallbackCanvas(Life3D midlet) { mymidlet = midlet; } /** * Initialize self. */ void init() { } /** * Cleanup and destroy. */ void destroy() { } /** * Ask mymidlet to paint itself */ protected void paint(Graphics g) { mymidlet.paint(g); } protected void keyPressed(int keyCode) { mymidlet.keyPressed(keyCode);} }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -