作者:mydeman 文章来源:J2ME开发网
TutorialMidlet
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
public class TutorialMidlet extends MIDlet implements CommandListener
{
// A variable that holds the unique display
private Display display = null;
// The canvas
private M3GCanvas canvas = null;
// The MIDlet itself
private static MIDlet self = null;
/** Called when the application starts, and when it is resumed.
* We ignore the resume here and allocate data for our canvas
* in the startApp method. This is generally very bad practice.
*/
protected void startApp() throws MIDletStateChangeException
{
// Allocate
display = Display.getDisplay(this);
canvas = new M3GCanvas(30);
// Add a quit command to the canvas
// This command won't be seen, as we
// are running in fullScreen mode
// but it's always nice to have a quit command
canvas.addCommand(new Command("Quit", Command.EXIT, 1));
// Set the listener to be the MIDlet
canvas.setCommandListener(this);
// Start canvas
canvas.start();
display.setCurrent(canvas);
// Set the self
self = this;
}
/** Called when the game should pause, such as during a call */
protected void pauseApp()
{
}
/** Called when the application should shut down */
protected void destroyApp(boolean unconditional) throws MIDletStateChangeException
{
// Method that shuts down the entire MIDlet
notifyDestroyed();
}
/** Listens to commands and processes */
public void commandAction(Command c, Displayable d) {
// If we get an EXIT command we destroy the application
if(c.getCommandType() == Command.EXIT)
notifyDestroyed();
}
/** Static method that quits our application
* by using the static field 'self' */
public static void die()
{
self.notifyDestroyed();
}
}
M3GCanvas
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.m3g.Background;
import javax.microedition.m3g.Camera;
import javax.microedition.m3g.Graphics3D;
import javax.microedition.m3g.Light;
import javax.microedition.m3g.Transform;
public class M3GCanvas
extends GameCanvas
implements Runnable {
// Thread-control
boolean running = false;
boolean done = true;
// If the game should end
public static boolean gameOver = false;
// Rendering hints
public static final int STRONG_RENDERING_HINTS = Graphics3D.ANTIALIAS | Graphics3D.TRUE_COLOR | Graphics3D.DITHER;
public static final int WEAK_RENDERING_HINTS = 0;
public static int RENDERING_HINTS = STRONG_RENDERING_HINTS;
// Key array
boolean[] key = new boolean[5];
// Key constants
public static final int FIRE = 0;
public static final int UP = FIRE + 1;
public static final int DOWN = UP + 1;
public static final int LEFT = DOWN + 1;
public static final int RIGHT = LEFT + 1;
// Global identity matrix
Transform identity = new Transform();
// Global Graphics3D object
Graphics3D g3d = null;
// The background
Background back = null;
// The global camera object
Camera cam = null;
// The particle system
ParticleSystem ps = null;
FountainEffect fx = null;
/** Constructs the canvas
*/
public M3GCanvas(int fps)
{
// We don't want to capture keys normally
super(true);
// We want a fullscreen canvas
setFullScreenMode(true);
// Load our camera
loadCamera();
// Load our background
loadBackground();
// Set up graphics 3d
setUp();
}
/** Prepares the Graphics3D engine for immediate mode rendering by adding a light */
private void setUp()
{
// Get the instance
g3d = Graphics3D.getInstance();
// Add a light to our scene, so we can see something
g3d.addLight(createAmbientLight(), identity);
}
/** Creates a simple ambient light */
private Light createAmbientLight()
{
Light l = new Light();
l.setMode(Light.AMBIENT);
l.setIntensity(1.0f);
return l;
}
/** When fullscreen mode is set, some devices will call
* this method to notify us of the new width/height.
* However, we don't really care about the width/height
* in this tutorial so we just let it be
*/
public void sizeChanged(int newWidth, int newHeight)
{
}
/** Loads our camera */
private void loadCamera()
{
// Create a new camera
cam = new Camera();
}
/** Loads the background */
private void loadBackground()
{
// Create a new background, set bg color to black
back = new Background();
back.setColor(0);
}
/** Draws to screen
*/
private void draw(Graphics g)
{
// Envelop all in a try/catch block just in case
try
{
// Get the Graphics3D context
g3d = Graphics3D.getInstance();
// First bind the graphics object. We use our pre-defined rendering hints.
g3d.bindTarget(g, true, RENDERING_HINTS);
// Clear background
g3d.clear(back);
// Bind camera at fixed position in origo
g3d.setCamera(cam, identity);
// Init particles
if(ps == null)
{
fx = new FountainEffect(90);
ps = new ParticleSystem(fx, 20);
}
// Emit the particles
ps.emit(g3d);
// Check controls for fountain rotation
if(key[LEFT])
fx.setAngle(fx.getAngle() + 5);
if(key[RIGHT])
fx.setAngle(fx.getAngle() - 5);
// Quit if user presses fire
if(key[FIRE])
TutorialMidlet.die();
}
catch(Exception e)
{
reportException(e);
}
finally
{
// Always remember to release!
g3d.releaseTarget();
}
}
/** Starts the canvas by firing up a thread
*/
public void start() {
Thread myThread = new Thread(this);
// Make sure we know we are running
running = true;
done = false;
// Start
myThread.start();
}
/** Run, runs the whole thread. Also keeps track of FPS
*/
public void run() {
while(running) {
try {
// Call the process method (computes keys)
process();
// Draw everything
draw(getGraphics());
flushGraphics();
// Sleep to prevent starvation
try{ Thread.sleep(30); } catch(Exception e) {}
}
catch(Exception e) {
reportException(e);
}
}
// Notify completion
done = true;
}
/**
* @param e
*/
private void reportException(Exception e) {
System.out.println(e.getMessage());
System.out.println(e);
e.printStackTrace();
}
/** Pauses the game
*/
public void pause() {}
/** Stops the game
*/
public void stop() { running = false; }
/** Processes keys
*/
protected void process()
{
int keys = getKeyStates();
if((keys & GameCanvas.FIRE_PRESSED) != 0)
key[FIRE] = true;
else
key[FIRE] = false;
if((keys & GameCanvas.UP_PRESSED) != 0)
key[UP] = true;
else
key[UP] = false;
if((keys & GameCanvas.DOWN_PRESSED) != 0)
key[DOWN] = true;
else
key[DOWN] = false;
if((keys & GameCanvas.LEFT_PRESSED) != 0)
key[LEFT] = true;
else
key[LEFT] = false;
if((keys & GameCanvas.RIGHT_PRESSED) != 0)
key[RIGHT] = true;
else
key[RIGHT] = false;
}
/** Checks if thread is running
*/
public boolean isRunning() { return running; }
/** checks if thread has finished its execution completely
*/
public boolean isDone() { return done; }
}
MeshFactory
import javax.microedition.lcdui.Image;
import javax.microedition.m3g.Appearance;
import javax.microedition.m3g.Image2D;
import javax.microedition.m3g.IndexBuffer;
import javax.microedition.m3g.Mesh;
import javax.microedition.m3g.PolygonMode;
import javax.microedition.m3g.Texture2D;
import javax.microedition.m3g.TriangleStripArray;
import javax.microedition.m3g.VertexArray;
import javax.microedition.m3g.VertexBuffer;
/**
* Static class that handles creation of code-generated Meshes
*/
public class MeshFactory
{
/** Creates a texture plane that is alpha-blended
*
* @param texFilename The name of the texture image file
* @param cullFlags The flags for culling. See PolygonMode.
* @param alpha The alpha value of blending. Is a full color in 0xAARRGGBB format
* @return The finished textured mesh
*/
public static Mesh createAlphaPlane(String texFilename, int cullFlags, int alpha)
{
// Create a normal mesh
Mesh mesh = createPlane(texFilename, cullFlags);
// Make it blended
MeshOperator.convertToBlended(mesh, alpha, Texture2D.FUNC_BLEND);
return mesh;
}
/**
* Creates a textured plane.
* @param texFilename The name of the texture image file
* @param cullFlags The flags for culling. See PolygonMode.
* @return The finished textured mesh
*/
public static Mesh createPlane(String texFilename, int cullFlags)
{
// The vertrices of the plane
short vertrices[] = new short[] {-1, -1, 0,
1, -1, 0,
1, 1, 0,
-1, 1, 0};
// Texture coords of the plane
short texCoords[] = new short[] {0, 255,
255, 255,
255, 0,
0, 0};
// The classes
VertexArray vertexArray, texArray;
IndexBuffer triangles;
// Create the model's vertrices
vertexArray = new VertexArray(vertrices.length/3, 3, 2);
vertexArray.set(0, vertrices.length/3, vertrices);
// Create the model's texture coords
texArray = new VertexArray(texCoords.length / 2, 2, 2);
texArray.set(0, texCoords.length / 2, texCoords);
// Compose a VertexBuffer out of the previous vertrices and texture coordinates
VertexBuffer vertexBuffer = new VertexBuffer();
vertexBuffer.setPositions(vertexArray, 1.0f, null);
vertexBuffer.setTexCoords(0, texArray, 1.0f/255.0f, null);
// Create indices and face lengths
int indices[] = new int[] {0, 1, 3, 2};
int[] stripLengths = new int[] {4};
// Create the model's triangles
triangles = new TriangleStripArray(indices, stripLengths);
// Create the appearance
Appearance appearance = new Appearance();
PolygonMode pm = new PolygonMode();
pm.setCulling(cullFlags);
appearance.setPolygonMode(pm);
// Create and set the texture
try
{
// Open image
Image texImage = Image.createImage(texFilename);
Texture2D theTexture = new Texture2D(new Image2D(Image2D.RGBA, texImage));
// Replace the mesh's original colors (no blending)
theTexture.setBlending(Texture2D.FUNC_REPLACE);
// Set wrapping and filtering
theTexture.setWrapping(Texture2D.WRAP_CLAMP, Texture2D.WRAP_CLAMP);
theTexture.setFiltering(Texture2D.FILTER_BASE_LEVEL, Texture2D.FILTER_NEAREST);
// Add texture to the appearance
appearance.setTexture(0, theTexture);
}
catch(Exception e)
{
// Something went wrong
System.out.println("Failed to create texture");
System.out.println(e);
}
// Finally create the Mesh
Mesh mesh = new Mesh(vertexBuffer, triangles, appearance);
// All done
return mesh;
}
}
MeshOperator
import javax.microedition.m3g.CompositingMode;
import javax.microedition.m3g.Mesh;
/**
* Performs some basic operations on Mesh objects
*/
public class MeshOperator
{
/** Sets the alpha blending of a mesh. Only meaningful if the mesh already is alpha blended */
public static void setMeshAlpha(Mesh m, int alpha)
{
m.getVertexBuffer().setDefaultColor(alpha);
}
/**
*
* @param m The mesh to convert to a blended one
* @param alpha The alpha color to blend with
* @param textureBlending The texture blending parameter.
*/
public static void convertToBlended(Mesh m, int alpha, int textureBlending)
{
// Set the alpha
setMeshAlpha(m, alpha);
// Fix the compositing mode
CompositingMode cm = new CompositingMode();
cm.setBlending(CompositingMode.ALPHA);
m.getAppearance(0).setCompositingMode(cm);
m.getAppearance(0).getTexture(0).setBlending(textureBlending);
}
}
Particle
/**
* Holds all the information of a particle.
* A particle's alpha is controlled directly by its life. Its alpha is always
* life * 255.
*/
public class Particle
{
// The life of the particle. Goes from 1.0f to 0.0f
private float life = 1.0f;
// The degradation of the particle
private float degradation = 0.1f;
// The velocities of the particle
private float[] vel = {0.0f, 0.0f, 0.0f};
// The position of the particle
private float[] pos = {0.0f, 0.0f, 0.0f};
// The color of the particle (RGB format 0xRRGGBB)
private int color = 0xffffff;
/** Empty initialization */
public Particle()
{
}
/**
* Initializes the particle
* @param velocity Sets the velocity
* @param position Sets the position
* @param color Sets the color (no alpha)
*/
public Particle(float[] velocity, float[] position, int color)
{
setVel(velocity);
setPos(position);
this.setColor(color);
}
/**
* @param life The life to set.
*/
void setLife(float life) {
this.life = life;
}
/**
* @return Returns the life.
*/
float getLife() {
return life;
}
/**
* @param vel The vel to set.
*/
void setVel(float[] tvel) {
System.arraycopy(tvel, 0, vel, 0, vel.length);
}
/**
* @return Returns the vel.
*/
float[] getVel() {
return vel;
}
/**
* @param pos The pos to set.
*/
void setPos(float[] tpos) {
System.arraycopy(tpos, 0, pos, 0, pos.length);
}
/**
* @return Returns the pos.
*/
float[] getPos() {
return pos;
}
/**
* @param color The color to set.
*/
void setColor(int color) {
this.color = color;
}
/**
* @return Returns the color.
*/
int getColor() {
return color;
}
/**
* @param degradation The degradation to set.
*/
public void setDegradation(float degradation) {
this.degradation = degradation;
}
/**
* @return Returns the degradation.
*/
public float getDegradation() {
return degradation;
}
}
ParticleEffect
import javax.microedition.m3g.Graphics3D;
/**
* The interface that determines which effect the particle engine will display.
* The ParticleEffect class also holds information about the bitmap used
* for displaying particles (if any)
*/
public interface ParticleEffect
{
// Initializes a particle
public void init(Particle p);
// Updates a particle
public void update(Particle p);
// Renders a particle
public void render(Particle p, Graphics3D g3d);
}
BitmapParticleEffect
import javax.microedition.m3g.Graphics3D;
import javax.microedition.m3g.Mesh;
import javax.microedition.m3g.PolygonMode;
import javax.microedition.m3g.Transform;
/**
* Represents a particle effect that uses a bitmap.
*/
public abstract class BitmapParticleEffect implements ParticleEffect
{
// The mesh
Mesh mesh = null;
// The transformation matrix
Transform trans = new Transform();
// The scale
float scale = 1.0f;
/** Initializes the bitmap used to render particles */
public BitmapParticleEffect(String filename, float scale)
{
// Load the plane with the wanted texture
mesh = MeshFactory.createAlphaPlane(filename, PolygonMode.CULL_BACK, 0xffffffff);
// Make sure we set the scale
this.scale = scale;
}
/**
* @see ParticleEffect#render(Particle, Graphics3D)
*/
public void render(Particle p, Graphics3D g3d)
{
// Calculate the alpha
int alpha = (int)(255 * p.getLife());
// Create the color
int color = p.getColor() | (alpha << 24);
// Set alpha
MeshOperator.setMeshAlpha(mesh, color);
// Transform
trans.setIdentity();
trans.postScale(scale, scale, scale);
float[] pos = p.getPos();
trans.postTranslate(pos[0], pos[1], pos[2]);
// Render
g3d.render(mesh, trans);
}
}
FountainEffect
import java.util.Random;
/*
* Created on 2005-aug-31
*/
/**
* Creates a nice fountain effect for the particles, that shoots particles
* in a certain direction, determined by its angle. The angle can be changed in real-time.
*/
public class FountainEffect extends BitmapParticleEffect
{
// The angle of particle emission
private int angle = 90;
// The sine and cosine of the current angle
private float[] trig = {1.0f, 0.0f};
// The emitting origin
private float[] pos = {0.0f, 0.0f, 0.0f};
// The randomizer
Random rand = null;
/**
* @param angle The angle of particle emission
*/
public FountainEffect(int angle)
{
// Init the bitmap
super("/res/particle.png", 0.05f);
// Set the angle
setAngle(angle);
// Get randomizer
rand = new Random();
}
/**
* @see ParticleEffect#init(Particle)
*/
public void init(Particle p)
{
// Set the particle's life
p.setLife(1.0f);
// Set the particle's position
p.setPos(pos);
// Create the particle's velocties
float[] vel = new float[3];
// We want velocities from 0.2f to 1.0f
float xyvel = rand.nextFloat() * 0.8f + 0.2f;
// We want the particle to die slowly
p.setDegradation(xyvel / 18);
// Set velocities according to trigonometry with a small deviation
vel[0] = xyvel * trig[1] + rand.nextFloat() * 0.125f - 0.0625f;
vel[1] = xyvel * trig[0] + rand.nextFloat() * 0.125f - 0.0625f;
// No movement in depth
vel[2] = 0.0f;
// Set the velocity
p.setVel(vel);
// Set the random color
int r = (int)(120 * rand.nextFloat()) + 135;
int g = (int)(120 * rand.nextFloat()) + 135;
int b = (int)(120 * rand.nextFloat()) + 135;
int col = (r << 16) | (g << 8) | b;
p.setColor(col);
}
/**
* @see ParticleEffect#update(Particle)
*/
public void update(Particle p)
{
// Simply update position
float[] ppos = p.getPos();
float[] vel = p.getVel();
ppos[0] += vel[0];
ppos[1] += vel[1];
ppos[2] += vel[2];
// Update life
p.setLife(p.getLife() - p.getDegradation());
// Check life. If it is dead, we just reinit it
if(p.getLife() < -0.001f)
{
init(p);
}
}
/**
* @param angle The angle to set.
*/
public void setAngle(int angle) {
this.angle = angle;
trig[0] = (float)Math.sin(Math.toRadians(angle));
trig[1] = (float)Math.cos(Math.toRadians(angle));
}
/**
* @return Returns the angle.
*/
public int getAngle() {
return angle;
}
/**
* @param pos The pos to set.
*/
void setEmittingOrigin(float[] pos) {
this.pos = pos;
}
/**
* @return Returns the pos.
*/
float[] getEmittingOrigin() {
return pos;
}
}
ParticleSystem
import javax.microedition.m3g.Graphics3D;
/**
* Manages emission of particles in our 3D world
*/
public class ParticleSystem
{
// The effect
private ParticleEffect effect = null;
// The particles
Particle[] parts = null;
/**
* Creates a particle system that emits particles according to a defined effect.
* @param effect The effect that controls the behaviour of the particles
* @param numParticles The number of particles to emit
*/
public ParticleSystem(ParticleEffect effect, int numParticles)
{
// Copy the effect
setEffect(effect);
// Init the particles
parts = new Particle[numParticles];
for(int i = 0; i < numParticles; i++)
{
parts[i] = new Particle();
effect.init(parts[i]);
}
}
/** The method that does it all. Needs to be called every tick of a game loop */
public void emit(Graphics3D g3d)
{
for(int i = 0; i < parts.length; i++)
{
getEffect().update(parts[i]);
getEffect().render(parts[i], g3d);
}
}
/**
* @param effect The effect to set.
*/
public void setEffect(ParticleEffect effect) {
this.effect = effect;
}
/**
* @return Returns the effect.
*/
public ParticleEffect getEffect() {
return effect;
}
}