ScorePopup working
This commit is contained in:
58
src/main/java/se/urmo/game/entities/ScorePopup.java
Normal file
58
src/main/java/se/urmo/game/entities/ScorePopup.java
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package se.urmo.game.entities;
|
||||||
|
|
||||||
|
import java.awt.AlphaComposite;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
|
||||||
|
public final class ScorePopup {
|
||||||
|
private final double startX, startY; // world px (not screen)
|
||||||
|
private final String text;
|
||||||
|
private final long startNs;
|
||||||
|
private final long lifeNs; // e.g. 1_000_000_000L (1s)
|
||||||
|
|
||||||
|
// simple motion params
|
||||||
|
private final double risePixels; // e.g. 16 px total rise
|
||||||
|
|
||||||
|
public ScorePopup(double x, double y, String text, long lifeNs, double risePixels) {
|
||||||
|
this.startX = x;
|
||||||
|
this.startY = y;
|
||||||
|
this.text = text;
|
||||||
|
this.lifeNs = lifeNs;
|
||||||
|
this.risePixels = risePixels;
|
||||||
|
this.startNs = System.nanoTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAlive() {
|
||||||
|
return (System.nanoTime() - startNs) < lifeNs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void draw(Graphics2D g, int offsetX, int offsetY, Font font) {
|
||||||
|
long dt = System.nanoTime() - startNs;
|
||||||
|
double t = Math.min(1.0, dt / (double) lifeNs); // 0..1
|
||||||
|
|
||||||
|
// motion: ease-out upward (quadratic)
|
||||||
|
double y = startY - (risePixels * (1 - (1 - t) * (1 - t)));
|
||||||
|
|
||||||
|
// fade: alpha 1 → 0
|
||||||
|
float alpha = (float) (1.0 - t);
|
||||||
|
|
||||||
|
var oldComp = g.getComposite();
|
||||||
|
var oldFont = g.getFont();
|
||||||
|
g.setFont(font);
|
||||||
|
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
|
||||||
|
|
||||||
|
// draw centered on tile center
|
||||||
|
var fm = g.getFontMetrics();
|
||||||
|
int sx = offsetX + (int) Math.round(startX);
|
||||||
|
int sy = offsetY + (int) Math.round(y);
|
||||||
|
int x = sx - fm.stringWidth(text) / 2;
|
||||||
|
int baseline = sy; // tune if you want it a tad above the exact point
|
||||||
|
|
||||||
|
g.setColor(Color.WHITE);
|
||||||
|
g.drawString(text, x, baseline);
|
||||||
|
|
||||||
|
g.setComposite(oldComp);
|
||||||
|
g.setFont(oldFont);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/main/java/se/urmo/game/main/ScorePopupManager.java
Normal file
41
src/main/java/se/urmo/game/main/ScorePopupManager.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package se.urmo.game.main;
|
||||||
|
|
||||||
|
import se.urmo.game.entities.ScorePopup;
|
||||||
|
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class ScorePopupManager {
|
||||||
|
private final List<ScorePopup> popups = new ArrayList<>();
|
||||||
|
|
||||||
|
// convenience defaults
|
||||||
|
private static final long LIFE_NS = 1_000_000_000L; // 1s
|
||||||
|
private static final double RISE_PX = 16.0;
|
||||||
|
private static final double worldX = GamePanel.SCREEN_WIDTH / 2.0 - 15;
|
||||||
|
private static final double worldY = 230;
|
||||||
|
|
||||||
|
public void spawn(String text) {
|
||||||
|
popups.add(new ScorePopup(worldX, worldY, text, LIFE_NS, RISE_PX));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void spawn(int score) {
|
||||||
|
spawn(String.valueOf(score));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
// time-based; no per-tick math needed, just prune dead ones
|
||||||
|
popups.removeIf(p -> !p.isAlive());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void draw(Graphics2D g, int offsetX, int offsetY, Font font) {
|
||||||
|
for (ScorePopup p : popups) {
|
||||||
|
p.draw(g, offsetX, offsetY, font);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
popups.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,6 +12,7 @@ import se.urmo.game.main.FruitManager;
|
|||||||
import se.urmo.game.main.GameStateManager;
|
import se.urmo.game.main.GameStateManager;
|
||||||
import se.urmo.game.main.GhostManager;
|
import se.urmo.game.main.GhostManager;
|
||||||
import se.urmo.game.main.LevelManager;
|
import se.urmo.game.main.LevelManager;
|
||||||
|
import se.urmo.game.main.ScorePopupManager;
|
||||||
import se.urmo.game.map.GameMap;
|
import se.urmo.game.map.GameMap;
|
||||||
import se.urmo.game.map.MapTile;
|
import se.urmo.game.map.MapTile;
|
||||||
import se.urmo.game.map.TileType;
|
import se.urmo.game.map.TileType;
|
||||||
@ -20,6 +21,7 @@ import se.urmo.game.util.GameFonts;
|
|||||||
import se.urmo.game.util.GameStateType;
|
import se.urmo.game.util.GameStateType;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Font;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
@ -54,6 +56,9 @@ public class PlayingState implements GameState {
|
|||||||
private boolean deathInProgress;
|
private boolean deathInProgress;
|
||||||
private int frightMultiplier;
|
private int frightMultiplier;
|
||||||
|
|
||||||
|
private final ScorePopupManager scorePopups = new ScorePopupManager();
|
||||||
|
private final Font scorePopupFont = GameFonts.arcade(16F); // or your arcade font
|
||||||
|
|
||||||
public PlayingState(GameStateManager gameStateManager, GameOverState gameOverState) {
|
public PlayingState(GameStateManager gameStateManager, GameOverState gameOverState) {
|
||||||
this.gameStateManager = gameStateManager;
|
this.gameStateManager = gameStateManager;
|
||||||
this.gameOverState = gameOverState;
|
this.gameOverState = gameOverState;
|
||||||
@ -105,6 +110,7 @@ public class PlayingState implements GameState {
|
|||||||
pacman.update();
|
pacman.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
scorePopups.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void advanceLevel() {
|
private void advanceLevel() {
|
||||||
@ -150,6 +156,7 @@ public class PlayingState implements GameState {
|
|||||||
pacman.draw(g);
|
pacman.draw(g);
|
||||||
fruitManager.draw(g);
|
fruitManager.draw(g);
|
||||||
drawUI(g);
|
drawUI(g);
|
||||||
|
scorePopups.draw(g, GameMap.OFFSET_X, GameMap.OFFSET_Y, scorePopupFont);
|
||||||
|
|
||||||
// Phase overlays
|
// Phase overlays
|
||||||
switch (phase) {
|
switch (phase) {
|
||||||
@ -221,7 +228,9 @@ public class PlayingState implements GameState {
|
|||||||
if (ghost.isEaten()) return;
|
if (ghost.isEaten()) return;
|
||||||
if (ghost.isFrightened()) {
|
if (ghost.isFrightened()) {
|
||||||
log.debug("Pacman eats ghost");
|
log.debug("Pacman eats ghost");
|
||||||
score += 200 * (1 << (ghostManager.getGhosts().size() - ghostManager.isFrightened()));
|
int pts = 200 * (1 << (ghostManager.getGhosts().size() - ghostManager.isFrightened()));
|
||||||
|
score += pts;
|
||||||
|
scorePopups.spawn(pts);
|
||||||
ghost.setMode(GhostMode.EATEN);
|
ghost.setMode(GhostMode.EATEN);
|
||||||
} else {
|
} else {
|
||||||
log.debug("Pacman loses a life");
|
log.debug("Pacman loses a life");
|
||||||
@ -251,6 +260,7 @@ public class PlayingState implements GameState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setScore(int score) {
|
public void setScore(int score) {
|
||||||
|
scorePopups.spawn(score);
|
||||||
this.score += score;
|
this.score += score;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user