/**
* <p>Title: Sprite.java</p>
*
* <p>Description: Sprite MIDP2.0</p>
*
* <p>Copyright: Copyright (c) 2009</p>
*
* @author fengsheng.yang
*
* @version 1.0
*
* @Date 2009-5-14
*/
package javax.microedition.lcdui.game;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
public class Sprite extends Layer {
// ----- definitions for the various transformations possible -----
/**
* No transform is applied to the Sprite. This constant has a value of
* <code>0</code>.
*/
public static final int TRANS_NONE = 0;
/**
* Causes the Sprite to appear rotated clockwise by 90 degrees. This
* constant has a value of <code>5</code>.
*/
public static final int TRANS_ROT90 = 5;
/**
* Causes the Sprite to appear rotated clockwise by 180 degrees. This
* constant has a value of <code>3</code>.
*/
public static final int TRANS_ROT180 = 3;
/**
* Causes the Sprite to appear rotated clockwise by 270 degrees. This
* constant has a value of <code>6</code>.
*/
public static final int TRANS_ROT270 = 6;
/**
* Causes the Sprite to appear reflected about its vertical center. This
* constant has a value of <code>2</code>.
*/
public static final int TRANS_MIRROR = 2;
/**
* Causes the Sprite to appear reflected about its vertical center and then
* rotated clockwise by 90 degrees. This constant has a value of
* <code>7</code>.
*/
public static final int TRANS_MIRROR_ROT90 = 7;
/**
* Causes the Sprite to appear reflected about its vertical center and then
* rotated clockwise by 180 degrees. This constant has a value of
* <code>1</code>.
*/
public static final int TRANS_MIRROR_ROT180 = 1;
/**
* Causes the Sprite to appear reflected about its vertical center and then
* rotated clockwise by 270 degrees. This constant has a value of
* <code>4</code>.
*/
public static final int TRANS_MIRROR_ROT270 = 4;
// --- member variables
/**
* If this bit is set, it denotes that the transform causes the axes to be
* interchanged
*/
private static final int INVERTED_AXES = 0x4;
/**
* If this bit is set, it denotes that the transform causes the x axis to be
* flipped.
*/
private static final int X_FLIP = 0x2;
/**
* If this bit is set, it denotes that the transform causes the y axis to be
* flipped.
*/
private static final int Y_FLIP = 0x1;
/**
* Bit mask for channel value in ARGB pixel.
*/
private static final int ALPHA_BITMASK = 0xff000000;
/**
* Source image
*/
Bitmap sourceImage;
/**
* The number of frames
*/
int numberFrames; // = 0;
/**
* list of X coordinates of individual frames
*/
int[] frameCoordsX;
/**
* list of Y coordinates of individual frames
*/
int[] frameCoordsY;
/**
* Width of each frame in the source image
*/
int srcFrameWidth;
/**
* Height of each frame in the source image
*/
int srcFrameHeight;
/**
* The sequence in which to display the Sprite frames
*/
int[] frameSequence;
/**
* The sequence index
*/
private int sequenceIndex; // = 0
/**
* Set to true if custom sequence is used.
*/
private boolean customSequenceDefined; // = false;
// -- reference point
/**
* Horizontal offset of the reference point from the top left of the sprite.
*/
int dRefX; // =0
/**
* Vertical offset of the reference point from the top left of the sprite.
*/
int dRefY; // =0
// --- collision rectangle
/**
* Horizontal offset of the top left of the collision rectangle from the top
* left of the sprite.
*/
int collisionRectX; // =0
/**
* Vertical offset of the top left of the collision rectangle from the top
* left of the sprite.
*/
int collisionRectY; // =0
/**
* Width of the bounding rectangle for collision detection.
*/
int collisionRectWidth;
/**
* Height of the bounding rectangle for collision detection.
*/
int collisionRectHeight;
// --- transformation(s)
// --- values that may change on setting transformations
// start with t_
/**
* The current transformation in effect.
*/
int t_currentTransformation;
/**
* Horizontal offset of the top left of the collision rectangle from the top
* left of the sprite.
*/
int t_collisionRectX;
/**
* Vertical offset of the top left of the collision rectangle from the top
* left of the sprite.
*/
int t_collisionRectY;
/**
* Width of the bounding rectangle for collision detection, with the current
* transformation in effect.
*/
int t_collisionRectWidth;
/**
* Height of the bounding rectangle for collision detection, with the
* current transformation in effect.
*/
int t_collisionRectHeight;
// ----- Constructors -----
/**
* Creates a new non-animated Sprite using the provided Image. This
* constructor is functionally equivalent to calling
* <code>new Sprite(image, image.getWidth(), image.getHeight())</code>
* <p>
* By default, the Sprite is visible and its upper-left corner is positioned
* at (0,0) in the painter's coordinate system. <br>
*
* @param image
* the <code>Image</code> to use as the single frame for the
* </code>Sprite
* @throws NullPointerException
* if <code>img</code> is <code>null</code>
*/
public Sprite(Bitmap image) {
super(image.getWidth(), image.getHeight());
initializeFrames(image, image.getWidth(), image.getHeight(), false);
// initialize collision rectangle
initCollisionRectBounds();
// current transformation is TRANS_NONE
this.setTransformImpl(TRANS_NONE);
}
/**
* Creates a new animated Sprite using frames contained in the provided
* Image. The frames must be equally sized, with the dimensions specified by
* <code>frameWidth</code> and <code>frameHeight</code>. They may be laid
* out in the image horizontally, vertically, or as a grid. The width of the
* source image must be an integer multiple of the frame width, and the
* height of the source image must be an integer multiple of the frame
* height. The values returned by {@link Layer#getWidth} and
* {@link Layer#getHeight} will reflect the frame width and frame height
* subject to the Sprite's current transform.
* <p>
* Sprites have a default frame sequence corresponding to the raw frame
* numbers, starting with frame 0. The frame sequence may be modified with
* {@link #setFrameSequence(int[])}.
* <p>
* By default, the Sprite is visible and its upper-left corner is positioned
* at (0,0) in the painter's coordinate system.
* <p>
*
* @param image
* the <code>Image</code> to use for <code>Sprite</code>
* @param frameWidth
* the <code>width</code>, in pixels, of the individual raw
* frames
* @param frameHeight
* the <code>height</code>, in pixels, of the individual raw
* frames
* @throws NullPointerException
* if <code>img</code> is <code>null</code>
* @throws IllegalArgumentException
* if <code>frameHeight</code> or <code>frameWidth</code> is
* less than <code>1</code>
* @throws IllegalArgumentException
* if the <code>image</code> width is not an integer multiple of
* the <code>frameWidth</code>
* @throws IllegalArgumentException
* if the <code>image</code> height is not an integer multiple
* of the <code>frameHeight</code>
*/
public Sprite(Bitmap image, int frameWidth, int frameHeight) {
super(frameWidth, frameHeight);
// if img is null img.getWidth() will throw NullPointerException
if ((frameWidth < 1 || frameHeight < 1)
|| ((image.getWidth() % frameWidth) != 0)
|| ((image.getHeight() % frameHeight) != 0)) {
throw new IllegalArgumentException();
}
// construct the array of images that
// we use as "frames" for the sprite.
// use default frame , sequence index = 0
initializeFrames(image, frameWidth, frameHeight, false);
// initialize collision rectangle
initCollisionRectBounds();
// current transformation is TRANS_NONE
this.setTransformImpl(TRANS_NONE);
}
/**
* Creates a new Sprite from another Sprite.
* <p>
*
* All instance attributes (raw frames, position, frame sequence, current
* frame, reference point, collision rectangle, transform, and visibility)
* of the source Sprite are duplicated in the new Sprite.
*
* @param s
* the <code>Sprite</code> to create a copy of
* @throws NullPointerException
* if <code>s</code> is <code>null</code>
*
*/
public Sprite(Sprite s) {
super(s != null ? s.getWidth() : 0, s != null ? s.getHeight() : 0);
if (s == null) {
throw new NullPointerException();
}
this.sourceImage = s.sourceImage;// Image.createImage(s.sourceImage);
this.numberFrames = s.numberFrames;
this.frameCoordsX = new int[this.numberFrames];
this.frameCoordsY = new int[this.numberFrames];
System.arraycopy(s.frameCoordsX, 0, this.frameCoordsX, 0, s
.getRawFrameCount());
System.arraycopy(s.frameCoordsY, 0, this.frameCoordsY, 0, s
.getRawFrameCount());
this.x = s.getX();
this.y = s.getY();
// these fields are set by defining a reference point
this.dRefX = s.dRefX;
this.dRefY = s.dRefY;
// these fields are set when defining a collision rectangle
this.collisionRectX = s.collisionRectX;
this.collisionRectY = s.collisionRectY;
this.collisionRectWidth = s.collisionRectWidth;
this.collisionRectHeight = s.collisionRectHeight;
// these fields are set when creating a Sprite from an Image
this.srcFrameWidth = s.srcFrameWidth;
this.srcFrameHeight = s.srcFrameHeight;
// the above fields are used in setTransform()
// which is why we set them first, then call setTransformImpl()
// to set up internally used data structures.
setTransformImpl(s.t_currentTransformation);
this.setVisible(s.isVisible());
this.frameSequence = new int[s.getFrameSequenceLength()];
this.setFrameSequence(s.frameSequence);
this.setFrame(s.getFrame());
this.setRefPixelPosition(s.getRefPixelX(), s.getRefPixelY());
}
// ----- public methods -----
/**
* Defines the reference pixel for this Sprite. The pixel is defined by its
* location relative to the upper-left corner of the Sprite's un-transformed
* frame, and it may lay outside of the frame's bounds.
* <p>
* When a transformation is applied, the reference pixel is defined relative
* to the Sprite's initial upper-left corner before transformation. This
* corner may no longer appear as the upper-left corner in the painter's
* coordinate system under current transformation.
* <p>
* By default, a Sprite's reference pixel is located at (0,0); that is, the
* pixel in the upper-left corner of the raw frame.
* <p>
* Changing the reference pixel does not change the Sprite's physical
* position in the painter's coordinate system; that is, the values returned
* by {@link #getX getX()} and {@link #getY getY()} will not change as a
* result of defining the reference pixel. However, subsequent calls to
* methods that involve the reference pixel will be impacted by its new
* definition.
*
* @param x
* the horizontal location of the reference pixel, relative to
* the left edge of the un-transformed frame
* @param y
* the vertical location of the reference pixel, relative to the
* top edge of the un-transformed frame
* @see #setRefPixelPosition
* @see #getRefPixelX
* @see #getRefPixelY
*/
public void defineReferencePixel(int x, int y) {
dRefX = x;
dRefY = y;
}
/**
* Sets this Sprite's position such that its reference pixel is located at
* (x,y) in the painter's coordinate system.
*
* @param x
* the horizontal location at which to place the reference pixel
* @param y
* the vertical location at which to place the reference pixel
* @see #defineReferencePixel
* @see #getRefPixelX
* @see #getRefPixelY
*/
public void setRefPixelPosition(int x, int y) {
// update this.x and this.y
this.x = x
- getTransformedPtX(dRefX, dRefY, this.t_currentTransformation);
this.y = y
- getTransformedPtY(dRefX, dRefY, this.t_currentTransformation);
}
/**
* Gets the horizontal position of this Sprite's reference pixel in the
* painter's coordinate system.
*
* @return the horizontal location of the reference pixel
* @see #defineReferencePixel
* @see #setRefPixelPosition
* @see #getRefPixelY
*/
public int getRefPixelX() {
return (this.x + getTransformedPtX(dRefX, dRefY,
this.t_currentTransformation));
}
/**
* Gets the vertical position of this Sprite's reference pixel in the
* painter's coordinate system.
*
* @return the vertical location of the reference pixel
* @see #defineReferencePixel
* @see #setRefPixelPosition
* @see #getRefPixelX
*/
public int getRefPixelY() {
return (this.y + getTransformedPtY(dRefX, dRefY,
this.t_currentTransformation));
}
/**
* Selects the current frame in the frame sequence.
* <p>
* The current frame is rendered when {@link #paint(Graphics)} is called.
* <p>
* The index provided refers to the desired entry in the frame sequence, not
* the index of the actual frame itself.
*
* @param sequenceIndex
* the index of of the desired entry in the frame sequence
* @throws IndexOutOfBoundsException
* if <code>frameIndex</code> is less than<code>0</code>
* @throws IndexOutOfBoundsException
* if <code>frameIndex</code> is equal to or greater than the
* length of the current frame sequence (or the number of raw
* frames for the default sequence)
* @see #setFrameSequence(int[])
* @see #getFrame
*/
public void setFrame(int sequenceIndex) {
if (sequenceIndex < 0 || sequenceIndex >= frameSequence.length) {
throw new IndexOutOfBoundsException();
}
this.sequenceIndex = sequenceIndex;
}
/**
* Gets the current index in the frame sequence.
* <p>
* The index returned refers to the current entry in the frame sequence, not
* the index of the actual frame that is displayed.
*
* @return the current index in the frame sequence
* @see #setFrameSequence(int[])
* @see #setFrame
*/
public final int getFrame() {
return sequenceIndex;
}
/**
* Gets the number of raw frames for this Sprite. The value returned
* reflects the number of frames; it does not reflect the length of the
* Sprite's frame sequence. However, these two values will be the same if
* the default frame sequence is used.
*
* @return the number of raw frames for this Sprite
* @see #getFrameSequenceLength
*/
public int getRawFrameCount() {
return numberFrames;
}
/**
* Gets the number of elements in the frame sequence. The value returned
* reflects the length of the Sprite's frame sequence; it does not reflect
* the number of raw frames. However, these two values will be the same if
* the default frame sequence is used.
*
* @return the number of elements in this Sprite's frame sequence
* @see #getRawFrameCount
*/
public int getFrameSequenceLength() {
return frameSequence.length;
}
/**
* Selects the next frame in the frame sequence.
* <p>
*
* The frame sequence is considered to be circular, i.e. if
* {@link #nextFrame} is called when at the end of the sequence, this method
* will advance to the first entry in the sequence.
*
* @see #setFrameSequence(int[])
* @see #prevFrame
*/
public void nextFrame() {
sequenceIndex = (sequenceIndex + 1) % frameSequence.length;
}
/**
* Selects the previous frame in the frame sequence.
* <p>
*
* The frame sequence is considered to be circular, i.e. if
* {@link #prevFrame} is called when at the start of the sequence, this
* method will advance to the last entry in the sequence.
*
* @see #setFrameSequence(int[])
* @see #nextFrame
*/
public void prevFrame() {
if (sequenceIndex == 0) {
sequenceIndex = frameSequence.length - 1;
} else {
sequenceIndex--;
}
}
/**
* Draws the Sprite.
* <P>
* Draws current frame of Sprite using the provided Graphics object. The
* Sprite's upper left corner is rendered at the Sprite's current position
* relative to the origin of the Graphics object. The current position of
* the Sprite's upper-left corner can be retrieved by calling
* {@link #getX()} and {@link #getY()}.
* <P>
* Rendering is subject to the clip region of the Graphics object. The
* Sprite will be drawn only if it is visible.
* <p>
* If the Sprite's Image is mutable, the Sprite is rendered using the
* current contents of the Image.
*
* @param g
* the graphics object to draw <code>Sprite</code> on
* @throws NullPointerException
* if <code>g</code> is <code>null</code>
*
*/
public final void paint(Canvas canvas) {
// managing the painting order is the responsibility of
// the layermanager, so depth is ignored
if (canvas == null) {
throw new NullPointerException();
}
if (visible) {
// width and height of the source
// image is the width and height
// of the original frame
drawImage(canvas,this.x,this.y,sourceImage,frameCoordsX[frameSequence[sequenceIndex]],
frameCoordsY[frameSequence[sequenceIndex]],
srcFrameWidth,
srcFrameHeight);
}
}
private void drawImage(Canvas canvas, int x, int y,
Bitmap bsrc, int sx, int sy, int w, int h) {
Rect rect_src = new Rect();
rect_src.left = sx;
rect_src.right = sx + w;
rect_src.top = sy;
rect_src.bottom = sy + h;
Rect rect_dst = new Rect();
rect_dst.left = x;
rect_dst.right = x + w;
rect_dst.top = y;
rect_dst.bottom = y + h;
canvas.drawBitmap(bsrc, rect_src, rect_dst, null);
rect_src = null;
rect_dst = null;
}
/**
* Set the frame sequence for this Sprite.
* <p>
*
* All Sprites have a default sequence that displays the Sprites frames in
* order. This method allows for the creation of an arbitrary sequence using
* the available frames. The current index in the frame sequence is reset to
* zero as a result of calling this method.
* <p>
* The contents of the sequence array are copied when this method is called;
* thus, any changes made to the array after this method returns have no
* effect on the Sprite's frame sequence.
* <P>
* Passing in <code>null</code> causes the Sprite to revert to the default
* frame sequence.
* <p>
*
* @param sequence
* an array of integers, where each integer represents a frame
* index
*
* @throws ArrayIndexOutOfBoundsException
* if seq is non-null and any member of the array has a value
* less than <code>0</code> or greater than or equal to the
* number of frames as reported by {@link #getRawFrameCount()}
* @throws IllegalArgumentException
* if the array has less than <code>1</code> element
* @see #nextFrame
* @see #prevFrame
* @see #setFrame
* @see #getFrame
*
*/
public void setFrameSequence(int sequence[]) {
if (sequence == null) {
// revert to the default sequence
sequenceIndex = 0;
customSequenceDefined = false;
frameSequence = new int[numberFrames];
// copy frames indices into frameSequence
for (int i = 0; i < numberFrames; i++) {
frameSequence[i] = i;
}
return;
}
if (sequence.length < 1) {
throw new IllegalArgumentException();
}
for (int i = 0; i < sequence.length; i++) {
if (sequence[i] < 0 || sequence[i] >= numberFrames) {
throw new ArrayIndexOutOfBoundsException();
}
}
customSequenceDefined = true;
frameSequence = new int[sequence.length];
System.arraycopy(sequence, 0, frameSequence, 0, sequence.length);
sequenceIndex = 0;
}
/**
* Changes the Image containing the Sprite's frames.
* <p>
* Replaces the current raw frames of the Sprite with a new set of raw
* frames. See the constructor {@link #Sprite(Image, int, int)} for
* information on how the frames are created from the image. The values
* returned by {@link Layer#getWidth} and {@link Layer#getHeight} will
* reflect the new frame width and frame height subject to the Sprite's
* current transform.
* <p>
* Changing the image for the Sprite could change the number of raw frames.
* If the new frame set has as many or more raw frames than the previous
* frame set, then:
* <ul>
* <li>The current frame will be unchanged
* <li>If a custom frame sequence has been defined (using
* {@link #setFrameSequence(int[])}), it will remain unchanged. If no custom
* frame sequence is defined (i.e. the default frame sequence is in use),
* the default frame sequence will be updated to be the default frame
* sequence for the new frame set. In other words, the new default frame
* sequence will include all of the frames from the new raw frame set, as if
* this new image had been used in the constructor.
* </ul>
* <p>
* If the new frame set has fewer frames than the previous frame set, then:
* <ul>
* <li>The current frame will be reset to entry 0
* <li>Any custom frame sequence will be discarded and the frame sequence
* will revert to the default frame sequence for the new frame set.
* </ul>
* <p>
* The reference point location is unchanged as a result of calling this
* method, both in terms of its defined location within the Sprite and its
* position in the painter's coordinate system. However, if the frame size
* is changed and the Sprite has been transformed, the position of the
* Sprite's upper-left corner may change such that the reference point
* remains stationary.
* <p>
* If the Sprite's frame size is changed by this method, the collision
* rectangle is reset to its default value (i.e. it is set to the new bounds
* of the untransformed Sprite).
* <p>
*
* @param img
* the <code>Image</code> to use for <code>Sprite</code>
* @param frameWidth
* the width in pixels of the individual raw frames
* @param frameHeight
* the height in pixels of the individual raw frames
* @throws NullPointerException
* if <code>img</code> is <code>null</code>
* @throws IllegalArgumentException
* if <code>frameHeight</code> or <code>frameWidth</code> is
* less than <code>1</code>
* @throws IllegalArgumentException
* if the image width is not an integer multiple of the
* <code>frameWidth</code>
* @throws IllegalArgumentException
* if the image height is not an integer multiple of the
* <code>frameHeight</code>
*/
public void setImage(Bitmap img, int frameWidth, int frameHeight) {
// if image is null image.getWidth() will throw NullPointerException
if ((frameWidth < 1 || frameHeight < 1)
|| ((img.getWidth() % frameWidth) != 0)
|| ((img.getHeight() % frameHeight) != 0)) {
throw new IllegalArgumentException();
}
int noOfFrames = (img.getWidth() / frameWidth)
* (img.getHeight() / frameHeight);
boolean maintainCurFrame = true;
if (noOfFrames < numberFrames) {
// use default frame , sequence index = 0
maintainCurFrame = false;
customSequenceDefined = false;
}
if (!((srcFrameWidth == frameWidth) && (srcFrameHeight == frameHeight))) {
// computing is the location
// of the reference pixel in the painter's coordinate system.
// and then use this to find x and y position of the Sprite
int oldX = this.x
+ getTransformedPtX(dRefX, dRefY,
this.t_currentTransformation);
int oldY = this.y
+ getTransformedPtY(dRefX, dRefY,
this.t_currentTransformation);
setWidthImpl(frameWidth);
setHeightImpl(frameHeight);
initializeFrames(img, frameWidth, frameHeight, maintainCurFrame);
// initialize collision rectangle
initCollisionRectBounds();
// set the new x and y position of the Sprite
this.x = oldX
- getTransformedPtX(dRefX, dRefY,
this.t_currentTransformation);
this.y = oldY
- getTransformedPtY(dRefX, dRefY,
this.t_currentTransformation);
// Calculate transformed sprites collision rectangle
// and transformed width and height
computeTransformedBounds(this.t_currentTransformation);
} else {
// just reinitialize the animation frames.
initializeFrames(img, frameWidth, frameHeight, maintainCurFrame);
}
}
/**
* Defines the Sprite's bounding rectangle that is used for collision
* detection purposes. This rectangle is specified relative to the
* un-transformed Sprite's upper-left corner and defines the area that is
* checked for collision detection. For pixel-level detection, only those
* pixels within the collision rectangle are checked.
*
* By default, a Sprite's collision rectangle is located at 0,0 as has the
* same dimensions as the Sprite. The collision rectangle may be specified
* to be larger or smaller than the default rectangle; if made larger, the
* pixels outside the bounds of the Sprite are considered to be transparent
* for pixel-level collision detection.
*
* @param x
* the horizontal location of the collision rectangle relative to
* the untransformed Sprite's left edge
* @param y
* the vertical location of the collision rectangle relative to
* the untransformed Sprite's top edge
* @param width
* the width of the collision rectangle
* @param height
* the height of the collision rectangle
* @throws IllegalArgumentException
* if the specified <code>width</code> or <code>height</code> is
* less than <code>0</code>
*/
public void defineCollisionRectangle(int x, int y, int width, int height) {
if (width < 0 || height < 0) {
throw new IllegalArgumentException();
}
collisionRectX = x;
collisionRectY = y;
collisionRectWidth = width;
collisionRectHeight = height;
// call set transform with current transformation to
// update transformed sprites collision rectangle
setTransformImpl(t_currentTransformation);
}
/**
* Sets the transform for this Sprite. Transforms can be applied to a Sprite
* to change its rendered appearance. Transforms are applied to the original
* Sprite image; they are not cumulative, nor can they be combined. By
* default, a Sprite's transform is {@link #TRANS_NONE}.
* <P>
* Since some transforms involve rotations of 90 or 270 degrees, their use
* may result in the overall width and height of the Sprite being swapped.
* As a result, the values returned by {@link Layer#getWidth} and
* {@link Layer#getHeight} may change.
* <p>
* The collision rectangle is also modified by the transform so that it
* remains static relative to the pixel data of the Sprite. Similarly, the
* defined reference pixel is unchanged by this method, but its visual
* location within the Sprite may change as a result.
* <P>
* This method repositions the Sprite so that the location of the reference
* pixel in the painter's coordinate system does not change as a result of
* changing the transform. Thus, the reference pixel effectively becomes the
* centerpoint for the transform. Consequently, the values returned by
* {@link #getRefPixelX} and {@link #getRefPixelY} will be the same both
* before and after the transform is applied, but the values returned by
* {@link #getX getX()} and {@link #getY getY()} may change.
* <p>
*
* @param transform
* the desired transform for this <code>Sprite</code>
* @throws IllegalArgumentException
* if the requested <code>transform</code> is invalid
* @see #TRANS_NONE
* @see #TRANS_ROT90
* @see #TRANS_ROT180
* @see #TRANS_ROT270
* @see #TRANS_MIRROR
* @see #TRANS_MIRROR_ROT90
* @see #TRANS_MIRROR_ROT180
* @see #TRANS_MIRROR_ROT270
*
*/
public void setTransform(int transform) {
setTransformImpl(transform);
}
/**
* Checks for a collision between this Sprite and the specified Sprite.
* <P>
* If pixel-level detection is used, a collision is detected only if opaque
* pixels collide. That is, an opaque pixel in the first Sprite would have
* to collide with an opaque pixel in the second Sprite for a collision to
* be detected. Only those pixels within the Sprites' respective collision
* rectangles are checked.
* <P>
* If pixel-level detection is not used, this method simply checks if the
* Sprites' collision rectangles intersect.
* <P>
* Any transforms applied to the Sprites are automatically accounted for.
* <P>
* Both Sprites must be visible in order for a collision to be detected.
* <P>
*
* @param s
* the <code>Sprite</code> to test for collision with
* @param pixelLevel
* <code>true</code> to test for collision on a pixel-by-pixel
* basis, <code>false</code> to test using simple bounds
* checking.
* @return <code>true</code> if the two Sprites have collided, otherwise
* <code>false</code>
* @throws NullPointerException
* if Sprite <code>s</code> is <code>null</code>
*/
public final boolean collidesWith(Sprite s, boolean pixelLevel) {
// check if either of the Sprite's are not visible
if (!(s.visible && this.visible)) {
return false;
}
// these are package private
// and can be accessed directly
int otherLeft = s.x + s.t_collisionRectX;
int otherTop = s.y + s.t_collisionRectY;
int otherRight = otherLeft + s.t_collisionRectWidth;
int otherBottom = otherTop + s.t_collisionRectHeight;
int left = this.x + this.t_collisionRectX;
int top = this.y + this.t_collisionRectY;
int right = left + this.t_collisionRectWidth;
int bottom = top + this.t_collisionRectHeight;
// check if the collision rectangles of the two sprites intersect
if (intersectRect(otherLeft, otherTop, otherRight, otherBottom, left,
top, right, bottom)) {
// collision rectangles intersect
if (pixelLevel) {
// we need to check pixel level collision detection.
// use only the coordinates within the Sprite frame if
// the collision rectangle is larger than the Sprite
// frame
if (this.t_collisionRectX < 0) {
left = this.x;
}
if (this.t_collisionRectY < 0) {
top = this.y;
}
if ((this.t_collisionRectX + this.t_collisionRectWidth) > this.width) {
right = this.x + this.width;
}
if ((this.t_collisionRectY + this.t_collisionRectHeight) > this.height) {
bottom = this.y + this.height;
}
// similarly for the other Sprite
if (s.t_collisionRectX < 0) {
otherLeft = s.x;
}
if (s.t_collisionRectY < 0) {
otherTop = s.y;
}
if ((s.t_collisionRectX + s.t_collisionRectWidth) > s.width) {
otherRight = s.x + s.width;
}
if ((s.t_collisionRectY + s.t_collisionRectHeight) > s.height) {
otherBottom = s.y + s.height;
}
// recheck if the updated collision area rectangles intersect
if (!intersectRect(otherLeft, otherTop, otherRight,
otherBottom, left, top, right, bottom)) {
// if they don't intersect, return false;
return false;
}
// the updated collision rectangles intersect,
// go ahead with collision detection
// find intersecting region,
// within the collision rectangles
int intersectLeft = (left < otherLeft) ? otherLeft : left;
int intersectTop = (top < otherTop) ? otherTop : top;
// used once, optimize.
int intersectRight = (right < otherRight) ? right : otherRight;
int intersectBottom = (bottom < otherBottom) ? bottom
: otherBottom;
int intersectWidth = Math.abs(intersectRight - intersectLeft);
int intersectHeight = Math.abs(intersectBottom - intersectTop);
// have the coordinates in painter space,
// need coordinates of top left and width, height
// in source image of Sprite.
int thisImageXOffset = getImageTopLeftX(intersectLeft,
intersectTop, intersectRight, intersectBottom);
int thisImageYOffset = getImageTopLeftY(intersectLeft,
intersectTop, intersectRight, intersectBottom);
int otherImageXOffset = s.getImageTopLeftX(intersectLeft,
intersectTop, intersectRight, intersectBottom);
int otherImageYOffset = s.getImageTopLeftY(intersectLeft,
intersectTop, intersectRight, intersectBottom);
// check if opaque pixels intersect.
return doPixelCollision(thisImageXOffset, thisImageYOffset,
otherImageXOffset, otherImageYOffset, this.sourceImage,
this.t_currentTransformation, s.sourceImage,
s.t_currentTransformation, intersectWidth,
intersectHeight);
} else {
// collides!
return true;
}
}
return false;
}
/**
* Checks for a collision between this Sprite and the specified TiledLayer.
* If pixel-level detection is used, a collision is detected only if opaque
* pixels collide. That is, an opaque pixel in the Sprite would have to
* collide with an opaque pixel in TiledLayer for a collision to be
* detected. Only those pixels within the Sprite's collision rectangle are
* checked.
* <P>
* If pixel-level detection is not used, this method simply checks if the
* Sprite's collision rectangle intersects with a non-empty cell in the
* TiledLayer.
* <P>
* Any transform applied to the Sprite is automatically accounted for.
* <P>
* The Sprite and the TiledLayer must both be visible in order for a
* collision to be detected.
* <P>
*
* @param t
* the <code>TiledLayer</code> to test for collision with
* @param pixelLevel
* <code>true</code> to test for collision on a pixel-by-pixel
* basis, <code>false</code> to test using simple bounds checking
* against non-empty cells.
* @return <code>true</code> if this <code>Sprite</code> has collided with
* the <code>TiledLayer</code>, otherwise <code>false</code>
* @throws NullPointerException
* if <code>t</code> is <code>null</code>
*/
public final boolean collidesWith(TiledLayer t, boolean pixelLevel) {
// check if either this Sprite or the TiledLayer is not visible
if (!(t.visible && this.visible)) {
return false;
}
// dimensions of tiledLayer, cell, and
// this Sprite's collision rectangle
// these are package private
// and can be accessed directly
int tLx1 = t.x;
int tLy1 = t.y;
int tLx2 = tLx1 + t.width;
int tLy2 = tLy1 + t.height;
int tW = t.getCellWidth();
int tH = t.getCellHeight();
int sx1 = this.x + this.t_collisionRectX;
int sy1 = this.y + this.t_collisionRectY;
int sx2 = sx1 + this.t_collisionRectWidth;
int sy2 = sy1 + this.t_collisionRectHeight;
// number of cells
int tNumCols = t.getColumns();
int tNumRows = t.getRows();
// temporary loop variables.
int startCol; // = 0;
int endCol; // = 0;
int startRow; // = 0;
int endRow; // = 0;
if (!intersectRect(tLx1, tLy1, tLx2, tLy2, sx1, sy1, sx2, sy2)) {
// if the collision rectangle of the sprite
// does not intersect with the dimensions of the entire
// tiled layer
return false;
}
// so there is an intersection
// note sx1 < sx2, tLx1 < tLx2, sx2 > tLx1 from intersectRect()
// use <= for comparison as this saves us some
// computation - the result will be 0
startCol = (sx1 <= tLx1) ? 0 : (sx1 - tLx1) / tW;
startRow = (sy1 <= tLy1) ? 0 : (sy1 - tLy1) / tH;
// since tLx1 < sx2 < tLx2, the computation will yield
// a result between 0 and tNumCols - 1
// subtract by 1 because sx2,sy2 represent
// the enclosing bounds of the sprite, not the
// locations in the coordinate system.
endCol = (sx2 < tLx2) ? ((sx2 - 1 - tLx1) / tW) : tNumCols - 1;
endRow = (sy2 < tLy2) ? ((sy2 - 1 - tLy1) / tH) : tNumRows - 1;
if (!pixelLevel) {
// check for intersection with a non-empty cell,
for (int row = startRow; row <= endRow; row++) {
for (int col = startCol; col <= endCol; col++) {
if (t.getCell(col, row) != 0) {
return true;
}
}
}
// worst case! we scanned through entire
// overlapping region and
// all the cells are empty!
return false;
} else {
// do pixel level
// we need to check pixel level collision detection.
// use only the coordinates within the Sprite frame if
// the collision rectangle is larger than the Sprite
// frame
if (this.t_collisionRectX < 0) {
sx1 = this.x;
}
if (this.t_collisionRectY < 0) {
sy1 = this.y;
}
if ((this.t_collisionRectX + this.t_collisionRectWidth) > this.width) {
sx2 = this.x + this.width;
}
if ((this.t_collisionRectY + this.t_collisionRectHeight) > this.height) {
sy2 = this.y + this.height;
}
if (!intersectRect(tLx1, tLy1, tLx2, tLy2, sx1, sy1, sx2, sy2)) {
return (false);
}
// we have an intersection between the Sprite and
// one or more cells of the tiledlayer
// note sx1 < sx2, tLx1 < tLx2, sx2 > tLx1 from intersectRect()
// use <= for comparison as this saves us some
// computation - the result will be 0
startCol = (sx1 <= tLx1) ? 0 : (sx1 - tLx1) / tW;
startRow = (sy1 <= tLy1) ? 0 : (sy1 - tLy1) / tH;
// since tLx1 < sx2 < tLx2, the computation will yield
// a result between 0 and tNumCols - 1
// subtract by 1 because sx2,sy2 represent
// the enclosing bounds of the sprite, not the
// locations in the coordinate system.
endCol = (sx2 < tLx2) ? ((sx2 - 1 - tLx1) / tW) : tNumCols - 1;
endRow = (sy2 < tLy2) ? ((sy2 - 1 - tLy1) / tH) : tNumRows - 1;
// current cell coordinates
int cellTop = startRow * tH + tLy1;
int cellBottom = cellTop + tH;
// the index of the current tile.
int tileIndex; // = 0;
for (int row = startRow; row <= endRow; row++, cellTop += tH, cellBottom += tH) {
// current cell coordinates
int cellLeft = startCol * tW + tLx1;
int cellRight = cellLeft + tW;
for (int col = startCol; col <= endCol; col++, cellLeft += tW, cellRight += tW) {
tileIndex = t.getCell(col, row);
if (tileIndex != 0) {
// current cell/sprite intersection coordinates
// in painter coordinate system.
// find intersecting region,
int intersectLeft = (sx1 < cellLeft) ? cellLeft : sx1;
int intersectTop = (sy1 < cellTop) ? cellTop : sy1;
// used once, optimize.
int intersectRight = (sx2 < cellRight) ? sx2
: cellRight;
int intersectBottom = (sy2 < cellBottom) ? sy2
: cellBottom;
if (intersectLeft > intersectRight) {
int temp = intersectRight;
intersectRight = intersectLeft;
intersectLeft = temp;
}
if (intersectTop > intersectBottom) {
int temp = intersectBottom;
intersectBottom = intersectTop;
intersectTop = temp;
}
int intersectWidth = intersectRight - intersectLeft;
int intersectHeight = intersectBottom - intersectTop;
int image1XOffset = getImageTopLeftX(intersectLeft,
intersectTop, intersectRight, intersectBottom);
int image1YOffset = getImageTopLeftY(intersectLeft,
intersectTop, intersectRight, intersectBottom);
int image2XOffset = t.tileSetX[tileIndex]
+ (intersectLeft - cellLeft);
int image2YOffset = t.tileSetY[tileIndex]
+ (intersectTop - cellTop);
if (doPixelCollision(image1XOffset, image1YOffset,
image2XOffset, image2YOffset, this.sourceImage,
this.t_currentTransformation, t.sourceImage,
TRANS_NONE, intersectWidth, intersectHeight)) {
// intersection found with this tile
return true;
}
}
} // end of for col
}// end of for row
// worst case! we scanned through entire
// overlapping region and
// no pixels collide!
return false;
}
}
/**
* Checks for a collision between this Sprite and the specified Image with
* its upper left corner at the specified location. If pixel-level detection
* is used, a collision is detected only if opaque pixels collide. That is,
* an opaque pixel in the Sprite would have to collide with an opaque pixel
* in Image for a collision to be detected. Only those pixels within the
* Sprite's collision rectangle are checked.
* <P>
* If pixel-level detection is not used, this method simply checks if the
* Sprite's collision rectangle intersects with the Image's bounds.
* <P>
* Any transform applied to the Sprite is automatically accounted for.
* <P>
* The Sprite must be visible in order for a collision to be detected.
* <P>
*
* @param image
* the <code>Image</code> to test for collision
* @param x
* the horizontal location of the <code>Image</code>'s upper left
* corner
* @param y
* the vertical location of the <code>Image</code>'s upper left
* corner
* @param pixelLevel
* <code>true</code> to test for collision on a pixel-by-pixel
* basis, <code>false</code> to test using simple bounds checking
* @return <code>true</code> if this <code>Sprite</code> has collided with
* the <code>Image</code>, otherwise <code>false</code>
* @throws NullPointerException
* if <code>image</code> is <code>null</code>
*/
public final boolean collidesWith(Bitmap image, int x, int y,
boolean pixelLevel) {
// check if this Sprite is not visible
if (!(this.visible)) {
return false;
}
// if image is null
// image.getWidth() will throw NullPointerException
int otherLeft = x;
int otherTop = y;
int otherRight = x + image.getWidth();
int otherBottom = y + image.getHeight();
int left = this.x + this.t_collisionRectX;
int top = this.y + this.t_collisionRectY;
int right = left + this.t_collisionRectWidth;
int bottom = top + this.t_collisionRectHeight;
// first check if the collision rectangles of the two sprites intersect
if (intersectRect(otherLeft, otherTop, otherRight, otherBottom, left,
top, right, bottom)) {
// collision rectangles intersect
if (pixelLevel) {
// find intersecting region,
// we need to check pixel level collision detection.
// use only the coordinates within the Sprite frame if
// the collision rectangle is larger than the Sprite
// frame
if (this.t_collisionRectX < 0) {
left = this.x;
}
if (this.t_collisionRectY < 0) {
top = this.y;
}
if ((this.t_collisionRectX + this.t_collisionRectWidth) > this.width) {
right = this.x + this.width;
}
if ((this.t_collisionRectY + this.t_collisionRectHeight) > this.height) {
bottom = this.y + this.height;
}
// recheck if the updated collision area rectangles intersect
if (!intersectRect(otherLeft, otherTop, otherRight,
otherBottom, left, top, right, bottom)) {
// if they don't intersect, return false;
return false;
}
// within the collision rectangles
int intersectLeft = (left < otherLeft) ? otherLeft : left;
int intersectTop = (top < otherTop) ? otherTop : top;
// used once, optimize.
int intersectRight = (right < otherRight) ? right : otherRight;
int intersectBottom = (bottom < otherBottom) ? bottom
: otherBottom;
int intersectWidth = Math.abs(intersectRight - intersectLeft);
int intersectHeight = Math.abs(intersectBottom - intersectTop);
// have the coordinates in painter space,
// need coordinates of top left and width, height
// in source image of Sprite.
int thisImageXOffset = getImageTopLeftX(intersectLeft,
intersectTop, intersectRight, intersectBottom);
int thisImageYOffset = getImageTopLeftY(intersectLeft,
intersectTop, intersectRight, intersectBottom);
int otherImageXOffset = intersectLeft - x;
int otherImageYOffset = intersectTop - y;
// check if opaque pixels intersect.
return doPixelCollision(thisImageXOffset, thisImageYOffset,
otherImageXOffset, otherImageYOffset, this.sourceImage,
this.t_currentTransformation, image, Sprite.TRANS_NONE,
intersectWidth, intersectHeight);
} else {
// collides!
return true;
}
}
return false;
}
// -----
// ----- private -----
/**
* create the Image Array.
*
* @param image
* Image to use for Sprite
* @param fWidth
* width, in pixels, of the individual raw frames
* @param fHeight
* height, in pixels, of the individual raw frames
* @param maintainCurFrame
* true if Current Frame is maintained
*/
private void initializeFrames(Bitmap image, int fWidth, int fHeight,
boolean maintainCurFrame) {
int imageW = image.getWidth();
int imageH = image.getHeight();
int numHorizontalFrames = imageW / fWidth;
int numVerticalFrames = imageH / fHeight;
sourceImage = image;
srcFrameWidth = fWidth;
srcFrameHeight = fHeight;
numberFrames = numHorizontalFrames * numVerticalFrames;
frameCoordsX = new int[numberFrames];
frameCoordsY = new int[numberFrames];
if (!maintainCurFrame) {
sequenceIndex = 0;
}
if (!customSequenceDefined) {
frameSequence = new int[numberFrames];
}
int currentFrame = 0;
for (int yy = 0; yy < imageH; yy += fHeight) {
for (int xx = 0; xx < imageW; xx += fWidth) {
frameCoordsX[currentFrame] = xx;
frameCoordsY[currentFrame] = yy;
if (!customSequenceDefined) {
frameSequence[currentFrame] = currentFrame;
}
currentFrame++;
}
}
}
/**
* initialize the collision rectangle
*/
private void initCollisionRectBounds() {
// reset x and y of collision rectangle
collisionRectX = 0;
collisionRectY = 0;
// intialize the collision rectangle bounds to that of the sprite
collisionRectWidth = this.width;
collisionRectHeight = this.height;
}
/**
* Detect rectangle intersection
*
* @param r1x1
* left co-ordinate of first rectangle
* @param r1y1
* top co-ordinate of first rectangle
* @param r1x2
* right co-ordinate of first rectangle
* @param r1y2
* bottom co-ordinate of first rectangle
* @param r2x1
* left co-ordinate of second rectangle
* @param r2y1
* top co-ordinate of second rectangle
* @param r2x2
* right co-ordinate of second rectangle
* @param r2y2
* bottom co-ordinate of second rectangle
* @return True if there is rectangle intersection
*/
private boolean intersectRect(int r1x1, int r1y1, int r1x2, int r1y2,
int r2x1, int r2y1, int r2x2, int r2y2) {
if (r2x1 >= r1x2 || r2y1 >= r1y2 || r2x2 <= r1x1 || r2y2 <= r1y1) {
return false;
} else {
return true;
}
}
/**
* Detect opaque pixel intersection between regions of two images
*
* @param image1XOffset
* left coordinate in the first image
* @param image1YOffset
* top coordinate in the first image
* @param image2XOffset
* left coordinate in the second image
* @param image2YOffset
* top coordinate in the second image
* @param image1
* first source image
* @param transform1
* The transform for the first image
* @param image2
* second source image
* @param transform2
* transform set on the second image
* @param width
* width of overlapping region, when transformed
* @param height
* height of overlapping region, when transformed
*
* Clarification required on parameters: XOffset and YOffset are
* the offsets from the top left hand corner of the image. width,
* height is the dimensions of the intersecting regions in the
* two transformed images. there fore appropriate conversions
* have to be made on these dimensions when using the values,
* according to the transformation that has been set.
*
* @return True if there is a pixel level collision
*/
private static boolean doPixelCollision(int image1XOffset,
int image1YOffset, int image2XOffset, int image2YOffset,
Bitmap image1, int transform1, Bitmap image2, int transform2,
int width, int height) {
// starting point of comparison
int startY1;
// x and y increments
int xIncr1, yIncr1;
// .. for image 2
int startY2;
int xIncr2, yIncr2;
int numPixels = height * width;
int[] argbData1 = new int[numPixels];
int[] argbData2 = new int[numPixels];
if (0x0 != (transform1 & INVERTED_AXES)) {
// inverted axes
// scanlength = height
if (0x0 != (transform1 & Y_FLIP)) {
xIncr1 = -(height); // - scanlength
startY1 = numPixels - height; // numPixels - scanlength
} else {
xIncr1 = height; // + scanlength
startY1 = 0;
}
if (0x0 != (transform1 & X_FLIP)) {
yIncr1 = -1;
startY1 += (height - 1);
} else {
yIncr1 = +1;
}
image1.getPixels(argbData1, 0, height, // scanlength = height
image1XOffset, image1YOffset, height, width);
} else {
// scanlength = width
if (0x0 != (transform1 & Y_FLIP)) {
startY1 = numPixels - width; // numPixels - scanlength
yIncr1 = -(width); // - scanlength
} else {
startY1 = 0;
yIncr1 = width; // + scanlength
}
if (0x0 != (transform1 & X_FLIP)) {
xIncr1 = -1;
startY1 += (width - 1);
} else {
xIncr1 = +1;
}
image1.getPixels(argbData1, 0, width, // scanlength = width
image1XOffset, image1YOffset, width, height);
}
if (0x0 != (transform2 & INVERTED_AXES)) {
// inverted axes
if (0x0 != (transform2 & Y_FLIP)) {
xIncr2 = -(height);
startY2 = numPixels - height;
} else {
xIncr2 = height;
startY2 = 0;
}
if (0x0 != (transform2 & X_FLIP)) {
yIncr2 = -1;
startY2 += height - 1;
} else {
yIncr2 = +1;
}
image2.getPixels(argbData2, 0, height, image2XOffset,
image2YOffset, height, width);
} else {
if (0x0 != (transform2 & Y_FLIP)) {
startY2 = numPixels - width;
yIncr2 = -(width);
} else {
startY2 = 0;
yIncr2 = +width;
}
if (0x0 != (transform2 & X_FLIP)) {
xIncr2 = -1;
startY2 += (width - 1);
} else {
xIncr2 = +1;
}
image2.getPixels(argbData2, 0, width, image2XOffset, image2YOffset,
width, height);
}
int x1, x2;
int xLocalBegin1, xLocalBegin2;
// the loop counters
int numIterRows;
int numIterColumns;
for (numIterRows = 0, xLocalBegin1 = startY1, xLocalBegin2 = startY2; numIterRows < height; xLocalBegin1 += yIncr1, xLocalBegin2 += yIncr2, numIterRows++) {
for (numIterColumns = 0, x1 = xLocalBegin1, x2 = xLocalBegin2; numIterColumns < width; x1 += xIncr1, x2 += xIncr2, numIterColumns++) {
if (((argbData1[x1] & ALPHA_BITMASK) != 0)
&& ((argbData2[x2] & ALPHA_BITMASK) != 0)) {
return true;
}
} // end for x
} // end for y
// worst case! couldn't find a single colliding pixel!
return false;
}
/**
* Given a rectangle that lies within the sprite in the painter's
* coordinates, find the X coordinate of the top left corner in the source
* image of the sprite
*
* @param x1
* the x coordinate of the top left of the rectangle
* @param y1
* the y coordinate of the top left of the rectangle
* @param x2
* the x coordinate of the bottom right of the rectangle
* @param y2
* the y coordinate of the bottom right of the rectangle
*
* @return the X coordinate in the source image
*
*/
private int getImageTopLeftX(int x1, int y1, int x2, int y2) {
int retX = 0;
// left = this.x
// right = this.x + this.width
// top = this.y
// bottom = this.y + this.height
switch (this.t_currentTransformation) {
case TRANS_NONE:
case TRANS_MIRROR_ROT180:
retX = x1 - this.x;
break;
case TRANS_MIRROR:
case TRANS_ROT180:
retX = (this.x + this.width) - x2;
break;
case TRANS_ROT90:
case TRANS_MIRROR_ROT270:
retX = y1 - this.y;
break;
case TRANS_ROT270:
case TRANS_MIRROR_ROT90:
retX = (this.y + this.height) - y2;
break;
}
retX += frameCoordsX[frameSequence[sequenceIndex]];
return retX;
}
/**
* Given a rectangle that lies within the sprite in the painter's
* coordinates, find the Y coordinate of the top left corner in the source
* image of the sprite
*
* @param x1
* the x coordinate of the top left of the rectangle
* @param y1
* the y coordinate of the top left of the rectangle
* @param x2
* the x coordinate of the bottom right of the rectangle
* @param y2
* the y coordinate of the bottom right of the rectangle
*
* @return the Y coordinate in the source image
*
*/
private int getImageTopLeftY(int x1, int y1, int x2, int y2) {
int retY = 0;
// left = this.x
// right = this.x + this.width
// top = this.y
// bottom = this.y + this.height
switch (this.t_currentTransformation) {
case TRANS_NONE:
case TRANS_MIRROR:
retY = y1 - this.y;
break;
case TRANS_ROT180:
case TRANS_MIRROR_ROT180:
retY = (this.y + this.height) - y2;
break;
case TRANS_ROT270:
case TRANS_MIRROR_ROT270:
retY = x1 - this.x;
break;
case TRANS_ROT90:
case TRANS_MIRROR_ROT90:
retY = (this.x + this.width) - x2;
break;
}
retY += frameCoordsY[frameSequence[sequenceIndex]];
return retY;
}
/**
* Sets the transform for this Sprite
*
* @param transform
* the desired transform for this Sprite
*/
private void setTransformImpl(int transform) {
// ---
// setTransform sets up all transformation related data structures
// except transforming the current frame's bitmap.
// x, y, width, height, dRefX, dRefY,
// collisionRectX, collisionRectY, collisionRectWidth,
// collisionRectHeight, t_currentTransformation,
// t_bufferImage
// The actual tranformed frame is drawn at paint time.
// ---
// update top-left corner position
this.x = this.x
+ getTransformedPtX(dRefX, dRefY, this.t_currentTransformation)
- getTransformedPtX(dRefX, dRefY, transform);
this.y = this.y
+ getTransformedPtY(dRefX, dRefY, this.t_currentTransformation)
- getTransformedPtY(dRefX, dRefY, transform);
// Calculate transformed sprites collision rectangle
// and transformed width and height
computeTransformedBounds(transform);
// set the current transform to be the one requested
t_currentTransformation = transform;
}
/**
* Calculate transformed sprites collision rectangle and transformed width
* and height
*
* @param transform
* the desired transform for this <code>Sprite</code>
*/
private void computeTransformedBounds(int transform) {
switch (transform) {
case TRANS_NONE:
t_collisionRectX = collisionRectX;
t_collisionRectY = collisionRectY;
t_collisionRectWidth = collisionRectWidth;
t_collisionRectHeight = collisionRectHeight;
this.width = srcFrameWidth;
this.height = srcFrameHeight;
break;
case TRANS_MIRROR:
// flip across vertical
// NOTE: top left x and y coordinate must reflect the transformation
// performed around the reference point
// the X-offset of the reference point from the top left corner
// changes.
t_collisionRectX = srcFrameWidth
- (collisionRectX + collisionRectWidth);
t_collisionRectY = collisionRectY;
t_collisionRectWidth = collisionRectWidth;
t_collisionRectHeight = collisionRectHeight;
// the Y-offset of the reference point from the top left corner
// remains the same,
// top left X-co-ordinate changes
this.width = srcFrameWidth;
this.height = srcFrameHeight;
break;
case TRANS_MIRROR_ROT180:
// flip across horizontal
// NOTE: top left x and y coordinate must reflect the transformation
// performed around the reference point
// the Y-offset of the reference point from the top left corner
// changes
t_collisionRectY = srcFrameHeight
- (collisionRectY + collisionRectHeight);
t_collisionRectX = collisionRectX;
t_collisionRectWidth = collisionRectWidth;
t_collisionRectHeight = collisionRectHeight;
// width and height are as before
this.width = srcFrameWidth;
this.height = srcFrameHeight;
// the X-offset of the reference point from the top left corner
// remains the same.
// top left Y-co-ordinate changes
break;
case TRANS_ROT90:
// NOTE: top left x and y coordinate must reflect the transformation
// performed around the reference point
// the bottom-left corner of the rectangle becomes the
// top-left when rotated 90.
// both X- and Y-offset to the top left corner may change
// update the position information for the collision rectangle
t_collisionRectX = srcFrameHeight
- (collisionRectHeight + collisionRectY);
t_collisionRectY = collisionRectX;
t_collisionRectHeight = collisionRectWidth;
t_collisionRectWidth = collisionRectHeight;
// set width and height
this.width = srcFrameHeight;
this.height = srcFrameWidth;
break;
case TRANS_ROT180:
// NOTE: top left x and y coordinate must reflect the transformation
// performed around the reference point
// width and height are as before
// both X- and Y- offsets from the top left corner may change
t_collisionRectX = srcFrameWidth
- (collisionRectWidth + collisionRectX);
t_collisionRectY = srcFrameHeight
- (collisionRectHeight + collisionRectY);
t_collisionRectWidth = collisionRectWidth;
t_collisionRectHeight = collisionRectHeight;
// set width and height
this.width = srcFrameWidth;
this.height = srcFrameHeight;
break;
case TRANS_ROT270:
// the top-right corner of the rectangle becomes the
// top-left when rotated 270.
// both X- and Y-offset to the top left corner may change
// update the position information for the collision rectangle
t_collisionRectX = collisionRectY;
t_collisionRectY = srcFrameWidth
- (collisionRectWidth + collisionRectX);
t_collisionRectHeight = collisionRectWidth;
t_collisionRectWidth = collisionRectHeight;
// set width and height
this.width = srcFrameHeight;
this.height = srcFrameWidth;
break;
case TRANS_MIRROR_ROT90:
// both X- and Y- offset from the top left corner may change
// update the position information for the collision rectangle
t_collisionRectX = srcFrameHeight
- (collisionRectHeight + collisionRectY);
t_collisionRectY = srcFrameWidth
- (collisionRectWidth + collisionRectX);
t_collisionRectHeight = collisionRectWidth;
t_collisionRectWidth = collisionRectHeight;
// set width and height
this.width = srcFrameHeight;
this.height = srcFrameWidth;
break;
case TRANS_MIRROR_ROT270:
// both X- and Y- offset from the top left corner may change
// update the position information for the collision rectangle
t_collisionRectY = collisionRectX;
t_collisionRectX = collisionRectY;
t_collisionRectHeight = collisionRectWidth;
t_collisionRectWidth = collisionRectHeight;
// set width and height
this.width = srcFrameHeight;
this.height = srcFrameWidth;
break;
default:
// INVALID TRANSFORMATION!
throw new IllegalArgumentException();
}
}
/**
* Given the x and y offsets off a pixel from the top left corner, in an
* untransformed sprite, calculates the x coordinate of the pixel when the
* same sprite is transformed, with the coordinates of the top-left pixel of
* the transformed sprite as (0,0).
*
* @param x
* Horizontal offset within the untransformed sprite
* @param y
* Vertical offset within the untransformed sprite
* @param transform
* transform for the sprite
* @return The x-offset, of the coordinates of the pixel, with the top-left
* corner as 0 when transformed.
*/
int getTransformedPtX(int x, int y, int transform) {
int t_x = 0;
switch (transform) {
case TRANS_NONE:
t_x = x;
break;
case TRANS_MIRROR:
t_x = srcFrameWidth - x - 1;
break;
case TRANS_MIRROR_ROT180:
t_x = x;
break;
case TRANS_ROT90:
t_x = srcFrameHeight - y - 1;
break;
case TRANS_ROT180:
t_x = srcFrameWidth - x - 1;
break;
case TRANS_ROT270:
t_x = y;
break;
case TRANS_MIRROR_ROT90:
t_x = srcFrameHeight - y - 1;
break;
case TRANS_MIRROR_ROT270:
t_x = y;
break;
default:
// INVALID TRANSFORMATION!
throw new IllegalArgumentException();
}
return t_x;
}
/**
* Given the x and y offsets off a pixel from the top left corner, in an
* untransformed sprite, calculates the y coordinate of the pixel when the
* same sprite is transformed, with the coordinates of the top-left pixel of
* the transformed sprite as (0,0).
*
* @param x
* Horizontal offset within the untransformed sprite
* @param y
* Vertical offset within the untransformed sprite
* @param transform
* transform for the sprite
* @return The y-offset, of the coordinates of the pixel, with the top-left
* corner as 0 when transformed.
*/
int getTransformedPtY(int x, int y, int transform) {
int t_y = 0;
switch (transform) {
case TRANS_NONE:
t_y = y;
break;
case TRANS_MIRROR:
t_y = y;
break;
case TRANS_MIRROR_ROT180:
t_y = srcFrameHeight - y - 1;
break;
case TRANS_ROT90:
t_y = x;
break;
case TRANS_ROT180:
t_y = srcFrameHeight - y - 1;
break;
case TRANS_ROT270:
t_y = srcFrameWidth - x - 1;
break;
case TRANS_MIRROR_ROT90:
t_y = srcFrameWidth - x - 1;
break;
case TRANS_MIRROR_ROT270:
t_y = x;
break;
default:
// INVALID TRANSFORMATION!
throw new IllegalArgumentException();
}
return t_y;
}
}