Implemented cameras

Added basic cameras with panning
This commit is contained in:
2026-03-24 15:38:02 -03:00
parent d1d117caaf
commit d954aef0eb
8 changed files with 281 additions and 21 deletions

View File

@@ -1,6 +1,7 @@
package io.github.eldek0;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
@@ -41,6 +42,7 @@ public class App extends Game {
@Override
public void render() {
super.render();
Gdx.graphics.setTitle(Gdx.graphics.getFramesPerSecond() + " FPS");
}

View File

@@ -59,12 +59,12 @@ public class GameAssetManager {
// =========================================================
// PATHS — Cameras (utils)
// =========================================================
private static final String CAM_PATH = "sprites/cameras/";
private static final String CAM_UTILS_PATH = CAM_PATH + "utils/";
private static final String CAM_LABELS_PATH = CAM_PATH + "Labels/";
private static final String CAM_STATIC_PATH = CAM_PATH + "static/";
private static final String CAM_LOC_PATH = CAM_PATH + "locations/";
private static final String CAM_MANGLE_PATH = CAM_PATH + "mangle/";
public static final String CAM_PATH = "sprites/cameras/";
public static final String CAM_UTILS_PATH = CAM_PATH + "utils/";
public static final String CAM_LABELS_PATH = CAM_PATH + "Labels/";
public static final String CAM_STATIC_PATH = CAM_PATH + "static/";
public static final String CAM_LOC_PATH = CAM_PATH + "locations/";
public static final String CAM_MANGLE_PATH = CAM_PATH + "mangle/";
public static final String CAMERA_MAP = CAM_UTILS_PATH + "Map.png";
public static final String CAMERA_BORDERLINE = CAM_UTILS_PATH + "Border.png";

View File

@@ -1,11 +1,239 @@
package io.github.eldek0.game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import io.github.eldek0.App;
import io.github.eldek0.screen.GameScene;
import static io.github.eldek0.App.assets;
import static io.github.eldek0.asset.GameAssetManager.*;
public class Camera {
public Camera(){}
private static final int CAM_COUNT = 12;
private static final float CAMERA_SPEED = 5f * 60f; // px/s
public void render(SpriteBatch batch){}
private final float[][] BTN_POS = new float[][] {
{ 620 - 25, 621 - 125 }, { 735 - 25, 621 - 125 },
{ 620 - 25, 556 - 125 }, { 735 - 25, 556 - 125 },
{ 600 - 15, 700 - 105 }, { 710 - 15, 700 - 105 },
{ 769 - 35, 482 - 115 }, { 612 - 37, 471 - 115 },
{ 920 - 25, 441 - 110 }, { 830 - 10, 570 - 115 },
{ 954 - 25, 515 - 110 }, { 945 - 25, 608 - 105 }
};;
public void update(float delta){}
/** 1-based room index currently shown on the monitor. */
private int inCameraRoom = 10;
private final Rectangle[] buttonScreenRects = new Rectangle[CAM_COUNT];
// Wide-camera panning (indices 6-11 → rooms 7-12)
private final float[] camerasXPosition = new float[CAM_COUNT];
private final int[] wideCameraMovDirection = new int[CAM_COUNT];
private final long[] timerCheckpoints = new long[CAM_COUNT];
private final long[] timerWaitTimeMs = new long[CAM_COUNT];
private final boolean[] occupiedCamera = new boolean[CAM_COUNT];
private float recBlinkTimer = 0f;
private boolean recVisible = true;
private GameScene gameScene;
public Camera(GameScene gameScene) {
this.gameScene = gameScene;
java.util.Random rng = new java.util.Random();
for (int i = 0; i < CAM_COUNT; i++) {
timerWaitTimeMs[i] = 5000 + (long) rng.nextInt(2001); // [5000,7000] ms
}
this.initButtonScreenRects();
}
private void initButtonScreenRects(){
Texture btnUnsel = assets.getTexture(CAM_LABELS_PATH + "13.png");
for (int i = 0; i < CAM_COUNT; i++) {
float[] btnPos = BTN_POS[i];
float worldY = App.SCREEN_HEIGHT - btnPos[1] - btnUnsel.getHeight();
buttonScreenRects[i] = new Rectangle(btnPos[0], worldY, btnUnsel.getWidth() , btnUnsel.getHeight());
}
}
public void update(float delta) {
for (int i = 6; i < CAM_COUNT; i++) {
updateCameraTimer(i, delta);
}
recBlinkTimer += delta;
if (recBlinkTimer >= 1f) {
recBlinkTimer -= 1f;
recVisible = !recVisible;
}
this.onTouchDown();
}
public void renderBackground(SpriteBatch batch) {
if (!gameScene.hud.isInsideCamera()){return;}
renderCurrentRoom(batch);
}
public void onTouchDown() {
Vector2 position = App.convertPosToWorldPos(new Vector2(Gdx.input.getX(), Gdx.input.getY()));
for (int i = 0; i < CAM_COUNT; i++) {
if (buttonScreenRects[i].contains(position)) {
if (Gdx.input.isButtonJustPressed(Input.Buttons.LEFT)) {
inCameraRoom = i + 1;
return;
}
}
}
}
private void renderCurrentRoom(SpriteBatch batch) {
int idx = inCameraRoom - 1;
float xOff = camerasXPosition[idx];
if (!occupiedCamera[idx]) {
Texture frame = getDefaultFrame(inCameraRoom);
if (frame != null) {
batch.draw(frame, xOff, 0);
}
}
}
private Texture getDefaultFrame(int room) {
return switch (room) {
case 1 -> assets.getTexture(CAM_LOC_PATH + "PartyRoom1/0.png");
case 2 -> assets.getTexture(CAM_LOC_PATH + "PartyRoom2/0.png");
case 3 -> assets.getTexture(CAM_LOC_PATH + "PartyRoom3/0.png");
case 4 -> assets.getTexture(CAM_LOC_PATH + "PartyRoom4/0.png");
case 5 -> assets.getTexture(CAM_LOC_PATH + "LeftAirVent/0.png");
case 6 -> assets.getTexture(CAM_LOC_PATH + "RightAirVent/0.png");
case 7 -> assets.getTexture(CAM_LOC_PATH + "MainHall/0.png");
case 8 -> assets.getTexture(CAM_LOC_PATH + "PartsnService/0.png");
case 9 -> assets.getTexture(CAM_LOC_PATH + "ShowStage/0.png");
case 10 -> assets.getTexture(CAM_LOC_PATH + "GameArea/2.png");
case 11 -> assets.getTexture(CAM_LOC_PATH + "PrizeCorner/0.png");
case 12 -> assets.getTexture(CAM_LOC_PATH + "KidsCove/0.png");
default -> null;
};
}
public void renderUI(SpriteBatch batch) {
if (!gameScene.hud.isInsideCamera()) {return;}
// Border overlay
Texture border = assets.getTexture(CAM_UTILS_PATH + "Border.png");
batch.draw(border, 0, 0);
// Map
Texture map = assets.getTexture(CAM_UTILS_PATH + "Map.png");
batch.draw(map, 550, App.SCREEN_HEIGHT - 310 - map.getHeight());
// Room label
Texture label = getRoomLabel(inCameraRoom);
if (label != null) {
batch.draw(label, 550, App.SCREEN_HEIGHT - 280 - label.getHeight());
}
drawCameraButtons(batch);
// REC sprite
if (recVisible) {
Texture rec = assets.getTexture(CAM_UTILS_PATH + "1.png");
batch.draw(rec, 40, App.SCREEN_HEIGHT - 40 - rec.getHeight());
}
// Signal interrupted banner
if (occupiedCamera[inCameraRoom - 1]) {
Texture sig = assets.getTexture(CAM_UTILS_PATH + "2.png");
float sx = Gdx.graphics.getWidth() / 2f - sig.getWidth() / 2f;
batch.draw(sig, sx, App.SCREEN_HEIGHT - 80 - sig.getHeight());
}
}
public void renderHitboxes(ShapeRenderer shapeRenderer) {
shapeRenderer.setColor(1, 0, 0, 1);
for (Rectangle rect : this.buttonScreenRects){
shapeRenderer.rect(rect.x, rect.y, rect.width, rect.height);
}
}
private void drawCameraButtons(SpriteBatch batch) {
Texture btnUnsel = assets.getTexture(CAM_LABELS_PATH + "13.png");
Texture btnSel = assets.getTexture(CAM_LABELS_PATH + "14.png");
for (int i = 0; i < CAM_COUNT; i++) {
Texture btn = (i + 1 == inCameraRoom) ? btnSel : btnUnsel;
float pyX = BTN_POS[i][0];
float pyY = BTN_POS[i][1];
float worldY = App.SCREEN_HEIGHT - pyY - btnUnsel.getHeight();
batch.draw(btn, pyX, worldY);
Texture lbl = assets.getTexture(CAM_LABELS_PATH + (i + 1) + ".png");
batch.draw(lbl, pyX + 5, worldY + 7);
}
}
private Texture getRoomLabel(int room) {
return switch (room) {
case 1 -> assets.getTexture(CAM_LOC_PATH + "PartyRoom1/label.png");
case 2 -> assets.getTexture(CAM_LOC_PATH + "PartyRoom2/label.png");
case 3 -> assets.getTexture(CAM_LOC_PATH + "PartyRoom3/label.png");
case 4 -> assets.getTexture(CAM_LOC_PATH + "PartyRoom4/label.png");
case 5 -> assets.getTexture(CAM_LOC_PATH + "LeftAirVent/label.png");
case 6 -> assets.getTexture(CAM_LOC_PATH + "RightAirVent/label.png");
case 7 -> assets.getTexture(CAM_LOC_PATH + "MainHall/label.png");
case 8 -> assets.getTexture(CAM_LOC_PATH + "PartsnService/label.png");
case 9 -> assets.getTexture(CAM_LOC_PATH + "ShowStage/label.png");
case 10 -> assets.getTexture(CAM_LOC_PATH + "GameArea/label.png");
case 11 -> assets.getTexture(CAM_LOC_PATH + "PrizeCorner/label.png");
case 12 -> assets.getTexture(CAM_LOC_PATH + "KidsCove/label.png");
default -> null;
};
}
private void updateCameraTimer(int index, float delta) {
if (index < 6) return;
Texture sample = getDefaultFrame(index + 1);
if (sample == null) return;
float maxOffset = -(sample.getWidth() - App.SCREEN_WIDTH);
long now = System.currentTimeMillis();
if (timerCheckpoints[index] != 0) {
if (now - timerCheckpoints[index] > timerWaitTimeMs[index]) {
timerCheckpoints[index] = 0;
}
return;
}
if (wideCameraMovDirection[index] == 0) {
camerasXPosition[index] -= CAMERA_SPEED * delta;
if (camerasXPosition[index] <= maxOffset) {
camerasXPosition[index] = maxOffset;
timerCheckpoints[index] = now;
wideCameraMovDirection[index] = 1;
}
} else {
camerasXPosition[index] += CAMERA_SPEED * delta;
if (camerasXPosition[index] >= 0) {
camerasXPosition[index] = 0;
timerCheckpoints[index] = now;
wideCameraMovDirection[index] = 0;
}
}
}
public int getInCameraRoom() { return inCameraRoom; }
public void setInCameraRoom(int room) { this.inCameraRoom = room; }
public boolean isOccupied(int roomIndex) { return occupiedCamera[roomIndex]; }
public void setOccupied(int roomIndex, boolean v) { occupiedCamera[roomIndex] = v; }
}

View File

@@ -167,7 +167,7 @@ public class Office {
}
public void update(float dt) {
positionX += (int) (SPEED * movement * dt);
positionX += SPEED * movement * dt;
if (-positionX < 0) positionX = 0;
else if (-positionX > MAX_POS_X) positionX = -MAX_POS_X;
}

View File

@@ -11,6 +11,7 @@ import com.badlogic.gdx.graphics.glutils.FrameBuffer;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import io.github.eldek0.App;
import io.github.eldek0.game.Camera;
import io.github.eldek0.game.Mask;
import io.github.eldek0.game.Office;
import io.github.eldek0.ui.HUD;
@@ -25,6 +26,7 @@ public class GameScene implements Screen {
private final Office office;
public final HUD hud;
private final Mask mask;
private final Camera camera;
FrameBuffer frameBuffer;
public GameScene(App app) {
@@ -36,6 +38,7 @@ public class GameScene implements Screen {
this.office = new Office(this.hud);
this.mask = new Mask(this.hud);
this.camera = new Camera(this);
frameBuffer = new FrameBuffer(Pixmap.Format.RGBA8888, App.SCREEN_WIDTH, App.SCREEN_HEIGHT, false);
@@ -55,11 +58,13 @@ public class GameScene implements Screen {
office.update(v);
hud.update(v);
mask.update(v);
camera.update(v);
frameBuffer.begin();
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
office.render(batch);
camera.renderBackground(batch);
batch.end();
frameBuffer.end();
@@ -72,6 +77,7 @@ public class GameScene implements Screen {
batch.setShader(null);
mask.render(batch);
camera.renderUI(batch);
hud.render();
batch.end();
@@ -82,6 +88,8 @@ public class GameScene implements Screen {
hud.renderHitboxes(shapeRenderer);
camera.renderHitboxes(shapeRenderer);
shapeRenderer.end();
if (Gdx.input.isKeyJustPressed(Input.Keys.F5) && App.DEBUG) {
@@ -117,7 +125,7 @@ public class GameScene implements Screen {
public void reloadShader() {
if (shader != null) shader.dispose();
String basePath = System.getProperty("user.dir") + "/assets/";
String basePath = System.getProperty("user.dir") + "/";
String vert = Gdx.files.internal(basePath + VERTEX_PATH).readString();
String frag = Gdx.files.internal(basePath + FRAGMENT_PATH).readString();

View File

@@ -24,6 +24,8 @@ public class Button {
private final Animation<Texture> animation;
private float stateTime;
private boolean enable = true;
public Button(Texture sprite, float x, float y, Rectangle bounds, Texture[] transitionFrames) {
this.buttonTexture = sprite;
this.x = x;
@@ -35,6 +37,7 @@ public class Button {
}
public void update(float delta) {
if (!enable){return;}
Vector2 position = App.convertPosToWorldPos(new Vector2(Gdx.input.getX(), Gdx.input.getY()));
if (bounds.contains(position)) {
@@ -57,10 +60,12 @@ public class Button {
}
public void render(SpriteBatch batch) {
if (!enable){return;}
batch.draw(buttonTexture, x, y, buttonTexture.getWidth(), buttonTexture.getHeight());
}
public void renderHitbox(ShapeRenderer shapeRenderer) {
if (!enable){return;}
shapeRenderer.rect(bounds.x, bounds.y, bounds.width, bounds.height);
}
@@ -89,6 +94,9 @@ public class Button {
}
public boolean isEnable() {return enable;}
public void enable() {this.enable = true;}
public void disable() {this.enable = false;}
public boolean isInTransition(){return entering || quitting;}
public boolean isInside() { return inside; }
public boolean isBeingPressed() { return beingPressed; }

View File

@@ -24,6 +24,19 @@ public class HUD {
cameraButton.render(batch);
batch.end();
if (maskButton.isInTransition() || maskButton.isInside()){
maskButton.enable();
cameraButton.disable();
}
else if (cameraButton.isInTransition() || cameraButton.isInside()) {
maskButton.disable();
cameraButton.enable();
}
else {
maskButton.enable();
cameraButton.enable();
}
}
public void renderHitboxes(ShapeRenderer shapeRenderer) {