/*
* SpriteGameLevel.java
*
* Copyright � 1998-2011 Research In Motion Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Note: For the sake of simplicity, this sample application may not leverage
* resource bundles and resource strings. However, it is STRONGLY recommended
* that application developers make use of the localization features available
* within the BlackBerry development platform to ensure a seamless application
* experience across a variety of languages and geographies. For more information
* on localizing your application, please refer to the BlackBerry Java Development
* Environment Development Guide associated with this release.
*/
package com.rim.samples.device.openglspritegamedemo;
import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;
import javax.microedition.khronos.opengles.GL11;
import net.rim.device.api.animation.Animation;
import net.rim.device.api.animation.Animator;
import net.rim.device.api.math.BoundingBox;
import net.rim.device.api.math.BoundingSphere;
import net.rim.device.api.math.Bounds;
import net.rim.device.api.math.Matrix4f;
import net.rim.device.api.math.Transform3D;
import net.rim.device.api.math.Vector3f;
import net.rim.device.api.opengles.GL20;
import net.rim.device.api.system.Application;
import net.rim.device.api.xml.parsers.ParserConfigurationException;
import net.rim.device.api.xml.parsers.SAXParser;
import net.rim.device.api.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* Represents the current level in the game. This class is responsible for
* initializing, rendering, and updating the different game objects such as the
* enemies, tiles, character, etc. There is also logic for collision detection.
*/
public class SpriteGameLevel {
/** Represents the margin of error allowed during collision detection */
private static final float EPSILON = 0.0005f;
/** Holds the LOSE state of the level */
public static final int LEVEL_LOSE = -1;
/** Holds the ACTIVE state of the level */
public static final int LEVEL_ACTIVE = 0;
/** Holds the WIN state of the level */
public static final int LEVEL_WIN = 1;
/** Holds the level's end object */
private Sprite _levelGoal = null;
/** Holds the level's blocks */
private final Vector _tiles = new Vector();
/** Holds the level's obstacles */
private final Vector _enemies = new Vector();
/** Holds the hero's translation during render */
private final Vector3f _t = new Vector3f();
/** Hold's the level's good guy character */
private SpriteGameCharacter _character;
/** A flag indicating if the level has been initialized */
private boolean _isLevelInitialized = false;
/** A flag indicating if the level has been loaded */
private boolean _isLevelLoaded = false;
/** Holds the level width */
private float _levelWidth = 0;
/** Holds the level height */
private float _levelHeight;
/**
* Creates a new SpriteGameLevel object
*
* @param levelPath
* The path where the level file is located
* @param animator
* The animator that will be used for the level's animations
*/
public SpriteGameLevel(final String levelPath, final Animator animator) {
loadLevel(levelPath, animator);
}
/**
* Initializes the game level
*
* @param gl
* The OpenGL v1.1 object that will be used to render graphics on
* the display
*/
public void initialize(final GL11 gl) {
Sprite.Batch.clearBatch(gl);
// Initialize all the tiles in the level
int count = _tiles.size();
Sprite tile;
for (int i = 0; i < count; i++) {
tile = (Sprite) _tiles.elementAt(i);
tile.initialize(gl);
tile.setIsBatched(gl, true);
}
// Initialize all the enemies in the level
count = _enemies.size();
for (int i = 0; i < count; i++) {
((Sprite) _enemies.elementAt(i)).initialize(gl);
}
// Initialize the level goal and the character
_levelGoal.initialize(gl);
_character.initialize(gl);
_isLevelInitialized = true;
}
/**
* Initializes the game level
*
* @param gl
* The OpenGL v2.0 object that will be used to render graphics on
* the display
*/
public void initialize(final GL20 gl) {
Sprite.Batch.clearBatch(gl);
// Initialize all the tiles in the level
int count = _tiles.size();
Sprite tile;
for (int i = 0; i < count; i++) {
tile = (Sprite) _tiles.elementAt(i);
tile.initialize(gl);
tile.setIsBatched(gl, true);
}
// Initialize all the enemies in the level
count = _enemies.size();
for (int i = 0; i < count; i++) {
((Sprite) _enemies.elementAt(i)).initialize(gl);
}
// Initialize the level goal and the character
_levelGoal.initialize(gl);
_character.initialize(gl);
_isLevelInitialized = true;
}
/**
* Updates the level
*
* @return LEVEL_LOSE if the character died; LEVEL_WIN if the character
* reached the level goal; LEVEL_ACTIVE if the level is still in
* play.
*/
public int update() {
// Update the obstacle animations
_character.update();
// Calculate the character's movement restrictions and whether
// the level has been lost or won using collision detection.
return detectCollisions();
}
/**
* Renders the level using OpenGL v1.1
*
* @param gl
* The reference to the OpenGL v1.1 object.
*/
public void render(final GL11 gl) {
// Save current matrix.
gl.glPushMatrix();
// Load the identity matrix
gl.glLoadIdentity();
_character._transform.getTranslation(_t);
// Determine which horizontal section of the level to render
if (_t.x > 0.0f) {
gl.glTranslatef(-_t.x, 0.0f, 0.0f);
}
// Determine which vertical section of the level to render
if (_t.y > 0.0f && _t.y <= _levelHeight) {
gl.glTranslatef(0.0f, -_t.y, 0.0f);
} else if (_t.y > _levelHeight) {
gl.glTranslatef(0.0f, -_levelHeight, 0.0f);
}
// Render the level's tiles
int count = _tiles.size();
for (int i = 0; i < count; i++) {
((Sprite) _tiles.elementAt(i)).render(gl);
}
// Render the level's enemies
count = _enemies.size();
for (int i = 0; i < count; i++) {
((Sprite) _enemies.elementAt(i)).render(gl);
}
// Render the level's goal and the character
_levelGoal.render(gl);
_character.render(gl);
gl.glPopMatrix();
}
/**
* Renders the level using OpenGl v2.0
*
* @param gl
* The reference to the OpenGL v2.0 object
*/
public void render(final GL20 gl, final Matrix4f modelview) {
final Matrix4f m = new Matrix4f(modelview);
_character._transform.getTranslation(_t);
// Determine which horizontal section of the level to render
if (_t.x > 0.0f) {
m.translate(-_t.x, 0.0f, 0.0f);
}
// Determine which vertical section of the level to render
if (_t.y > 0.0f && _t.y <= _levelHeight) {
m.translate(0.0f, -_t.y, 0.0f);
} else if (_t.y > _levelHeight) {
m.translate(0.0f, -_levelHeight, 0.0f);
}
// Render the level's tiles
int count = _tiles.size();
for (int i = 0; i < count; i++) {
((Sprite) _tiles.elementAt(i)).render(gl, m);
}
// Render the level's enemies
count = _enemies.size();
for (int i = 0; i < count; i++) {
((Sprite) _enemies.elementAt(i)).render(gl, m);
}
// Render the level's goal and the character
_levelGoal.render(gl, m);
_character.render(gl, m);
}
/**
* Retrieves the current loaded state
*
* @return True if the level is loaded; false if the level isn't loaded
*/
public boolean isLevelLoaded() {
return _isLevelLoaded;
}
/**
* Retrieves if the level is initialized or not
*
* @return True if the level is initialized; false if the level isn't
* initialized
*/
public boolean isLevelInitialized() {
return _isLevelInitialized;
}
/**
* Retrieves the level's character object
*
* @return The level's character object
*/
public SpriteGameCharacter getCharacter() {
return _character;
}
/**
* Detects collisions and whether the level has been won or lost
*
* @return 0 if the level is still active, -1 if the level has been lost and
* 1 if the level has been won.
*/
private int detectCollisions() {
Bounds boundsLevel;
final BoundingBox bounds = (BoundingBox) _character.getBounds();
final BoundingBox prevBounds = (BoundingBox) _character.getPrevBounds();
// Get the character's corners
final Vector3f[] corners = new Vector3f[8];
for (int i = 0; i < corners.length; i++) {
corners[i] = new Vector3f();
}
bounds.getCorners(corners, 0);
final float charTop = corners[0].y;
final float charBottom = corners[1].y;
final float charLeft = corners[0].x;
final float charRight = corners[2].x;
prevBounds.getCorners(corners, 0);
final float prevCharTop = corners[0].y;
final float prevCharBottom = corners[1].y;
final float prevCharLeft = corners[0].x;
final float prevCharRight = corners[2].x;
final Vector3f characterCenter =
new Vector3f((charLeft + charRight) / 2.0f,
(charTop + charBottom) / 2.0f, 0.0f);
// Die if you fell off the level
if (characterCenter.y < -20.0f) {
return -1;
}
// Detect collision with the level end object
boundsLevel = _levelGoal.getBounds();
if (bounds.intersects(boundsLevel)) {
_character.addMovementRestriction(SpriteGameCharacter.MOVE_DOWN
| SpriteGameCharacter.MOVE_UP
| SpriteGameCharacter.MOVE_LEFT
| SpriteGameCharacter.MOVE_RIGHT);
return 1;
}
// Detect collisions with obstacles
int count = _enemies.size();
for (int i = 0; i < count; i++) {
boundsLevel = ((Sprite) _enemies.elementAt(i)).getBounds();
if (bounds.intersects(boundsLevel)) {
_character.addMovementRestriction(SpriteGameCharacter.MOVE_DOWN
| SpriteGameCharacter.MOVE_UP
| SpriteGameCharacter.MOVE_LEFT
| SpriteGameCharacter.MOVE_RIGHT);
return -1;
}
}
_character.clearMovementRestrictions();
// Detect collisions with blocks and calculate the character's movement
// restrictions
BoundingBox tileBounds;
count = _tiles.size();
Sprite tile;
for (int i = 0; i < count; i++) {
tile = (Sprite) _tiles.elementAt(i);
tileBounds = (BoundingBox) tile.getBounds();
if (bounds.intersects(tileBounds)) {
// Calculate the center point of the block's bounding box
final Vector3f min = new Vector3f();
final Vector3f max = new Vector3f();
tileBounds.getMin(min);
tileBounds.getMax(max);
final Vector3f blockCenter = new Vector3f(min);
blockCenter.add(max);
blockCenter.scale(0.5f);
// Get character scale
final Vector3f s = new Vector3f();
_character._transform.getScale(s);
// Determine which way(s) the character's
// movement should be restricted. We also set the character's
// translation vector
// so that it does not visibly intersect the blocks.
final float tileTop = max.y;
final float tileBottom = min.y;
final float tileLeft = min.x;
final float tileRight = max.x;
final float tileMidX = (tileLeft + tileRight) / 2.0f;
final float tileMidY = (tileBottom + tileTop) / 2.0f;
final Vector3f v = new Vector3f();
// If character's top or bottom is between tile's
// top and bottom, restrict horizontally
if (charTop < tileTop - 0.1f && charTop > tileBottom + 0.1f
|| charBottom > tileBottom + 0.1f
&& charBottom < tileTop - 0.1f) {
// If left edge of character is against right
// edge of block, cannot move left.
if (prevCharLeft > tileRight - 0.1f && charLeft < tileRight
&& charLeft > tileMidX) {
_character._transform.getTranslation(v);
v.x = tileRight + s.x - EPSILON;
_character._transform.setTranslation(v);
_character
.addMovementRestriction(SpriteGameCharacter.MOVE_LEFT);
}
// Right edge of character is against left
// edge of block -> cannot move right.
if (prevCharRight < tileLeft + 0.1f && charRight > tileLeft
&& charRight < tileMidX) {
_character._transform.getTranslation(v);
v.x = tileLeft - s.x + EPSILON;
_character._transform.setTranslation(v);
_character
.addMovementRestriction(SpriteGameCharacter.MOVE_RIGHT);
}
}
// If character's left or right edge is between tile's left
// and right edges, restrict vertically.
if (charLeft > tileLeft + 0.1f && charLeft < tileRight - 0.1f
|| charRight < tileRight - 0.1f
&& charRight > tileLeft + 0.1f) {
// Bottom edge of character is against top
// edge of block -> cannot move down.
if (prevCharBottom > tileTop - 0.1f && charBottom < tileTop
&& charBottom > tileMidY) {
_character._transform.getTranslation(v);
v.y = tileTop + s.y - EPSILON;
_character._transform.setTranslation(v);
_character.setTy(v.y);
_character
.addMovementRestriction(SpriteGameCharacter.MOVE_DOWN);
}
// Top edge of character is against bottom
// edge of block -> cannot move up.
if (prevCharTop < tileBottom + 0.1f && charTop > tileBottom
&& charTop < tileMidY) {
_character._transform.getTranslation(v);
v.y = tileBottom - s.y + EPSILON;
_character._transform.setTranslation(v);
_character
.addMovementRestriction(SpriteGameCharacter.MOVE_UP);
}
}
}
}
return 0;
}
/**
* Loads from the given material XML file into the given model's material
*
* @param levelPath
* Path to the material file
* @param animator
* The animator that the material can use
*/
public void loadLevel(final String levelPath, final Animator animator) {
try {
_levelWidth = 0.0f;
_levelHeight = 0.0f;
_tiles.removeAllElements();
_enemies.removeAllElements();
// Set up the SAXParser that will parse the XML file
final SAXParser parser =
SAXParserFactory.newInstance().newSAXParser();
InputStream stream;
stream =
Application.getApplication().getClass()
.getResourceAsStream(levelPath);
final DefaultHandler dh = new LevelSaxHandler(animator);
parser.parse(stream, dh);
} catch (final ParserConfigurationException e) {
SpriteGame.errorDialog(e.toString());
} catch (final SAXException e) {
SpriteGame.errorDialog(e.toString());
} catch (final IOException e) {
SpriteGame.errorDialog(e.toString());
}
}
/**
* This class parses the level file and creates the different level objects
* (tile, enemy, character, etc) with their specific attributes.
*/
private class LevelSaxHandler extends DefaultHandler {
private static final String ANIMATION = "animation";
private static final String CURVE = "curve";
private static final String DURATION = "duration";
private static final String ENEMY = "enemy";
private static final String FRAMES = "frames";
private static final String GOAL = "goal";
private static final String CHARACTER = "character";
private static final String LEVEL = "level";
private static final String LINEAR = "linear";
private static final String POSITION = "position";
private static final String REPEAT = "repeat";
private static final String ROTATE = "rotate";
private static final String SCALE = "scale";
private static final String TEXTURE = "texture";
private static final String TILE = "tile";
private static final String TIMES = "times";
private static final String TRANSLATE = "translate";
private static final String TYPE = "type";
private static final String VALUES = "values";
private int _enemyCount = 0;
private final Animator _animator;
/**
* Creates a new LevelSaxHandler object
*
* @param animator
* The animator that the different level entities can use
*/
public LevelSaxHandler(final Animator animator) {
_animator = animator;
}
/**
* @see org.xml.sax.helpers.DefaultHandler#startElement(String, String,
* String, Attributes)
*/
public void startElement(final String uri, final String localName,
final String qName, final Attributes attributes)
throws SAXException {
// Determine what kind of level object we are about to parse
if (qName.equalsIgnoreCase(TILE)) {
parseTile(attributes);
} else if (qName.equalsIgnoreCase(ENEMY)) {
parseEnemy(attributes);
} else if (qName.equalsIgnoreCase(CHARACTER)) {
parseHero(attributes);
} else if (qName.equalsIgnoreCase(GOAL)) {
parseLevelGoal(attributes);
} else if (qName.equalsIgnoreCase(ANIMATION)) {
parseAnimation(attributes);
}
}
/**
* @see org.xml.sax.helpers.DefaultHandler#endElement(String, String,
* String)
*/
public void endElement(final String uri, final String localName,
final String qName) {
// Check if we made it to the end of the file
if (qName.equalsIgnoreCase(LEVEL)) {
_isLevelLoaded = true;
_levelWidth += 1.0f;
_levelHeight += 1.0f;
} else if (qName.equalsIgnoreCase(ENEMY)) {
// We've gone to the next obstacle
_enemyCount++;
}
}
/**
* Parses data from the level load file to create a row of game tiles
*
* @param attributes
* The attributes of the tile
*/
private void parseTile(final Attributes attributes) {
final float[] pos = new float[2];
parseString(attributes.getValue(POSITION), ',', pos, 0);
final String texture = attributes.getValue(TEXTURE);
final int repeat = Integer.parseInt(attributes.getValue(REPEAT));
final String scaleStr = attributes.getValue(SCALE);
float scale = 1.0f;
// Check the tiles scale
if (scaleStr != null) {
scale = Float.parseFloat(scaleStr);
}
// Potentially increase the width of the level
if (pos[0] + repeat > _levelWidth) {
_levelWidth = pos[0] + repeat;
}
// Potentially increase the height of the level
if (pos[1] > _levelHeight) {
_levelHeight = pos[1];
}
// Add tiles for the amount of times that the tile should be
// repeated
for (int i = 0; i < repeat; i++) {
final Sprite tile =
new Sprite(texture, new Vector3f(-11.0f + (pos[0] + i)
* 2.0f, -11.0f + pos[1] * 2.0f, 0.0f));
tile._transform.scale(scale);
_tiles.addElement(tile);
}
}
/**
* Parses the data from the level load file to create an enemy
*
* @param attributes
* The attributes of the enemy
*/
private void parseEnemy(final Attributes attributes) {
final float[] pos = new float[2];
parseString(attributes.getValue(POSITION), ',', pos, 0);
final String texture = attributes.getValue(TEXTURE);
final String scaleStr = attributes.getValue(SCALE);
float scale = 1.0f;
// Check the enemy's scale
if (scaleStr != null) {
scale = Float.parseFloat(scaleStr);
}
// Create the enemy
Sprite enemy;
Bounds bounds = null;
if (texture.equals("obstacle_ball.png")) {
bounds =
new BoundingBox(new Vector3f(-0.9f, -0.9f, 0.0f),
new Vector3f(0.9f, 0.9f, 0.0f));
} else if (texture.equals("obstacle_spikewheel.png")) {
bounds = new BoundingSphere(new Vector3f(), 0.75f);
}
enemy =
new Sprite(texture, bounds, new Vector3f(-11.0f + pos[0]
* 2.0f, -11.0f + pos[1] * 2.0f, 0.0f));
enemy._transform.scale(scale);
_enemies.addElement(enemy);
}
/**
* Parses the data form the level load file to create the character
*
* @param attributes
* The attributes of the character
*/
private void parseHero(final Attributes attributes) {
final float[] pos = new float[2];
parseString(attributes.getValue(POSITION), ',', pos, 0);
final String texture = attributes.getValue(TEXTURE);
final String scaleStr = attributes.getValue(SCALE);
float scale = 1.0f;
// Check the character's scale
if (scaleStr != null) {
scale = Float.parseFloat(scaleStr);
}
// Create the character
_character =
new SpriteGameCharacter(texture, _animator, new Vector3f(
-11.0f + pos[0] * 2.0f, -11.0f + pos[1] * 2.0f,
0.0f));
_character._transform.scale(scale);
}
/**
* Parses the data from the load level file to create the level's goal
*
* @param attributes
* The attributes of the level goal
*/
private void parseLevelGoal(final Attributes attributes) {
final float[] pos = new float[2];
parseString(attributes.getValue(POSITION), ',', pos, 0);
final String texture = attributes.getValue(TEXTURE);
final String scaleStr = attributes.getValue(SCALE);
float scale = 1.0f;
// Check the goal's scale
if (scaleStr != null) {
scale = Float.parseFloat(scaleStr);
}
// Create the level goal object
_levelGoal =
new Sprite(texture, new Vector3f(-11.0f + pos[0] * 2.0f,
-11.0f + pos[1] * 2.0f, 0.0f));
_levelGoal._transform.scale(scale);
}
/**
* Parses the data from the level load file to create an animation
*
* @param attributes
* The attributes of the animation
*/
private void parseAnimation(final Attributes attributes) {
final Sprite enemy = (Sprite) _enemies.elementAt(_enemyCount);
// Get the number of key frames and the key times
final int keyframes = Integer.parseInt(attributes.getValue(FRAMES));
final float[] keyTimes = new float[keyframes];
parseString(attributes.getValue(TIMES), ';', keyTimes, 0);
// Get the duration of the animation
final long duration = Long.parseLong(attributes.getValue(DURATION));
// Default to linear
int curve = Animation.EASINGCURVE_LINEAR;
if (attributes.getValue(CURVE).equalsIgnoreCase(LINEAR)) {
curve = Animation.EASINGCURVE_LINEAR;
}
Animation animation = null;
// Check if the animation is a TRANSLATE
if (attributes.getValue(TYPE).equalsIgnoreCase(TRANSLATE)) {
animation =
parseTranslationAnimation(enemy, keyframes, keyTimes,
duration, curve, attributes);
}
// Check if the animation is a ROTATE
else if (attributes.getValue(TYPE).equalsIgnoreCase(ROTATE)) {
animation =
parseRotationAnimation(enemy, keyframes, keyTimes,
duration, curve, attributes);
}
// Check if the animation is a SCALE
else if (attributes.getValue(TYPE).equalsIgnoreCase(SCALE)) {
animation =
parseScaleAnimation(enemy, keyframes, keyTimes,
duration, curve, attributes);
}
// Add the animation to the enemy
if (animation != null) {
enemy.addAnimation(animation);
final int repeatCount =
Integer.parseInt(attributes.getValue(REPEAT));
if (repeatCount == -1) {
animation.setRepeatCount(Animation.REPEAT_COUNT_INDEFINITE);
} else {
animation.setRepeatCount(repeatCount);
}
}
}
/**
* Parses a translation animation
*
* @param enemy
* Enemy to add the animation to
* @param keyframes
* The animations key frames
* @param keyTimes
* The animations key times
* @param duration
* The duration of the animation
* @param curve
* The curve at which the animation will travel
* @param attributes
* The attributes of the animation
* @return True if adding the animation was a success; false if adding
* the animation failed
*/
private Animation parseTranslationAnimation(final Sprite enemy,
final int keyframes, final float[] keyTimes,
final long duration, final int curve,
final Attributes attributes) {
final Vector3f t = new Vector3f();
enemy._transform.getTranslation(t);
// Get the key values
final String[] strs = parseString(attributes.getValue(VALUES), ';');
// Get key values out as floats
final float[] values = new float[keyframes * 3];
for (int i = 0; i < keyframes; i++) {
// Parse the string
parseString(strs[i], ',', values, i * 3);
// Convert the coords
values[i * 3] = values[i * 3] * 2 + t.x;
values[i * 3 + 1] = values[i * 3 + 1] * 2 + t.y;
values[i * 3 + 2] = t.z;
}
// Create the animation
return _animator.addAnimation(enemy._transform,
Transform3D.ANIMATION_PROPERTY_TRANSLATE, keyframes,
keyTimes, 0, values, 0, curve, duration);
}
/**
* Parses a rotation animation
*
* @param enemy
* Enemy to add the animation to
* @param keyframes
* The animations key frames
* @param keyTimes
* The animations key times
* @param duration
* The duration of the animation
* @param curve
* The curve at which the animation will travel
* @param attributes
* The attributes of the animation
* @return True if adding the animation was a success; false if adding
* the animation failed
*/
private Animation parseRotationAnimation(final Sprite enemy,
final int keyframes, final float[] keyTimes,
final long duration, final int curve,
final Attributes attributes) {
final float[] values = new float[keyframes];
// Get the key values
parseString(attributes.getValue(VALUES), ';', values, 0);
final int size = keyframes * 4;
// Get the key values out as floats
final float[] keyValues = new float[size];
int j = 0;
// Convert the degrees into radians
for (int i = 0; i < size; i++) {
if (i % 4 == 3) {
keyValues[i] = (float) (values[j++] * 180 / Math.PI);
} else if (i % 4 == 2) {
keyValues[i] = 1.0f;
} else {
keyValues[i] = 0.0f;
}
}
// Create the animation
return _animator.addAnimation(enemy._transform,
Transform3D.ANIMATION_PROPERTY_ROTATE, keyframes, keyTimes,
0, keyValues, 0, curve, duration);
}
/**
* Parses a scale animation
*
* @param enemy
* Enemy to add the animation to
* @param keyframes
* The animations key frames
* @param keyTimes
* The animations key times
* @param duration
* The duration of the animation
* @param curve
* The curve at which the animation will travel
* @param attributes
* The attributes of the animation
* @return True if adding the animation was a success; false if adding
* the animation failed
*/
private Animation parseScaleAnimation(final Sprite enemy,
final int keyframes, final float[] keyTimes,
final long duration, final int curve,
final Attributes attributes) {
final float[] values = new float[keyframes];
// Get the key values
parseString(attributes.getValue(VALUES), ';', values, 0);
final int size = keyframes * 3;
// Get the key values out as floats
final float[] keyvalues = new float[size];
int j = 0;
for (int i = 0; i < size; i++) {
if (i % 3 == 2) {
keyvalues[i] = 1.0f;
j++;
} else {
keyvalues[i] = values[j];
}
}
return _animator.addAnimation(enemy._transform,
Transform3D.ANIMATION_PROPERTY_SCALE, keyframes, keyTimes,
0, keyvalues, 0, curve, duration);
}
/**
* Parses a string
*
* @param src
* The source string
* @param delim
* The string delimiter
* @return The string tokens
*/
private String[] parseString(final String src, final char delim) {
return explode(src, delim);
}
/**
* Parses a string
*
* @param src
* The source string
* @param delim
* The string delimiter
* @param dst
* The destination string
* @param index
* The position in the destination string to start butting
* the string tokens
*/
private void parseString(final String src, final char delim,
final float[] dst, final int index) {
final String[] tokens = explode(src, delim);
final int count = tokens.length;
for (int i = 0; i < count; i++) {
dst[i + index] = Float.parseFloat(tokens[i]);
}
}
}
/**
* Breaks the source string up into the different .tokens based on the
* delimiter
*
* @param src
* The source string
* @param delim
* The delimiter to use
* @return A string array containing the string tokens
*/
private static String[] explode(final String src, final char delim) {
// Determine the number of tokens
int count = 1;
for (int i = 0, length = src.length(); i < length; i++) {
if (src.charAt(i) == delim) {
++count;
}
}
final String[] array = new String[count];
// Fill the string array with the tokens that
// are separated by the delimiter.
for (int arrayIndex = 0, strIndex = 0; arrayIndex < array.length
&& strIndex < src.length(); arrayIndex++) {
final int nextDelimIndex = src.indexOf(delim, strIndex);
if (nextDelimIndex < 0) {
array[arrayIndex] = src.substring(strIndex);
} else {
array[arrayIndex] = src.substring(strIndex, nextDelimIndex);
strIndex = nextDelimIndex + 1;
}
}
return array;
}
}