Visual Content in J2ME
An Introduction |
Prof. David Bernstein |
Computer Science Department |
bernstdh@jmu.edu |
VisualComponent
Class:
Vector
instead of ArrayList
GameCanvas
instead of
JComponent
Visualization
Class:
Vector
instead of ArrayList
Enumeration
instead of Iterator
VisualizationView
Class:
int
instead of Color
Enumeration
instead of Iterator
Graphics
object
The AbstractTransformableContent
class can no longer
be used because:
package visual.statik.described; import java.util.*; import javax.microedition.lcdui.*; import gui.*; /** * A Canvas that renders a Rectangle with randomly * generated attributes each time it's paint method is called * * Changes for J2ME Version: * Graphics instead of Graphics2D * drawRect() instead of draw() * * @author Prof. David Bernstein, James Madison University * @version 1.0 (J2ME) */ public class RandomRectangleCanvas extends VisualComponent { private Random generator; /** * Default Constructor */ public RandomRectangleCanvas() { super(); generator = new Random(); } /** * Render this Canvas * * @param g The rendering engine to use */ public void paint(Graphics g) { int height, maxHeight, maxWidth, width, x, y; maxHeight = g.getClipHeight(); maxWidth = g.getClipWidth(); x = generator.nextInt(maxWidth - 1); y = generator.nextInt(maxHeight - 1); width = generator.nextInt(maxWidth - x - 1); height = generator.nextInt(maxHeight - y - 1); // Note: There is no Color class or Shape interface // Clear the screen g.setColor(0x00FFFFFF); g.fillRect(0, 0, maxWidth, maxHeight); // Draw the rectangle g.setColor(0x00FF00FF); g.drawRect(x, y, width, height); } }
The TransformableContent
interface can no longer
include setComposite()
or
setBufferedImageOp()
The factories have to change:
package visual.statik.sampled; import java.io.*; import javax.microedition.lcdui.*; /** * A utility class for constructing/creating BufferedImage objects * * Changes in J2ME Version: * * Completely re-written * * @author Prof. David Bernstein, James Madison University * @version 1.0 (J2ME) */ public class ImageFactory { /** * Default Constructor */ public ImageFactory() { super(); } /** * Create an Image from a file * containing an Image * * @param name The name of the file * @return The Image or null if an Exception was thrown */ public Image createBufferedImage(String name) { Image result; result = null; try { InputStream is = ImageFactory.class.getResourceAsStream("/"+name); if(null != is) result = Image.createImage(is); } catch (IOException ioe) { } return result; } }
package visual.statik.sampled; import java.io.*; import javax.microedition.lcdui.*; /** * A utility class for constructing/creating * visual.statik.sampled.Content objects * * Changes in J2ME Version: * * Completely re-written * * @author Prof. David Bernstein, James Madison University * @version 1.0 (J2ME) */ public class ContentFactory { private ImageFactory imageFactory; /** * Default Constructor */ public ContentFactory() { super(); imageFactory = new ImageFactory(); } /** * Read a row-oriented array of Content objects from a file * * * * @param file The name of the file (without a path) * @param n The number of images * @param channels IGNORED! * @return The images or null if an Exception was thrown */ public Content[] createContents(String file, int n, int channels) { Image composite, subimage; Content[] result; int height, width; result = null; composite = imageFactory.createBufferedImage(file); if (composite != null) { height = composite.getHeight(); width = composite.getWidth() / n; result = new Content[n]; for (int j=0; j<n; j++) { subimage = Image.createImage(composite, j*width, 0, width, height, 0); result[j] = new Content(subimage, 0, 0); } } return result; } /** * Read a table of Content objects from a file * * * * @param file The name of the file (without a path) * @param rows The number of rows * @param columns The number of columns * @param channels IGNORED! * @return The images or null if an Exception was thrown */ public Content[][] createContents(String file, int rows, int columns, int channels) { Image composite, subimage; Content[][] result; int height, width; result = null; composite = imageFactory.createBufferedImage(file); if (composite != null) { height = composite.getHeight() / rows; width = composite.getWidth() / columns; result = new Content[rows][columns]; for (int i=0; i<rows; i++) { for (int j=0; j<columns; j++) { subimage = Image.createImage(composite, j*width, i*height, width, height, 0); result[i][j] = new Content(subimage, 0, 0); } } } return result; } /** * Create a Content from a file * * * * @param file The name of the file (without a path) * @return The image or null if an Exception was thrown */ public Content createContent(String file) { Image content; Content result; result = null; content = imageFactory.createBufferedImage(file); if (content != null) result = new Content(content, 0, 0); return result; } }
package visual.statik.sampled; import javax.microedition.lcdui.*; import gui.*; /** * A concrete extension of a Canvas that illustrates * the rendering of sampled static visual content * * Changes in J2ME Version: * * Packed int instead of Color * * @author Prof. David Bernstein, James Madison University * @version 1.0 (J2ME) */ public class ImageCanvas extends VisualComponent { private Image image; /** * Explicit Value Constructor * * @param image The Image to render */ public ImageCanvas(Image image) { this.image = image; } /** * Render this Canvas * * @param g The rendering engine to use */ public void paint(Graphics g) { int height, x, y, width; // Note: The Canvas class is abstract so we don't // call super.paint(g) x = g.getClipX(); y = g.getClipY(); width = g.getClipWidth(); height = g.getClipHeight(); // Note: The background needs to be "cleared" g.setColor(0x00FFFFFF); g.fillRect(x, y, width, height); // Note: The registration point must be specified g.drawImage(image, 0, 0, Graphics.TOP|Graphics.LEFT); } }
Nothing (significant) has to change
Stage
Class:
Enumeration
instead of Iterator
int
instead of Color
Sprite
Class:
RuleBasedSprite
Class:
Vector
instead of ArrayList
package visual.dynamic.described; import java.util.*; import javax.microedition.lcdui.*; import geom.*; import visual.statik.TransformableContent; /** * A character that falls from the top of the screen to the bottom * (as in "The Matrix") * * Note: This is a simple rule-based Sprite. It does not use key-frames * or tweening. Hence, it has rules in handleTick(). * * Changes in the J2ME Version: * * render() was re-written * getContent() is not needed * * @author Prof. David Bernstein, James Madison University * @version 1.0 (J2ME) * */ public class FallingCharacter extends Sprite { private char character; private int green, speed; private Rectangle2D bounds; private static Font font = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_LARGE); private static Random rng = new Random(); /** * DefaultConstructor * */ public FallingCharacter() { super(); character = (char)(33 + rng.nextInt(95)); green = rng.nextInt(255); bounds = new Rectangle2D(); bounds.x = rng.nextInt(180); bounds.y = -1 * rng.nextInt(196/2); bounds.width = font.charWidth(character); bounds.height = font.getHeight(); speed = rng.nextInt(5) + 1; } /** * Handle a tick event (generated by the Stage) * * @param time The current time (in milliseconds) */ public void handleTick(int time) { bounds.y += speed; if (bounds.y > 196+30) { bounds.y = 0; bounds.x = rng.nextInt(180); speed = rng.nextInt(5) + 1; } } /** * Get the bounding rectangle for this Sprite * * @return The bounding rectangle */ public Rectangle2D getBounds2D() { return bounds; } public TransformableContent getContent() { return null; } /** * Render the Sprite in its current state * * @param g The rendering engine to use */ public void render(Graphics g) { // Setup the rendering engine g.setFont(font); g.setColor(0, green, 0); // Render the character g.drawChar(character, bounds.x, bounds.y, Graphics.TOP|Graphics.LEFT); } }
package visual.dynamic.described; import javax.microedition.lcdui.*; import event.*; import geom.*; import visual.statik.TransformableContent; /** * The Cupola in the Balloon game. * * J2ME Changes: * Changed PointerListener to KeyboardListener for ease of game play * * @author Prof. David Bernstein, James Madison University * @version 1.0 (J2ME) */ public class Cupola extends RuleBasedSprite implements KeyboardListener { private double left, top; /** * Explicit Value Constructor * * @param content The static visual content * @param stageWidth The width of the Stage * @param stageHeight The height of the Stage */ public Cupola(TransformableContent content, double stageWidth, double stageHeight) { super(content); Rectangle2D bounds; bounds = content.getBounds2D(false); top = (stageHeight - bounds.getHeight()); left = (stageWidth - bounds.getWidth())/2.0; setLocation(left, top); } /** * Handle a tick event (generated by the Stage) * * @param time The current time (which is not used) */ public void handleTick(int time) { setLocation(left, top); } /** * Handle a "key press" event * (required by KeyboardListener) * * @param key The key */ public void handleKeyPress(int key) { if (key == KeyDictionary.KEY_LEFT) left -= 5; else if (key == KeyDictionary.KEY_RIGHT) left += 5; } }
package visual.dynamic.described; import java.util.Random; import javax.microedition.lcdui.*; import geom.*; import visual.statik.TransformableContent; /** * A Balloon that drops to the ground * * @author Prof. David Bernstein, James Madison University * @version 1.0 (J2ME) */ public class Balloon extends RuleBasedSprite { private double left, speed, top; private int maxY, maxX, minY; private static Random rng = new Random(); /** * Explicit Value Constructor * * @param content The static content for the balloon * @param stageWidth The width of the Stage * @param stageHeight The height of the Stage */ public Balloon(TransformableContent content, double stageWidth, double stageHeight) { super(content); Rectangle2D bounds; bounds = content.getBounds2D(false); maxX = (int)(stageWidth - bounds.getWidth()); maxY = (int)(stageHeight); minY = (int)(-bounds.getHeight()); left = rng.nextInt(maxX); top = minY; speed = 1 + rng.nextInt(3); } /** * Handle a tick event (generated by the Stage) * * @param time The current time (which is not used) */ public void handleTick(int time) { Sprite cupola; // Check for an intersection cupola = null; if (protagonists.size() > 0) cupola = (Sprite)protagonists.elementAt(0); if ((cupola != null) && (intersects(cupola))) { speed = 0; setVisible(false); } // Update the location top += speed; if (top > maxY) { left = rng.nextInt(maxX); top = minY; speed = rng.nextInt(10); } // Set the location setLocation(left, top); } }