import java.util.*;
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
import javax.microedition.m3g.*;
// Main class. It setups the 3D scene and does the movements
// and rendering
class MazeCanvas
extends GameCanvas
implements Runnable
{
// Height of the walls
private final static float WALL_HEIGHT = 10.f;
// overall side of the maze
private final static float MAZE_SIDE_LENGTH =200f;
// amount to advance at each step
private final static float STEP = 3.0f;
// amount to rotate each time
private final static float ANGLE_STEP = 8.0f;
// millis to wait in the main loop
private final static int MILLIS_PER_TICK = 5;
// Size of the steps to directions x and z
// stored to avoid extra trigonometric calculations
private float stepz = -STEP, stepx = 0f;
// player's location
private float locationx = 0f, locationz = 0;
// player's rotation
private float angley = 0f;
// flags whether we are looking the game from the top
private boolean topView = false;
// flags to keep the drawing going
private volatile boolean playing = true;
// stores the amount of frames per second being displayed
private volatile float fps = 0;
// stores how long has taken to run thorugh the maze
private volatile long gameStart = 0;
// stores how long has taken to run thorugh the maze
private volatile long duration = 0;
// indicates whether the game has finished
private boolean finished;
// Parent MIDlet
private final Maze3DMIDlet midlet;
// Graphics 3D global instance
private Graphics3D g3d;
// the world of the application
private World world;
// the world has 2 cameras
private Camera camera, topCamera;
// world's background
private Background background = null;
// simple rectangular mesh used to locate the player
private Mesh locationSquare = null;
// wall alternate appeareances
private Appearance wallClearAppearance = new Appearance();
private Appearance wallAppearance = new Appearance();
// points to the mesh last made semi transparent
private Mesh transparentMesh = null;
private Maze maze = new Maze(10, MAZE_SIDE_LENGTH, WALL_HEIGHT);
private Thread mainThread = null;
// Construct the Displayable
MazeCanvas(Maze3DMIDlet midlet)
{
super(true);
this.midlet = midlet;
}
// (Re)Starts the game
void start()
{
playing = true;
gameStart = System.currentTimeMillis() - duration;
mainThread = new Thread(this);
mainThread.start();
}
// stops/pauses the game
void stop()
{
duration = System.currentTimeMillis() - gameStart;
playing = false;
}
// switches from top view to normal view and vice versa
void switchView()
{
topView = !topView;
setView();
// informs the MIDLet since this can be switched internally
midlet.viewSwitched();
}
void setView()
{
// reset the locaiton and draw on top of the maze
locationSquare.setTranslation(locationx,
2 * WALL_HEIGHT + 3f, locationz);
// the square is rendered only in top view
locationSquare.setRenderingEnable(topView);
// the background is removed in top view
world.setBackground(topView ? null : background);
// sets the actual active camera
if (topView)
{
world.setActiveCamera(topCamera);
}
else
{
world.setActiveCamera(camera);
}
}
// Builds the world
void init()
{
setFullScreenMode(true);
duration = 0;
// get a Graphics3D instance
g3d = Graphics3D.getInstance();
// build the world and the cameras
world = new World();
camera = new Camera();
topCamera = new Camera();
world.addChild(camera);
world.addChild(topCamera);
float w = (float)getWidth();
float h = (float)getHeight();
// set the perspective
camera.setPerspective(60.f, w / h, 0.1f, 1000.f);
topCamera.setPerspective(60.f, w / h, 0.1f, 1000.f);
// the top camer has a fixed transform looking from the top
Transform topCameraTransform = new Transform();
topCameraTransform.postRotate(90, -1f, 0f, 0f);
topCameraTransform.postTranslate(0f, 0f,MAZE_SIDE_LENGTH);
//topCameraTransform.postTranslate(0f, 0f, 1.2f * MAZE_SIDE_LENGTH);
topCamera.setTransform(topCameraTransform);
// Setup the background
background = new Background();
Image backgroundImage = Maze3DMIDlet.makeImage("/background.png");
if (backgroundImage != null)
{
background.setImage(new Image2D(Image2D.RGB, backgroundImage));
background.setImageMode(Background.REPEAT, Background.REPEAT);
}
world.setBackground(background);
createFloor();
createLocationSquare();
setUpMaze();
createStartEndMarks();
setView();
// setup the initial location
locationx = maze.findStartLocationX();
locationz = maze.findStartLocationZ();
// look at the center
angley = -180f;
setupCamera();
start();
}
// creates the labeld at the start and at the end of the maze
private void createStartEndMarks()
{
// The marks' appearance
Appearance startMarkAppearance = new Appearance();
Appearance endMarkAppearance = new Appearance();
// The composite mode is ALPHA to show only the letters
// the background is hidden using the alpha layer
CompositingMode markCompositeMode = new CompositingMode();
markCompositeMode.setBlending(CompositingMode.ALPHA);
startMarkAppearance.setCompositingMode(markCompositeMode);
endMarkAppearance.setCompositingMode(markCompositeMode);
// The label's text is built using a texture
Texture2D startMarkTexture = null;
Image startMarkTextureImage =
Maze3DMIDlet.makeImage("/start_label.png");
if (startMarkTextureImage != null)
{
startMarkTexture = new Texture2D(
new Image2D(Image2D.RGBA, startMarkTextureImage));
// the texture is not repeated
startMarkTexture.setWrapping(Texture2D.WRAP_CLAMP,
Texture2D.WRAP_CLAMP);
startMarkTexture.setBlending(Texture2D.FUNC_REPLACE);
startMarkTexture.setFiltering(Texture2D.FILTER_NEAREST,
Texture2D.FILTER_NEAREST);
startMarkAppearance.setTexture(0, startMarkTexture);
}
Texture2D endMarkTexture = null;
Image endMarkTextureImage =
Maze3DMIDlet.makeImage("/end_label.png");
if (endMarkTextureImage != null)
{
endMarkTexture = new Texture2D(
new Image2D(Image2D.RGBA, endMarkTextureImage));
// the texture is not repeated
endMarkTexture.setWrapping(Texture2D.WRAP_CLAMP,
Texture2D.WRAP_CLAMP);
endMarkTexture.setBlending(Texture2D.FUNC_REPLACE);
endMarkTexture.setFiltering(Texture2D.FILTER_NEAREST,
Texture2D.FILTER_NEAREST);
endMarkAppearance.setTexture(0, endMarkTexture);
}
// create the start mesh
Plane startMarkPlane = maze.createStartMark();
Mesh startMarkMesh = startMarkPlane.createMesh();
startMarkMesh.setAppearance(0, startMarkAppearance);
// these are not pickable
startMarkMesh.setPickingEnable(false);
world.addChild(startMarkMesh);
// creates the end mesh
Plane endMarkPlane = maze.createEndMark();
Mesh endMarkMesh = endMarkPlane.createMesh();
endMarkMesh.setAppearance(0, endMarkAppearance);
// is not pickable either
endMarkMesh.setPickingEnable(false);
// Create a sequence of 4 keyframes
KeyframeSequence keyframes =
new KeyframeSequence(4,3, KeyframeSequence.LINEAR);
keyframes.setDuration(12000);
keyframes.setRepeatMode(KeyframeSequence.LOOP);
float[] trans = new float[4];
endMarkM