MazeCanvas.java
上传用户:gyyuli
上传日期:2013-07-09
资源大小:3050k
文件大小:20k
源码类别:

J2ME

开发平台:

Java

  1. import java.util.*;
  2. import javax.microedition.lcdui.*;
  3. import javax.microedition.lcdui.game.*;
  4. import javax.microedition.m3g.*;
  5. // Main class. It setups the 3D scene and does the movements
  6. // and rendering
  7. class MazeCanvas
  8.   extends GameCanvas
  9.   implements Runnable
  10. {
  11.   // Height of the walls
  12.   private final static float WALL_HEIGHT = 10.f;
  13.   // overall side of the maze
  14.   private final static float MAZE_SIDE_LENGTH =200f;
  15.   // amount to advance at each step
  16.   private final static float STEP = 3.0f;
  17.   // amount to rotate each time
  18.   private final static float ANGLE_STEP = 8.0f;
  19.   // millis to wait in the main loop
  20.   private final static int MILLIS_PER_TICK = 5;
  21.   // Size of the steps to directions x and z
  22.   // stored to avoid extra trigonometric calculations
  23.   private float stepz = -STEP, stepx = 0f;
  24.   // player's location
  25.   private float locationx = 0f, locationz = 0;
  26.   // player's rotation
  27.   private float angley = 0f;
  28.   // flags whether we are looking the game from the top
  29.   private boolean topView = false;
  30.   // flags to keep the drawing going
  31.   private volatile boolean playing = true;
  32.   // stores the amount of frames per second being displayed
  33.   private volatile float fps = 0;
  34.   // stores how long has taken to run thorugh the maze
  35.   private volatile long gameStart = 0;
  36.   // stores how long has taken to run thorugh the maze
  37.   private volatile long duration = 0;
  38.   // indicates whether the game has finished
  39.   private boolean finished;
  40.   // Parent MIDlet
  41.   private final Maze3DMIDlet midlet;
  42.   // Graphics 3D global instance
  43.   private Graphics3D g3d;
  44.   // the world of the application
  45.   private World world;
  46.   // the world has 2 cameras
  47.   private Camera camera, topCamera;
  48.   // world's background
  49.   private Background background = null;
  50.   // simple rectangular mesh used to locate the player
  51.   private Mesh locationSquare = null;
  52.   // wall alternate appeareances
  53.   private Appearance wallClearAppearance = new Appearance();
  54.   private Appearance wallAppearance = new Appearance();
  55.   // points to the mesh last made semi transparent
  56.   private Mesh transparentMesh = null;
  57.   private Maze maze = new Maze(10, MAZE_SIDE_LENGTH, WALL_HEIGHT);
  58.   private Thread mainThread = null;
  59.   // Construct the Displayable
  60.   MazeCanvas(Maze3DMIDlet midlet)
  61.   {
  62.     super(true);
  63.     this.midlet = midlet;
  64.   }
  65.   // (Re)Starts the game
  66.   void start()
  67.   {
  68.     playing = true;
  69.     gameStart = System.currentTimeMillis() - duration;
  70.     mainThread = new Thread(this);
  71.     mainThread.start();
  72.   }
  73.   // stops/pauses the game
  74.   void stop()
  75.   {
  76.     duration = System.currentTimeMillis() - gameStart;
  77.     playing = false;
  78.   }
  79.   // switches from top view to normal view and vice versa
  80.   void switchView()
  81.   {
  82.     topView = !topView;
  83.     setView();
  84.     // informs the MIDLet since this can be switched internally
  85.     midlet.viewSwitched();
  86.   }
  87.   void setView()
  88.   {
  89.     // reset the locaiton and draw on top of the maze
  90.     locationSquare.setTranslation(locationx,
  91.                                   2 * WALL_HEIGHT + 3f, locationz);
  92.     // the square is rendered only in top view
  93.     locationSquare.setRenderingEnable(topView);
  94.     // the background is removed in top view
  95.     world.setBackground(topView ? null : background);
  96.     // sets the actual active camera
  97.     if (topView)
  98.     {
  99.       world.setActiveCamera(topCamera);
  100.     }
  101.     else
  102.     {
  103.       world.setActiveCamera(camera);
  104.     }
  105.   }
  106.   // Builds the world
  107.   void init()
  108.   {
  109.     setFullScreenMode(true);
  110.     duration = 0;
  111.     // get a Graphics3D instance
  112.     g3d = Graphics3D.getInstance();
  113.     // build the world and the cameras
  114.     world = new World();
  115.     camera = new Camera();
  116.     topCamera = new Camera();
  117.     world.addChild(camera);
  118.     world.addChild(topCamera);
  119.     float w = (float)getWidth();
  120.     float h = (float)getHeight();
  121.     // set the perspective
  122.     camera.setPerspective(60.f, w / h, 0.1f, 1000.f);
  123.     topCamera.setPerspective(60.f, w / h, 0.1f, 1000.f);
  124.     // the top camer has a fixed transform looking from the top
  125.     Transform topCameraTransform = new Transform();
  126.     topCameraTransform.postRotate(90, -1f, 0f, 0f);
  127.     topCameraTransform.postTranslate(0f, 0f,MAZE_SIDE_LENGTH);
  128.     //topCameraTransform.postTranslate(0f, 0f, 1.2f * MAZE_SIDE_LENGTH);
  129.     topCamera.setTransform(topCameraTransform);
  130.     // Setup the background
  131.     background = new Background();
  132.     Image backgroundImage = Maze3DMIDlet.makeImage("/background.png");
  133.     if (backgroundImage != null)
  134.     {
  135.       background.setImage(new Image2D(Image2D.RGB, backgroundImage));
  136.       background.setImageMode(Background.REPEAT, Background.REPEAT);
  137.     }
  138.     world.setBackground(background);
  139.     createFloor();
  140.     createLocationSquare();
  141.     setUpMaze();
  142.     createStartEndMarks();
  143.     setView();
  144.     // setup the initial location
  145.     locationx = maze.findStartLocationX();
  146.     locationz = maze.findStartLocationZ();
  147.     // look at the center
  148.     angley = -180f;
  149.     setupCamera();
  150.     start();
  151.   }
  152.   // creates the labeld at the start and at the end of the maze
  153.   private void createStartEndMarks()
  154.   {
  155.     // The marks' appearance
  156.     Appearance startMarkAppearance = new Appearance();
  157.     Appearance endMarkAppearance = new Appearance();
  158.     // The composite mode is ALPHA to show only the letters
  159.     // the background is hidden using the alpha layer
  160.     CompositingMode markCompositeMode = new CompositingMode();
  161.     markCompositeMode.setBlending(CompositingMode.ALPHA);
  162.     startMarkAppearance.setCompositingMode(markCompositeMode);
  163.     endMarkAppearance.setCompositingMode(markCompositeMode);
  164.     // The label's text is built using a texture
  165.     Texture2D startMarkTexture = null;
  166.     Image startMarkTextureImage =
  167.         Maze3DMIDlet.makeImage("/start_label.png");
  168.     if (startMarkTextureImage != null)
  169.     {
  170.       startMarkTexture = new Texture2D(
  171.           new Image2D(Image2D.RGBA, startMarkTextureImage));
  172.       // the texture is not repeated
  173.       startMarkTexture.setWrapping(Texture2D.WRAP_CLAMP,
  174.                                    Texture2D.WRAP_CLAMP);
  175.       startMarkTexture.setBlending(Texture2D.FUNC_REPLACE);
  176.       startMarkTexture.setFiltering(Texture2D.FILTER_NEAREST,
  177.                                     Texture2D.FILTER_NEAREST);
  178.       startMarkAppearance.setTexture(0, startMarkTexture);
  179.     }
  180.     Texture2D endMarkTexture = null;
  181.     Image endMarkTextureImage =
  182.         Maze3DMIDlet.makeImage("/end_label.png");
  183.     if (endMarkTextureImage != null)
  184.     {
  185.       endMarkTexture = new Texture2D(
  186.           new Image2D(Image2D.RGBA, endMarkTextureImage));
  187.       // the texture is not repeated
  188.       endMarkTexture.setWrapping(Texture2D.WRAP_CLAMP,
  189.                                  Texture2D.WRAP_CLAMP);
  190.       endMarkTexture.setBlending(Texture2D.FUNC_REPLACE);
  191.       endMarkTexture.setFiltering(Texture2D.FILTER_NEAREST,
  192.                                   Texture2D.FILTER_NEAREST);
  193.       endMarkAppearance.setTexture(0, endMarkTexture);
  194.     }
  195.     // create the start mesh
  196.     Plane startMarkPlane = maze.createStartMark();
  197.     Mesh startMarkMesh = startMarkPlane.createMesh();
  198.     startMarkMesh.setAppearance(0, startMarkAppearance);
  199.     // these are not pickable
  200.     startMarkMesh.setPickingEnable(false);
  201.     world.addChild(startMarkMesh);
  202.     // creates the end mesh
  203.     Plane endMarkPlane = maze.createEndMark();
  204.     Mesh endMarkMesh = endMarkPlane.createMesh();
  205.     endMarkMesh.setAppearance(0, endMarkAppearance);
  206.     // is not pickable either
  207.     endMarkMesh.setPickingEnable(false);
  208.     // Create a sequence of 4 keyframes
  209.     KeyframeSequence keyframes =
  210.         new KeyframeSequence(4,3, KeyframeSequence.LINEAR);
  211.     keyframes.setDuration(12000);
  212.     keyframes.setRepeatMode(KeyframeSequence.LOOP);
  213.     float[] trans = new float[4];
  214.     endMarkMesh.getOrientation(trans);
  215.     // create set the keyframes.
  216.     // Basically keep x and z and animate in the y axis
  217.     keyframes.setKeyframe(0, 0,
  218.         new float[]{trans[0], trans[1], trans[2]});
  219.     keyframes.setKeyframe(1, 4000,
  220.         new float[]{trans[0], trans[1] - WALL_HEIGHT / 4, trans[2]});
  221.     keyframes.setKeyframe(2, 8000,
  222.         new float[]{trans[0], trans[1] + WALL_HEIGHT / 4, trans[2]});
  223.     keyframes.setKeyframe(3, 12000,
  224.         new float[]{trans[0], trans[1], trans[2]});
  225.     AnimationTrack animationTrack =
  226.         new AnimationTrack(keyframes, AnimationTrack.TRANSLATION);
  227.     AnimationController anim = new AnimationController();
  228.     animationTrack.setController(anim);
  229.     // add the animation track
  230.     endMarkMesh.addAnimationTrack(animationTrack);
  231.     world.addChild(endMarkMesh);
  232.   }
  233.   // creates the floor
  234.   private void createFloor()
  235.   {
  236.     float floorSide = MAZE_SIDE_LENGTH / 2;
  237.     // define the location and size of the floor
  238.     Transform floorTransform = new Transform();
  239.     floorTransform.postRotate(90.0f, -1.0f, 0.0f, 0.0f);
  240.     floorTransform.postScale(floorSide, floorSide, 1.0f);
  241.     // The floor appearance. Basically a texture repeated many times
  242.     Appearance floorAppearance = new Appearance();
  243.     // The floor needs that perspective correction is enabled
  244.     PolygonMode floorPolygonMode = new PolygonMode();
  245.     floorPolygonMode.setPerspectiveCorrectionEnable(true);
  246.     floorAppearance.setPolygonMode(floorPolygonMode);
  247.     // load the texture
  248.     Texture2D floorTexture = null;
  249.     Image floorTextureImage = Maze3DMIDlet.makeImage("/floor.png");
  250.     if (floorTextureImage != null)
  251.     {
  252.       floorTexture = new Texture2D(
  253.           new Image2D(Image2D.RGB, floorTextureImage));
  254.       // the texture is repeated many times
  255.       floorTexture.setWrapping(Texture2D.WRAP_REPEAT,
  256.                                Texture2D.WRAP_REPEAT);
  257.       floorTexture.setBlending(Texture2D.FUNC_REPLACE);
  258.       floorTexture.setFiltering(Texture2D.FILTER_LINEAR,
  259.                                 Texture2D.FILTER_NEAREST);
  260.       floorAppearance.setTexture(0, floorTexture);
  261.     }
  262.     Plane floor = new Plane(floorTransform, 10);
  263.     // build the mesh
  264.     Mesh floorMesh = floor.createMesh();
  265.     floorMesh.setAppearance(0, floorAppearance);
  266.     // the floor is not pickable
  267.     floorMesh.setPickingEnable(false);
  268.     // add to the world
  269.     world.addChild(floorMesh);
  270.   }
  271.   // create the player location square
  272.   private void createLocationSquare()
  273.   {
  274.     // initial location of the square
  275.     // we are only interested in rotating and scaling it
  276.     Transform locationSquareTransform = new Transform();
  277.     locationSquareTransform.postRotate(90.0f, -1.0f, 0.0f, 0.0f);
  278.     locationSquareTransform.postScale(8, 8, 1.0f);
  279.     // the location apparance is a simple texture decal
  280.     Appearance locationSquareAppearance = new Appearance();
  281.     Texture2D locationSquareTexture = null;
  282.     Image locationSquareImage = Maze3DMIDlet.makeImage("/location.png");
  283.     if (locationSquareImage != null)
  284.     {
  285.       locationSquareTexture = new Texture2D(
  286.           new Image2D(Image2D.RGB, locationSquareImage));
  287.       // the texture is not repeated
  288.       locationSquareTexture.setWrapping(Texture2D.WRAP_CLAMP,
  289.                                         Texture2D.WRAP_CLAMP);
  290.       locationSquareTexture.setBlending(Texture2D.FUNC_REPLACE);
  291.       locationSquareTexture.setFiltering(Texture2D.FILTER_NEAREST,
  292.                                          Texture2D.FILTER_NEAREST);
  293.       locationSquareAppearance.setTexture(0, locationSquareTexture);
  294.     }
  295.     Plane locationPlane = new Plane(locationSquareTransform, 1);
  296.     locationSquare = locationPlane.createMesh();
  297.     locationSquare.setAppearance(0, locationSquareAppearance);
  298.     locationSquare.setRenderingEnable(false);
  299.     locationSquare.setPickingEnable(false);
  300.     world.addChild(locationSquare);
  301.   }
  302.   private void setUpMaze()
  303.   {
  304.     // the walls need perspection correction enabled
  305.     PolygonMode wallPolygonMode = new PolygonMode();
  306.     wallPolygonMode.setPerspectiveCorrectionEnable(true);
  307.     // build the wall semi transparent appearance
  308.     wallClearAppearance.setPolygonMode(wallPolygonMode);
  309.     // this is to make a wall semi transparent
  310.     CompositingMode wallClearCompositeMode = new CompositingMode();
  311.     wallClearCompositeMode.setBlending(CompositingMode.ALPHA_ADD);
  312.     wallClearAppearance.setCompositingMode(wallClearCompositeMode);
  313.     // build the normal wall appearance
  314.     wallAppearance.setPolygonMode(wallPolygonMode);
  315.     // load the wall texture
  316.     Image wallTextureImage = Maze3DMIDlet.makeImage("/wall.png");
  317.     if (wallTextureImage != null)
  318.     {
  319.       Texture2D wallTexture = null;
  320.       wallTexture = new Texture2D(
  321.           new Image2D(Image2D.RGB, wallTextureImage));
  322.       // the texture is repeated
  323.       wallTexture.setWrapping(Texture2D.WRAP_REPEAT,
  324.                               Texture2D.WRAP_CLAMP);
  325.       wallTexture.setBlending(Texture2D.FUNC_REPLACE);
  326.       wallTexture.setFiltering(Texture2D.FILTER_LINEAR,
  327.                                Texture2D.FILTER_NEAREST);
  328.       // set the texture
  329.       wallAppearance.setTexture(0, wallTexture);
  330.       wallClearAppearance.setTexture(0, wallTexture);
  331.     }
  332.     // The maze create the planes and they are added to the world
  333.     Enumeration wallsEnum = maze.createPlanes();
  334.     while (wallsEnum.hasMoreElements())
  335.     {
  336.       Mesh wallMesh = ((Plane) wallsEnum.nextElement()).createMesh();
  337.       wallMesh.setAppearance(0, wallAppearance);
  338.       world.addChild(wallMesh);
  339.     }
  340.   }
  341.   // decides whether it is allowed to move to a given place
  342.   // otherwise it is possible to cross walls and go outside the maze
  343.   private boolean canMove(float stepx, float stepz, boolean forward)
  344.   {
  345.     float x = locationx + stepx;
  346.     float z = locationz + stepz;
  347.     // First check if inside the maze area
  348.     float mazeSize = MAZE_SIDE_LENGTH / 2 - 2;
  349.     if (x <= -mazeSize
  350.         || z <= -mazeSize
  351.         || x > mazeSize
  352.         || z > mazeSize)
  353.     {
  354.       return false;
  355.     }
  356.     // collision with walls is easily implemented using picking
  357.     // if there is something in front of the camera and the distance is too
  358.     // small, the movement is not allowed
  359.     RayIntersection ri = new RayIntersection();
  360.     // inverse the point of view to detect if it is moving
  361.     // bacwards
  362.     if (!forward)
  363.     {
  364.       camera.postRotate(180, 0f, 1f, 0f);
  365.     }
  366.     if (world.pick(-1, 0.5f, 0.5f, camera, ri))
  367.     {
  368.       Node selected = ri.getIntersected();
  369.       if (selected instanceof Mesh)
  370.       {
  371.         // multiply per 9 since that's the size of a step
  372.         float distance = ri.getDistance() * 9;
  373.         if (distance <= 0.1)
  374.         {
  375.           return false;
  376.         }
  377.       }
  378.     }
  379.     if (!forward)
  380.     {
  381.       // revert to the original view
  382.       camera.postRotate(-180, 0f, 1f, 0f);
  383.     }
  384.     return true;
  385.   }
  386.   // the method checks whenever the keys are pressed
  387.   private void checkKeys()
  388.   {
  389.     int keyState = getKeyStates();
  390.     boolean moved = false;
  391.     // if fire is detected the mesh in front of you is made semi transparent
  392.     if ((keyState & FIRE_PRESSED) != 0)
  393.     {
  394.       RayIntersection ri = new RayIntersection();
  395.       if (world.pick(-1, 0.5f, 0.5f, camera, ri))
  396.       {
  397.         // if there is something to pick
  398.         Node selected = ri.getIntersected();
  399.         if (selected instanceof Mesh)
  400.         {
  401.           // make the wall semi transparent
  402.           transparentMesh = ((Mesh) selected);
  403.           transparentMesh.setAppearance(0, wallClearAppearance);
  404.           transparentMesh.setAlphaFactor(0.8f);
  405.         }
  406.       }
  407.     }
  408.     // here we assume only one button is pressed otherwise the "second"
  409.     // press is ignored
  410.     if ((keyState & LEFT_PRESSED) != 0)
  411.     {
  412.       // move to the left
  413.       angley += ANGLE_STEP;
  414.       stepx = STEP * (float) Math.sin(Math.toRadians(angley));
  415.       stepz = STEP * (float) Math.cos(Math.toRadians(angley));
  416.       // shift the background a bit to the right
  417.       background.setCrop(background.getCropX() + 3,
  418.                          0,
  419.                          getWidth(),
  420.                          getHeight());
  421.       moved = true;
  422.     }
  423.     else if ((keyState & RIGHT_PRESSED) != 0)
  424.     {
  425.       // move to the right
  426.       angley -= ANGLE_STEP;
  427.       stepx = STEP * (float) Math.sin(Math.toRadians(angley));
  428.       stepz = STEP * (float) Math.cos(Math.toRadians(angley));
  429.       // shift the background a bit to the left
  430.       background.setCrop(background.getCropX() - 3,
  431.                          0,
  432.                          getWidth(),
  433.                          getHeight());
  434.       moved = true;
  435.     }
  436.     else if ((keyState & UP_PRESSED) != 0)
  437.     {
  438.       // move forward, but first check if it is allowed
  439.       if (canMove(-stepx, -stepz, true))
  440.       {
  441.         locationx -= stepx;
  442.         locationz -= stepz;
  443.       }
  444.       moved = true;
  445.     }
  446.     else if ((keyState & DOWN_PRESSED) != 0)
  447.     {
  448.       // move backwards, but first check if it is allowed
  449.       if (canMove(stepx, stepz, false))
  450.       {
  451.         locationx += stepx;
  452.         locationz += stepz;
  453.       }
  454.       moved = true;
  455.     }
  456.     // if moved we make the last transparent wall normal
  457.     // and reset the view
  458.     if (moved)
  459.     {
  460.       if (!finished && maze.isAtTheEnd(locationx, locationz, 4f))
  461.       {
  462.         finished = true;
  463.       }
  464.       // if there was a mesh semi transparent
  465.       // return to normal
  466.       if (transparentMesh != null)
  467.       {
  468.         transparentMesh.setAppearance(0, wallAppearance);
  469.         transparentMesh.setAlphaFactor(1f);
  470.         transparentMesh = null;
  471.       }
  472.       // if we move we reset the top view state
  473.       if (topView)
  474.       {
  475.         switchView();
  476.       }
  477.       setupCamera();
  478.     }
  479.   }
  480.   // key pressed used to show the main menu
  481.   protected void keyPressed(int key)
  482.   {
  483.     if (key < 0)
  484.     {
  485.       midlet.showMenu();
  486.     }
  487.   }
  488.   // sets the camera location
  489.   private void setupCamera()
  490.   {
  491.     camera.setOrientation(angley, 0f, 1f, 0f);
  492.     camera.setTranslation(locationx, 10f, locationz);
  493.   }
  494.   // draw 2d information on top of the screen
  495.   private void draw2D(Graphics g)
  496.   {
  497.     // text color
  498.     g.setColor(45, 235, 45);
  499.     StringBuffer status = new StringBuffer(finished ? "Done" : "");
  500.     if (finished)
  501.     {
  502.       duration = System.currentTimeMillis() - gameStart;
  503.       status.append(" Time:").append(duration / 1000);
  504.     }
  505.     else
  506.     {
  507.       status.append(" Time:");
  508.       status.append((System.currentTimeMillis() - gameStart) / 1000);
  509.     }
  510.     // draw time on the top right
  511.     g.drawString(status.toString(),
  512.                  getWidth(),
  513.                  0,
  514.                  Graphics.TOP | Graphics.RIGHT);
  515.     // draw FPS on the bottom left
  516.     g.drawString("FPS: " + fps,
  517.                  0,
  518.                  getHeight(),
  519.                  Graphics.BOTTOM | Graphics.LEFT);
  520.   }
  521.   // paint the scene
  522.   private void draw3D(Graphics g)
  523.   {
  524.     boolean bound = false;
  525.     try
  526.     {
  527.       // binds the target
  528.       g3d.bindTarget(g);
  529.       bound = true;
  530.       // advance the animation
  531.       world.animate((int)(System.currentTimeMillis() - gameStart));
  532.       // do the rendering
  533.       g3d.render(world);
  534.     }
  535.     finally
  536.     {
  537.       // release the target
  538.       if (bound)
  539.       {
  540.         g3d.releaseTarget();
  541.       }
  542.     }
  543.   }
  544.   public void run()
  545.   {
  546.     Graphics g = getGraphics();
  547.     while (playing)
  548.     {
  549.       try
  550.       {
  551.         long startTime = System.currentTimeMillis();
  552.         if (isShown())
  553.         {
  554.           // update the world if the player has moved
  555.           checkKeys();
  556.           long drawingTime = System.currentTimeMillis();
  557.           // draw the 3d part
  558.           draw3D(g);
  559.           drawingTime = System.currentTimeMillis() - drawingTime;
  560.           // calculate fps
  561.           fps = 1000 / drawingTime;
  562.           // draw the 2d part
  563.           draw2D(g);
  564.           // flush to the screen
  565.           flushGraphics();
  566.         }
  567.         // wait for a little and give the other threads
  568.         // the chance to run
  569.         long timeTaken = System.currentTimeMillis() - startTime;
  570.         if (timeTaken < MILLIS_PER_TICK)
  571.         {
  572.           synchronized (this)
  573.           {
  574.             wait(MILLIS_PER_TICK - timeTaken);
  575.           }
  576.         }
  577.         else
  578.         {
  579.           Thread.currentThread().yield();
  580.         }
  581.       }
  582.       catch (Exception e)
  583.       {
  584.         // ignore. Not much to but we want to keep the loop running
  585.       }
  586.     }
  587.   }
  588. }