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