package com.tkraindesigns.gameutility; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PointF; import android.graphics.RectF; /** * This is the base class for all objects that can exist within the game universe. Paddle, bullet, enemy, etc should all * descend from Actor * Created by Brian on 7/6/2015. */ public class Actor { /** * Center X of object */ protected float centerX; /** * Center Y of object */ protected float centerY; /** * Width of object */ protected float width; /** * Height of object */ protected float height; /** * Width of screen */ protected float screenX; /** * Height of screen */ protected float screenY; /** * X component vector (pixels/second) */ protected float vectorX; /** * Y component vector (pixels/second) */ protected float vectorY; /** * Last frame's Y position * Used to backtrack object, to just outside the actual collision boundary */ protected float lastY; /** * Last frame's X position * Used to backtrack object, to just outside the actual collision boundary */ protected float lastX; /** * No Bitmap, BitmapBuffer, or Spritesheet available */ final public int NOTDEFINED=-1; /** * A {@link Bitmap} has been assigned. {@link Actor#draw(Canvas, Paint)} will use that bitmap to draw * the actor. */ final public int BITMAP=0; /** * A {@link BitmapBuffer} has been assigned. {@link Actor#draw(Canvas, Paint)} will use that * BitmapBuffer to draw the actor. */ final public int BITMAPBUFFER=1; /** * A {@link Spritesheet} has been assigned. {@link Actor#draw(Canvas, Paint)} will use that * Spritesheet to draw the actor */ final public int SPRITESHEET=2; /** * {@link BitmapBuffer} assigned to this Actor (if any) */ protected BitmapBuffer bitmapBuffer; /** * {@link Spritesheet} assigned to this Actor (if any) */ protected Spritesheet spriteSheet; /** * {@link Bitmap} assigned to this Actor (if any) */ protected Bitmap bitmap; /** * Current frame, used by {@link Actor#draw(Canvas, Paint)} to determine which frame to use * when drawing. This should always be set using {@link Actor#setFrame(int)} to avoid illegal * bounds errors. */ protected int frame; /** * Set by {@link Actor#setBitmap(Bitmap)}, {@link Actor#setBitmapBuffer(BitmapBuffer)}, or * {@link Actor#setSpriteSheet(Spritesheet)}. Should not be set otherwise under most circumstances. * Used by {@link Actor#draw(Canvas, Paint)} to determine how to draw the actor */ protected int imagetype =NOTDEFINED; /** * BARE minimum to create an Actor, you must know screenX, screenY * @param screenx Screen Resolution * @param screeny Screen Resolution */ @SuppressWarnings("unused") public Actor(float screenx,float screeny) { super(); centerX = 0; centerY = 0; width = 0; height = 0; screenX = screenx; screenY = screeny; lastX=centerX; lastY=centerY; vectorX=0; vectorY=0; } /** * Actor at specified position. * @param centerx Center position * @param centery Center position * @param Width Width of actor * @param Height Height of actor * @param screenx Screen resolution * @param screeny Screen resolution */ public Actor (float centerx,float centery, float Width, float Height, float screenx, float screeny) { super(); centerX = centerx; centerY = centery; width = Width; height = Height; screenX = screenx; screenY = screeny; lastX=centerX; lastY=centerY; vectorX=0; vectorY=0; } /** * Creates an actor in motion * @param centerx Center position * @param centery Center position * @param Width Width of actor * @param Height Height of actor * @param screenx Screen resolution * @param screeny Screen resolution * @param vectorx X component of speed * @param vectory Y component of speed */ @SuppressWarnings("unused") public Actor(float centerx,float centery,float Width, float Height, float screenx, float screeny, float vectorx,float vectory) { super(); centerX = centerx; centerY = centery; width = Width; height = Height; screenX = screenx; screenY = screeny; lastX=centerX; lastY=centerY; vectorX=vectorx; vectorY=vectory; } /** * Used in Update calls... First set VectorX and VectorY (or leave unchanged, of course) * Then in actor subclass update method, call MoveActor(fps) * This will let the UncollideActor function work properly as well as maintain a consistent * codebase for actors and movement. * @param fps Frames Per Second */ @SuppressWarnings("unused") public void MoveActor(long fps) { lastX=centerX; lastY=centerY; centerX+=vectorX/(float)fps; centerY+=vectorY/(float)fps; } /** * Used after a detected collision... moves this actor outside of the offending colliding actor * This function simply calls EdgeRectF(actor.getRectF()); * @param actor The other actor, who we want to get uncrossed with */ @SuppressWarnings("unused") public void UncollideActor(Actor actor) { UncollideRectF(actor.getRect()); } /** * Back away from left edge of screen */ @SuppressWarnings("unused") public void UncollideLeft() { UncollideRectF(new RectF(-100,0,0,screenY)); } /** * Back away from right edge of screen */ @SuppressWarnings("unused") public void UncollideRight() { UncollideRectF(new RectF(screenX,0,screenX+100,screenY)); } /** * Back away from top of screen */ @SuppressWarnings("unused") public void UncollideTop() { UncollideRectF(new RectF(0,-100,screenX,0)); } /** * Back away from bottom of screen */ @SuppressWarnings("unused") public void UncollideBottom() { UncollideRectF(new RectF(0,screenY,screenX,screenY+100)); } /** * pick up a bit of the speed of the object you hit * @param other Other object to get some speed from */ @SuppressWarnings("unused") public void absorbX(Actor other) { float otherX=other.getVectorX(); otherX=otherX/2f; vectorX+=otherX; } /** * pick up a bit of the speed of the object you hit * @param other Other object to get some speed from */ @SuppressWarnings("unused") public void absorbY(Actor other) { vectorY+=other.vectorY/2; } private float sign(float f) { if (f<0) { return -1; } else if (f>0) { return 1; } else { return 0; } } /** *

This only works if you've been using MoveActor and setting VectorX/VectorY properly.

*

The logic here is that once the program's update phase has detected a collision with a rect, * then it wants to move this object back to just the point where the two rectangles "collide" * Sort of a clearObstacle type function. By handling both the X and Y component at the same time * this should avoid some of the jerkier motion effects.

* @param rect The RectF to avoid. */ @SuppressWarnings("unused") public void UncollideRectF(RectF rect) { float vx; float vy; if (vectorX==0) { //Test case for 0/180 angles, to avoid division by zero vx=0; vy=sign(vectorY); } else { //Test case for 90/270 angles, to avoid division by zero if (vectorY==0) { vx=sign(vectorX); vy=0; } else { //This sets the vector logic... greatest vector becomes 1, smallest less than if (vectorY>vectorX) { vx=vectorX/vectorY; vy=1; } else { vx=1; vy=vectorY/vectorX; } } } //Now we can begin subtracting (backing away) until the object is cleared while (collision(rect)) { centerX-=vx; centerY-=vy; } } /** * The bounding rectangle for the object. Useful for collision detection and drawing * @return RectF */ @SuppressWarnings("unused") public RectF getRect() { return new RectF(centerX-width/2,centerY-height/2,centerX+width/2,centerY+height/2); } /** * The bounding rectangle for drawing the object. Override this in cases where the rectangle * is non-standard * @return RectF */ @SuppressWarnings("unused") public RectF getDrawRect() { return new RectF(centerX-width/2,centerY-height/2,centerX+width/2,centerY+height/2); } /** * Convenience function, determine if a given point is within the bounding rectangle. * Equivilant to getRect().contains(x,y) * @param x Coordinate * @param y Coordinate * @return True if x,y is within rectangle */ @SuppressWarnings("unused") public boolean rectContains(int x, int y) { return getRect().contains(x, y); } /** * Convenience function, determine if a given point is within the bounding rectangle. * Equivilant to getRect().contains(x,y) * @param x Coordinate * @param y Coordinate * @return True if x,y is within rectangle */ @SuppressWarnings("unused") public boolean rectContains(float x, float y) { return getRect().contains(x, y); } /** * Convenience function, determine if a given point is within the bounding rectangle * Uses a PointF and breaks it out for the contains(x,y) function * @param point Location to test * @return True if within the bounding rectangle */ @SuppressWarnings("unused") public boolean rectContains(PointF point) { return getRect().contains(point.x, point.y); } /** * Useage: if (collision(rect)) * * @param rect rectangle of object to check * @return true if intersection */ @SuppressWarnings("unused") public boolean collision(RectF rect) { return getRect().intersect(rect); } /** * Useage: if (collision(actor)) * Checks to see if the two actors collide * @param actor Actor to check collission. * @return true if collision */ @SuppressWarnings("unused") public boolean collision(Actor actor) { return getRect().intersect(actor.getRect()); } /** * This is the logic: If the sphere intersects our rectangle, then the difference between * the CenterX of the sphere and the rectangle will be less than the radius and 1/2 the width. * The CenterY will also be similarly situated. * @param x Coordinates of sphere * @param y Coordinates of sphere * @param radius radius of sphere * @return True if intersects */ public boolean collisionSphere(float x,float y, float radius) { float deltaX=Math.abs(x-centerX); float deltaY=Math.abs(y-centerY); return deltaX Math.pow(centerY-y,2)+Math.pow(centerX-x,2); } /** * This function calls collisionSphereSphere against actor, presuming both this actor * is a sphere and target actor is a sphere * @param actor Actor to check against * @return True if collision occurred */ @SuppressWarnings("unused") public boolean collisionSphereSphere(Actor actor) { return collisionSphereSphere(actor.getCenterX(), actor.getCenterY(), actor.getWidth() / 2); } /** * Returns true if any portion of the object is on the screen * @return True if on screen */ @SuppressWarnings("unused") public boolean onScreen() { return getRect().intersect(0, 0, screenX, screenY); } /** * Difference in centerX between this object and actor * @param actor Other actor * @return the distance */ @SuppressWarnings("unused") public float distanceX(Actor actor) { return centerX-actor.getCenterX(); } /** * Difference in centerX between this object and actor * @param actor Other actor * @return the distance */ @SuppressWarnings("unused") public float distanceY(Actor actor) { return centerY-actor.getCenterY(); } /** * @return centerX */ @SuppressWarnings("unused") public float getCenterX() { return centerX; } /** * Set object position. * @param x Position to set * @param y Position to set */ @SuppressWarnings("unused") public void setPosition(float x, float y) { centerX=x; centerY=y; } /** * Set object position * @param x Position to set * @param y Position to set */ @SuppressWarnings("unused") public void setPosition(int x, int y) { centerX=x; centerY=y; } /** * Set position using a PointF * @param point PointF location */ @SuppressWarnings("unused") public void setPosition(PointF point) { centerX=point.x; centerY=point.y; } /** * PointF of centerX,CenterY * @return new PointF(centerX,CenterY) */ @SuppressWarnings("unused") public PointF getPosition() { return new PointF(centerX,centerY); } /** * Preferred to use setPosition(x,y); * @param centerX X position to set */ @SuppressWarnings("unused") public void setCenterX(float centerX) { this.centerX = centerX; } /** * @return centerY */ @SuppressWarnings("unused") public float getCenterY() { return centerY; } /** * Preferred to use setPosition(x,y); * @param centerY Y position to set */ @SuppressWarnings("unused") public void setCenterY(float centerY) { this.centerY = centerY; } /** * @return Width of object */ @SuppressWarnings("unused") public float getWidth() { return width; } /** * Sets width of object * @param width desired width */ @SuppressWarnings("unused") public void setWidth(float width) { this.width = width; } /** * @return Height of object */ @SuppressWarnings("unused") public float getHeight() { return height; } /** * set Height of object * @param height desired height */ @SuppressWarnings("unused") public void setHeight(float height) { this.height = height; } /** * @return Width of screen */ @SuppressWarnings("unused") public float getScreenX() { return screenX; } /** * @param screenX Width of screen */ @SuppressWarnings("unused") public void setScreenX(float screenX) { this.screenX = screenX; } /** * @return Height of screen */ @SuppressWarnings("unused") public float getScreenY() { return screenY; } /** * @param screenY Height of screen */ @SuppressWarnings("unused") public void setScreenY(float screenY) { this.screenY = screenY; } /** * Returns speed in pixels/second X Component * @return pixels/second X */ @SuppressWarnings("unused") public float getVectorX() { return vectorX; } /** * @param vectorX Pixels/second X Component */ @SuppressWarnings("unused") public void setVectorX(float vectorX) { this.vectorX = vectorX; } /** * Returns speed in pixels/second Y Component * @return Pixels/second Y component */ @SuppressWarnings("unused") public float getVectorY() { return vectorY; } /** * @param vectorY Pixels/second Y Component */ @SuppressWarnings("unused") public void setVectorY(float vectorY) { this.vectorY = vectorY; } /** * Draws the object's bounding box on the specified canvas * @param canvas Canvas to draw to * @param paint Paint to use */ @SuppressWarnings("unused") public void drawBoundingBox(Canvas canvas,Paint paint) { canvas.drawRect(getRect(), paint); } /** * Draws the object using specified bitmap * @param canvas Canvas to draw to * @param bitmap Bitmap to use * @param paint Paint to use */ @SuppressWarnings("unused") public void draw(Canvas canvas,Bitmap bitmap, Paint paint) { canvas.drawBitmap(bitmap, null, getDrawRect(), paint); } /** * Draws the object using the default bitmap, whether from a BitmapBuffer, Spritesheet or a bitmap * as set with setBitmap, setBitmapBuffer, or setSpritesheet. Frame is set in advance using setFrame * @param canvas Canvas to draw on * @param paint Pain to use */ @SuppressWarnings("unused") public void draw(Canvas canvas, Paint paint) { switch (imagetype) { case BITMAP: draw(canvas,bitmap,paint) ;break; case BITMAPBUFFER: draw(canvas,bitmapBuffer.get(frame),paint);break; case SPRITESHEET: draw(canvas,spriteSheet.getFrame(frame),paint);break; } } @SuppressWarnings("unused") public BitmapBuffer getBitmapBuffer() { return bitmapBuffer; } @SuppressWarnings("unused") public void setBitmapBuffer(BitmapBuffer bitmapBuffer) { this.bitmapBuffer = bitmapBuffer; imagetype =BITMAPBUFFER; } @SuppressWarnings("unused") public Spritesheet getSpriteSheet() { return spriteSheet; } @SuppressWarnings("unused") public void setSpriteSheet(Spritesheet spriteSheet) { this.spriteSheet = spriteSheet; imagetype =SPRITESHEET; } @SuppressWarnings("unused") public Bitmap getBitmap() { return bitmap; } @SuppressWarnings("unused") public void setBitmap(Bitmap bitmap) { this.bitmap = bitmap; imagetype =BITMAP; } @SuppressWarnings("unused") public int getFrame() { return frame; } /** * Sets the current {@link Actor#frame} Do not set frame directly, use this function. If frame * exceeds the actual number of frames available, it is wrapped back to zero. If frames is less * than zero, it is set to the last frame of the animation. If there is no {@link BitmapBuffer} * or {@link Spritesheet} set, then the actual frame is always set to zero. * @param newframe Desired frame. */ @SuppressWarnings("unused") public void setFrame(int newframe) { frame=newframe; switch (imagetype) { case BITMAPBUFFER: { if (frame>bitmapBuffer.size()-1) { frame=0; } else if (frame<0) { frame = bitmapBuffer.size()-1; } break; } case SPRITESHEET: { if (frame>spriteSheet.size()-1) { frame = 0; } else if (frame<0) { frame = spriteSheet.size()-1; } break; } default: { frame=0; //If there is no BitMapBuffer or Spritesheet, then Frame makes no sense so Zero it } } } @SuppressWarnings("unused") public int getImagetype() { return imagetype; } @SuppressWarnings("unused") public void setImagetype(int imagetype) { this.imagetype = imagetype; } /** * Last position since {@link Actor#MoveActor(long)} was called * @return Last Y coordinate */ @SuppressWarnings("unused") public float getLastY() { return lastY; } /** * Position before {@link Actor#MoveActor(long)} was called * @return Last X coordinate */ @SuppressWarnings("unused") public float getLastX() { return lastX; } /** * Returns the number of animation frames available in the object's bitmapBuffer or spritesheet * @return Number of frames. One if a bitmap or zero if no frames available. */ @SuppressWarnings("unused") public int numframes() { int result=0; switch (imagetype) { case BITMAP: result=1;break; case BITMAPBUFFER: result=bitmapBuffer.size();break; case SPRITESHEET: result=spriteSheet.size();break; } return result; } }