|
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);
}
}