/*
* SpriteGameCharacter.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 net.rim.device.api.animation.AbstractAnimation;
import net.rim.device.api.animation.Animatable;
import net.rim.device.api.animation.Animation;
import net.rim.device.api.animation.AnimationListener;
import net.rim.device.api.animation.AnimationValue;
import net.rim.device.api.animation.Animator;
import net.rim.device.api.math.BoundingBox;
import net.rim.device.api.math.Bounds;
import net.rim.device.api.math.Transform3D;
import net.rim.device.api.math.Vector3f;
/**
* Represents the character for the sprite game.
*/
public class SpriteGameCharacter extends Sprite implements Animatable,
AnimationListener {
/** Holds the character's texture coordinates */
private static final float[] TEXCOORDS = new float[] { 0.1875f, 0.8125f,
0.8125f, 0.8125f, 0.8125f, 0.1875f, 0.1875f, 0.1875f };
/** TODO */
private final Transform3D _prevTransform;
/** TODO */
private final Bounds _prevBounds;
/** Holds how much the character falls each frame */
private static final float FALL_INCREMENT = 0.5f;
/** Holds how much the character's shrink mode affects walking and jumping */
private static final float SHRINK_FACTOR = 0.85f;
/** Holds how much the character moves each frame when in normal mode */
private static final float MOVE_INCREMENT = 0.04f;
/** Holds whether the character is in shrink mode */
private boolean _isShrunk = false;
/** Holds whether the character is changing into or out of shrink mode */
private boolean _isChangingShrinkState = false;
/** Represents the movement of the character for the current frame */
private float _movement = 0.0f;
/** Holds the factor to multiply movement by each frame */
private static final float SLOW_FACTOR = 0.85f;
/**
* Temporary vector used to update the character's translation (see
* update()).
*/
private final Vector3f _t = new Vector3f();
/** Holds the base translation along the Y axis */
private float _ty = 0.0f;
/** Holds whether the character is jumping or not */
private boolean _isJumping = false;
/** Holds the character's jump value */
private float _jumpY = 0.0f;
/** Animation property for jumping */
public static final int ANIMATION_PROPERTY_JUMP_VALUE = 0;
/** Total number of animation properties */
public static final int ANIMATION_PROPERTY_COUNT = 1;
/** List of all animation properties */
public static final int[] ANIMATION_PROPERTIES =
{ ANIMATION_PROPERTY_JUMP_VALUE };
/** List of the component sizes for each animation property */
public static final int[] ANIMATION_PROPERTY_COMPONENTCOUNTS = { 1 };
/** Holds the character's jump animation's key times */
private static float[] JUMP_KEYTIMES = new float[] { 0.0f, 1.0f };
/** Holds the character's jump animation's key values */
private static float[] JUMP_KEYVALUES = new float[] { 0.0f, 3.6f };
/** Holds the character's jump animation */
private final Animation _jump;
/** Holds the character's default scale */
protected static final float DEFAULT_SCALE = 0.8f;
/** Holds the character's "enter shrink" animation's key values */
private static float[] SHRINK_ENTER_KEYVALUES = new float[] { 0.36f, 0.36f,
0.36f };
/** Holds the character's "exit shrink" animation's key values */
private static float[] SHRINK_EXIT_KEYVALUES = new float[] { DEFAULT_SCALE,
DEFAULT_SCALE, DEFAULT_SCALE };
/** Holds the character's "enter shrink" animation */
private final Animation _shrinkEnter;
/** Holds the character's "exit shrink" animation */
private final Animation _shrinkExit;
/** Constant used to restrict movement upwards */
public static final int MOVE_UP = 0x01;
/** Constant used to restrict movement downwards */
public static final int MOVE_DOWN = 0x02;
/** Constant used to restrict movement to the left */
public static final int MOVE_LEFT = 0x04;
/** Constant used to restrict movement to the right */
public static final int MOVE_RIGHT = 0x08;
/** Holds the directions the character is currently not allowed to move in */
private int _movementRestrictions = 0;
/**
* Creates a new SpriteGameCharacter object
*
* @param texture
* The texture to use for the character
* @param animator
* The animator that the character will use
* @param position
* The position vector for the character
*/
SpriteGameCharacter(final String texture, final Animator animator,
final Vector3f position) {
super(TEXCOORDS, texture, new BoundingBox(new Vector3f(-1.0f, -1.0f,
0.0f), new Vector3f(1.0f, 1.0f, 0.0f)), position);
_transform.setScale(new Vector3f(DEFAULT_SCALE, DEFAULT_SCALE,
DEFAULT_SCALE));
_prevTransform = new Transform3D(_transform);
_prevBounds = new BoundingBox();
// Create the character's jump animation
_jump =
animator.addAnimation(this,
SpriteGameCharacter.ANIMATION_PROPERTY_JUMP_VALUE, 2,
JUMP_KEYTIMES, 0, JUMP_KEYVALUES, 0,
Animation.EASINGCURVE_QUARTIC_OUT, 400L);
_jump.setListener(this);
// Create the character's "shrink enter" animation
_shrinkEnter =
animator.addAnimationTo(_transform,
Transform3D.ANIMATION_PROPERTY_SCALE,
SHRINK_ENTER_KEYVALUES, 0,
Animation.EASINGCURVE_QUARTIC_IN, 400L);
_shrinkEnter.setListener(this);
// Create the character's "shrink exit" animation
_shrinkExit =
animator.addAnimationTo(_transform,
Transform3D.ANIMATION_PROPERTY_SCALE,
SHRINK_EXIT_KEYVALUES, 0,
Animation.EASINGCURVE_QUARTIC_IN, 400L);
_shrinkExit.setListener(this);
}
/**
* Tells the character to move
*
* @param direction
* The direction and magnitude of movement
*/
void move(final float direction) {
if (direction != 0) {
_movement +=
direction
* (!_isShrunk ? MOVE_INCREMENT : MOVE_INCREMENT
* SHRINK_FACTOR);
}
}
/**
* Tells the character to jump (if possible - i.e. if the character is not
* in mid-air).
*/
void jump() {
// If the character is not in mid-jump or mid-fall and
// can jump, then start the jump animation.
if (!_isJumping && !canMoveDown() && canMoveUp()) {
_isJumping = true;
_jump.begin(0L);
}
}
/**
* Tells the character to enter or exit shrink mode (toggles between the
* two).
*/
void shrink() {
if (!_isChangingShrinkState) {
if (!_isShrunk) {
_shrinkEnter.begin(0L);
} else {
_shrinkExit.begin(0L);
}
_isChangingShrinkState = true;
}
}
/**
* Updates the character
*/
public void update() {
// Get the translation
_transform.getTranslation(_t);
// Update the vertical movement
if (_isJumping) {
// While the character is jumping and is allowed to move
// upward, continue the jump animation.
if (canMoveUp()) {
_t.y = _ty + (!_isShrunk ? _jumpY : _jumpY * SHRINK_FACTOR);
} else {
// If the character has hit a block from below, stop jumping
_isJumping = false;
_jump.end(0L);
}
}
// Character is falling
if (canMoveDown() && !_isChangingShrinkState) {
_t.y -= FALL_INCREMENT;
}
// Update the horizontal movement
if (_movement > 0.0f && canMoveRight() || _movement < 0.0f
&& canMoveLeft()) {
_t.x += _movement;
}
// Character is slowing down
if (_movement != 0.0f) {
_movement *= SLOW_FACTOR;
if (Math.abs(_movement) < 0.001) {
_movement = 0.0f;
}
}
// Update the translation
_prevTransform.set(_transform);
_transform.setTranslation(_t);
}
/**
* Retrieves the previous transform bounds
*
* @return The previous transform bounds
*/
public Bounds getPrevBounds() {
_prevTransform.transformBounds(_originalBounds, _prevBounds);
return _prevBounds;
}
/**
* Sets the base translation along the Y axis
*
* @param ty
* The new base translation along the Y axis
*/
public void setTy(final float ty) {
// Don't set this if the character grazes a tile while jumping
if (!_isJumping) {
_ty = ty;
}
}
/**
* Resets the character to its initial state for starting the level
*/
public void reset() {
super.reset();
_isJumping = false;
_ty = 0.0f;
_transform.setScale(new Vector3f(SpriteGameCharacter.DEFAULT_SCALE,
SpriteGameCharacter.DEFAULT_SCALE,
SpriteGameCharacter.DEFAULT_SCALE));
}
/**
* Clears the movement restrictions
*/
public void clearMovementRestrictions() {
_movementRestrictions = 0;
}
/**
* Adds the given movement restriction
*
* @param restriction
* The direction to restrict movement in
*/
public void addMovementRestriction(final int restriction) {
_movementRestrictions |= restriction;
if (restriction == MOVE_LEFT && _movement < 0.0f
|| restriction == MOVE_RIGHT && _movement > 0.0f) {
_movement = 0.0f;
}
}
/**
* Determines if the character can move up
*
* @return Whether the character can move up or not
*/
private boolean canMoveUp() {
return (_movementRestrictions & MOVE_UP) == 0;
}
/**
* Determines if the character can move down
*
* @return Whether the character can move down or not
*/
private boolean canMoveDown() {
return (_movementRestrictions & MOVE_DOWN) == 0;
}
/**
* Determines if the character can move left
*
* @return Whether the character can move left or not
*/
private boolean canMoveLeft() {
return (_movementRestrictions & MOVE_LEFT) == 0;
}
/**
* Determines if the character can move right
*
* @return Whether the character can move right or not
*/
private boolean canMoveRight() {
return (_movementRestrictions & MOVE_RIGHT) == 0;
}
/**
* @see net.rim.device.api.animation.Animatable#getAnimationValue(int,
* AnimationValue)
*/
public void
getAnimationValue(final int property, final AnimationValue value) {
switch (property) {
case ANIMATION_PROPERTY_JUMP_VALUE:
value.setFloat(0, _jumpY);
break;
default:
break;
}
}
/**
* @see net.rim.device.api.animation.Animatable#setAnimationValue(int,
* AnimationValue)
*/
public void
setAnimationValue(final int property, final AnimationValue value) {
switch (property) {
case ANIMATION_PROPERTY_JUMP_VALUE:
_jumpY = value.getFloat(0);
break;
default:
break;
}
}
/**
* @see net.rim.device.api.animation.Animatable#getAnimationPropertyComponentCount(int)
*/
public int getAnimationPropertyComponentCount(final int property) {
if (property >= 0 && property < ANIMATION_PROPERTY_COUNT) {
return ANIMATION_PROPERTY_COMPONENTCOUNTS[property];
}
return 0;
}
/**
* @see net.rim.device.api.animation.AnimationListener#animationBegin(AbstractAnimation)
*/
public void animationBegin(final AbstractAnimation animation) {
// Not implemented
}
/**
* @see net.rim.device.api.animation.AnimationListener#animationEnd(AbstractAnimation)
*/
public void animationEnd(final AbstractAnimation animation) {
// Update the shrink state variables based on the status
// of the shrink animations.
if (animation == _shrinkEnter) {
_isShrunk = true;
_isChangingShrinkState = false;
} else if (animation == _shrinkExit) {
_isShrunk = false;
_isChangingShrinkState = false;
} else if (animation == _jump) {
_isJumping = false;
}
}
}