package scene2d;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.math.Rectangle;
public class Camera extends OrthographicCamera {
private static Camera instance;
private static float duration;
private static float time;
private static Interpolation interpolation;
private static boolean moveCompleted;
private static float lastPercent;
private static float percentDelta;
private static float panSpeedX, panSpeedY;
private static final Array<Actor> hudActors = new Array<Actor>();
private static Actor followedActor = null;
private static float followSpeed = 1f;
/*
* This is to set the offsets of camera position when following the actor
* When the camera follows the actor its (x,y) position is set to actor's (x,y) position
* based on followSpeed. The offsets are used to position the camera in such a way that the actor
* doesn't need to be at the center of the camera always
*/
public static Rectangle followOffset = new Rectangle(10,70,10,60);
private static boolean followContinous = false;
public static boolean usePan = false;
public static boolean useDrag = false;
/*
* Sets the speed at which the camera pans. By default it moves 1px for a duration a 1f
* so its speed is 1px/f. So reduce the duration to increase its speed.
* ex: setPanSpeed(0.5) will change its speed to 2px/f
* Here: f can/maybe also indicate seconds
*/
public static float panSpeed = 5f;
public static Rectangle panSize;
/*
* This sets the boundary of the camera till what position can it move or pan in the
* directions left, right, top, down. This is to prevent is from panning overboard the game area.
* Usually the bounds of the camera is like a rectangle. This must be calculated carefully
* as the camera's position is based on its center.
*/
public static Rectangle bounds = new Rectangle(0,0,999,999);
Camera(){
setToOrtho(false, Scene.targetWidth, Scene.targetHeight);
position.set(Scene.targetWidth/2, Scene.targetHeight/2, 0);
instance = this;
panSize = new Rectangle(10, 10, Scene.targetWidth-10, Scene.targetHeight - 10);
}
/*
* Moves the camera to x,y over a time duration
*/
public void moveTo(float x, float y, float duration) {
moveBy(x-position.x, y-position.y, duration);
}
/*
* Moves the camera by amountX, amountY over a time duration
*/
public static void moveBy (float amountX, float amountY, float duration) {
moveBy(amountX, amountY, duration, null);
}
/*
* Moves the camera by amountX, amountY over a time duration and interpolation interp
*/
public static void moveBy (float amountX, float amountY, float dur, Interpolation interp) {
duration = dur;
interpolation = interp;
panSpeedX = amountX;
panSpeedY = amountY;
lastPercent = 0;
time = 0;
moveCompleted = false;
}
private static Rectangle cullRect = new Rectangle();
private void moveByAction(float delta){
time += delta;
moveCompleted = time >= duration;
float percent;
if (moveCompleted)
percent = 1;
else {
percent = time / duration;
if (interpolation != null) percent = interpolation.apply(percent);
}
percentDelta = percent - lastPercent;
if(Scene.cullingEnabled){
cullRect.set(getXLeft(), getYBot(), Scene.targetWidth, Scene.targetHeight);
Scene.getCurrentScene().setCullingArea(cullRect);
}
translate(panSpeedX * percentDelta, panSpeedY * percentDelta, 0);
for(Actor actor: hudActors)
actor.setPosition(actor.getX()+panSpeedX * percentDelta, actor.getY()+panSpeedY * percentDelta);
lastPercent = percent;
if (moveCompleted) interpolation = null;
}
public void resetCamera(){
position.set(Scene.targetWidth/2, Scene.targetHeight/2, 0);
}
/*
* This makes the camera follow the actor once and only once. Once the camera reaches its
* target, it stops following the actor.
*/
public static void followActor(Actor actor){
followedActor = actor;
followContinous = false;
}
/*
* This makes the camera follow the actor continuously, even after the camera reaches its
* target, it keeps following the if the actor changes its position.
*/
public static void followActorContinuously(Actor actor){
followedActor = actor;
followContinous = true;
}
/*
* Sets the speed at which the camera follows the actor. By default it moves 1px for a duration of 1f
* so its speed is 1px/f. So reduce the duration to increase its speed.
* ex: setPanSpeed(0.5) will change its speed to 2px/f
* Here: f can/maybe also indicate seconds
*/
public static void setFollowSpeed(float duration){
followSpeed = duration;
}
private void follow(){
//if(camera.position.x == followedActor.getX()+followLeftOffset &&
// camera.position.y == followedActor.getY()+followTopOffset)
//return;
//moveTo(followedActor.getX()+followLeftOffset, followedActor.getY()+followTopOffset, 100f);
if(position.x < followedActor.getX() - followOffset.x) moveBy(1f, 0, followSpeed);
else if(position.x > followedActor.getX() + followOffset.width) moveBy(-1f, 0, followSpeed);
else if(position.y < followedActor.getY() - followOffset.y) moveBy(0, 1f, followSpeed);
else if(position.y > followedActor.getY() - followOffset.height) moveBy(0, -1f, followSpeed);
else {
if(!followContinous)
followedActor = null;
}
}
@Override
public void update(){
super.update();
Scene.mouse.x = Gdx.input.getX();
Scene.mouse.y = Gdx.graphics.getHeight() - Gdx.input.getY();
if(!moveCompleted)
moveByAction(Scene.stateTime);//FIXME
if(usePan)
panCameraWithMouse();
if(followedActor != null)
follow();
}
private void panCameraWithMouse(){
if(Scene.mouse.x > panSize.width && getXLeft() < bounds.width) moveBy(1f, 0, panSpeed);
else if(Scene.mouse.x < panSize.x && getXLeft() > bounds.x) moveBy(-1f, 0, panSpeed);
else if(Scene.mouse.y < panSize.y && getYBot() > bounds.y) moveBy(0, -1f, panSpeed);
else if(Scene.mouse.y > panSize.height && getYBot() < bounds.height) moveBy(0, 1f, panSpeed);
}
private final static Vector3 curr = new Vector3();
private final static Vector3 last = new Vector3(-1, -1, -1);
private final static Vector3 deltaDrag = new Vector3();
private static float deltaCamX = 0;
private static float deltaCamY = 0;
public static void dragCam(int x, int y){
instance.unproject(curr.set(x, y, 0));
if (!(last.x == -1 && last.y == -1 && last.z == -1)) {
instance.unproject(deltaDrag.set(last.x, last.y, 0));
deltaDrag.sub(curr);
deltaCamX = deltaDrag.x + instance.position.x;
deltaCamY = deltaDrag.y + instance.position.y;
if(deltaCamX > bounds.x && deltaCamX < bounds.x+bounds.width)
moveBy(deltaDrag.x, 0f, 0f);
if(deltaCamY > bounds.y && deltaCamY < bounds.y+bounds.height)
moveBy(0f, deltaDrag.y, 0f);
}
last.set(x, y, 0);
}
public static void resetDrag(){
last.set(-1, -1, -1);
}
public static void reset(){
usePan = false;
followActor(null);
clearAllHud();
instance.position.set(Scene.targetWidth/2, Scene.targetHeight/2, 0);
}
/* If you want to make any elements/actors to move along with the camera
* like HUD's add them using this method */
public static void addHud(Actor actor){
if(actor != null){
Scene.getCurrentScene().addActor(actor);
hudActors.add(actor);
}
}
/* If you want to make any elements/actors to move along with the camera
* like HUD's add them using this method */
public static void addHud(String actorName){
if(actorName != null && !actorName.isEmpty()){
Actor actor = Scene.getCurrentScene().findActor(actorName);
Scene.getCurrentScene().addActor(actor);
hudActors.add(actor);
}
}
/* If you want to any elements/actors which was a Hud the use this */
public static void removeHud(Actor actor){
Scene.getCurrentScene().removeActor(actor);
hudActors.removeValue(actor, true);
}
/* If you want to any elements/actors which was a Hud the use this */
public static void removeHud(String actorName){
if(actorName != null && !actorName.isEmpty()){
Actor actor = Scene.getCurrentScene().findActor(actorName);
Scene.getCurrentScene().removeActor(actor);
hudActors.removeValue(actor, true);
}
}
/*
* Clears all hud's registered with the camera
*/
public static void clearAllHud(){
hudActors.clear();
}
/*
* Returns the x position of the camera
*/
public static float getX(){
return instance.position.x;
}
/*
* Returns the y position of the camera
*/
public static float getY(){
return instance.position.y;
}
public static float getXLeft(){
return instance.position.x - Scene.targetWidth/2;
}
public static float getYBot(){
return instance.position.y - Scene.targetHeight/2;
}
public static float getWidth(){
return instance.viewportWidth;
}
public static float getHeight(){
return instance.viewportHeight;
}
}