package com.jonathan.survivor.entity;
import com.esotericsoftware.spine.Skeleton;
import com.jonathan.survivor.math.Cell;
import com.jonathan.survivor.math.Collider;
import com.jonathan.survivor.math.Rectangle;
import com.jonathan.survivor.math.Vector2;
/*
* Represents an object with a collider in the world.
*/
public abstract class GameObject
{
/** Stores the bottom-bottom-center position of the GameObject */
private final Vector2 position;
/** Holds the GameObject's previous position before the current game tick. */
private final Vector2 previousPosition;
/** Stores the velocity of the gameObject the previous frame. */
private final Vector2 oldVelocity;
/** Stores the velocity of the GameObject */
private final Vector2 velocity;
/** Stores the acceleration of the GameObject */
private final Vector2 acceleration;
/** Stores the collider used by the GameObject for collision */
private Collider collider;
/** Stores the Spine skeleton that controls the bones of the GameObject and its appearance. */
private Skeleton skeleton;
/** Stores the row and column corresponding to the layer where the GameObject resides on the TerrainLevel. */
private Cell terrainCell;
/** The GameObject's id, used to identify GameObjects inside a save file. Used to identify whether or not a GameObject should be placed on a TerrainLayer.*/
private int objectId;
/** Stores the amount of time the GameObject has been in a specific state. (i.e., if the player has been jumping for 0.5 seconds, stateTime = 0.5). */
protected float stateTime;
/** Creates a GameObject with a bottom-bottom-center at (0,0) and a rectangle collider with width/height of zero. */
public GameObject()
{
this(0,0,0,0);
}
/** Creates a GameObject with bottom-center at (x,y) and rectangle collider with given width/height. */
public GameObject(float x, float y, float width, float height)
{
//Creates the Vector2s for the motion variables of the GameObject.
position = new Vector2(x, y);
previousPosition = new Vector2();
oldVelocity = new Vector2();
velocity = new Vector2();
acceleration = new Vector2();
//Creates a rectangle collider for the GameObject with the given width and height, positioned at the bottom-center of the GameObject.
collider = new Rectangle(x - width/2, y - width/2, width, height);
//Populates the terrainCell variable with a new Cell object for later user.
terrainCell = new Cell();
}
/** Updates the GameObject's game logic. */
public abstract void update(float deltaTime);
/** Updates the position of the GameObject according to its velocity and acceleration. */
public void updatePosition(float deltaTime)
{
//Store the old position before changing it
previousPosition.set(position.x, position.y);
//Store the GameObject's old velocity, before changing it
oldVelocity.set(velocity);
velocity.add(acceleration.mul(deltaTime));
position.add(oldVelocity.add(velocity).mul(0.5f).mul(deltaTime));
}
/** Snaps the GameObject's collider to the GameObject's position */
public void updateCollider()
{
//If the collider used by the GameObject is a rectangle
if(collider instanceof Rectangle)
//Place the lower left position of the collider at the right point. Note that a rectangle's position is its bottom-left corner.
collider.getPosition().set(position.x - ((Rectangle)collider).getWidth()/2, position.y);
}
/** Returns true if the GameObject can be targetted by a Human. */
public abstract boolean canTarget();
/** Retrieves the bottom-center position of the gameObject as a Vector2. Operations can be performed on the Vector2 using its
* instance methods, as it is mutable. */
public Vector2 getPosition() {
return position;
}
/** Sets the bottom-center position of the GameObject at the desired (x,y) coordinates. */
public void setPosition(float x, float y)
{
//Store the old position before changing it
previousPosition.set(position.x, position.y);
//Change the current position of the GameObject.
position.set(x, y);
}
/** Returns the bottom-center x-position of the GameObject. */
public float getX()
{
//Returns the x-position of the GameObject.
return position.x;
}
/** Sets the center x-position of the GameObject. */
public void setX(float x)
{
//Store the old x-position of the GameObject in the previousPosition Vector2.
previousPosition.x = position.x;
//Updates the center x-position of the GameObject.
position.x = x;
}
/** Returns the bottom-center y-position of the GameObject. */
public float getY()
{
//Returns the y-position of the GameObject.
return position.y;
}
/** Sets the bottom y-position of the GameObject. */
public void setY(float y)
{
//Store the old y-position of the GameObject in the previousPosition Vector2.
previousPosition.y = position.y;
//Updates the bottom y-position of the GameObject.
position.y = y;
}
/** Returns the GameObject's previous position before the current game tick. */
public Vector2 getPreviousPosition()
{
return previousPosition;
}
/** Retrieves the velocity of the gameObject as a Vector2. Operations can be performed on the Vector2 using its
* instance methods, as it is mutable. */
public Vector2 getVelocity() {
return velocity;
}
/** Sets the velocity of the GameObject at their desired (x,y) values. */
public void setVelocity(float x, float y)
{
velocity.set(x, y);
}
/** Updates the x-velocity of the GameObject. */
public void setVelocityX(float x)
{
velocity.x = x;
}
/** Updates the y-velocity of the GameObject. */
public void setVelocityY(float y)
{
velocity.y = y;
}
/** Retrieves the acceleration of the gameObject as a Vector2. Operations can be performed on the Vector2 using its
* instance methods, as it is mutable. */
public Vector2 getAcceleration() {
return velocity;
}
/** Sets the acceleration of the GameObject at their desired (x,y) values. */
public void setAcceleration(float x, float y)
{
acceleration.set(x, y);
}
/** Moves the GameObject to the desired (x,y) position in a straight line at the given speed in m/s. */
public void moveTo(float x, float y, float time)
{
//Finds the (x,y) distance from the GameObject to the target (x,y) position.
float xDist = x - position.x;
float yDist = y - position.y;
//Calculates the distance between the GameObject and the target position.
float dist = (float) Math.sqrt(xDist*xDist + yDist*yDist);
//Finds the speed the GameObject must go to reach the target in the given amount of time (v = d/t).
float speed = dist / time;
//Set the GameObject's velocity to the (x,y) distance to the target
velocity.set(xDist, yDist);
//Normalize the velocity to keep only the direction to the target.
velocity.normalize();
//Multiply the normalized vector by the speed so that the GameObject moves to the target position at the correct speed.
velocity.mul(speed);
}
/** Returns true if this GameObject is above the given GameObject. */
public boolean isAbove(GameObject gameObject)
{
//Gets the top-most y-position of the GameObject.
float topY = gameObject.getY() + ((Rectangle)gameObject.getCollider()).getHeight();
//If this GameObject is above the other GameObject
if(getY() > topY)
{
//Return true, since this GameObject is on top of the given GameObject.
return true;
}
//Else, if, last frame, this GameObject was above the GameObject, but is now below the GameObject
if(previousPosition.y > + topY && getY() < topY)
{
//This GameObject is still technically above the GameObject. Therefore, return true. Otherwise, the player would never hit the zombie's head.
return true;
}
//If this statement is reached, this GameObject is below the given GameObject. Therefore, return false.
return false;
}
/** Returns the collider used by the gameObject for collisions. */
public <T extends Collider> T getCollider()
{
return (T)collider;
}
/** Sets the collider used by the gameObject for collisions. */
public void setCollider(Collider c)
{
this.collider = c;
}
/** Returns the skeleton used to render the GameObject to the screen. Returns null if the GameObject doesn't use a Spine skeleton. */
public Skeleton getSkeleton() {
return skeleton;
}
/** Sets the spine skeleton used by the GameObject to be rendered on-screen. */
public void setSkeleton(Skeleton skeleton) {
this.skeleton = skeleton;
}
/** Gets the cell coordinates where the GameObject is placed on the TerrainLevel. */
public Cell getTerrainCell() {
return terrainCell;
}
/** Sets the cell coordinates where the GameObject is placed on the TerrainLevel. */
public void setTerrainCell(int row, int col) {
terrainCell.set(row, col);
}
/** Gets the amount of time the GameObject has been in a specific state. (e.g., stateTime = 0.4 if the GameObject has been in the JUMP
* state for 0.4 seconds. */
public float getStateTime() {
return stateTime;
}
/** Sets the amount of time the GameObject has been in a specific state. Only the gameObject's update() method should update this value. */
public void setStateTime(float stateTime) {
this.stateTime = stateTime;
}
/** Gets the ID of the GameObject, used to identify a GameObject in a specific TerrainLayer. */
public int getObjectId() {
return objectId;
}
/** Sets the ID of the GameObject, used to identify a GameObject in a specific TerrainLayer. */
public void setObjectId(int objectId) {
this.objectId = objectId;
}
}