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.GhostManager;
|
||||
import se.urmo.game.main.LevelManager;
|
||||
import se.urmo.game.main.ScorePopupManager;
|
||||
import se.urmo.game.map.GameMap;
|
||||
import se.urmo.game.map.MapTile;
|
||||
import se.urmo.game.map.TileType;
|
||||
@ -20,6 +21,7 @@ import se.urmo.game.util.GameFonts;
|
||||
import se.urmo.game.util.GameStateType;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.event.KeyEvent;
|
||||
@ -54,6 +56,9 @@ public class PlayingState implements GameState {
|
||||
private boolean deathInProgress;
|
||||
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) {
|
||||
this.gameStateManager = gameStateManager;
|
||||
this.gameOverState = gameOverState;
|
||||
@ -105,6 +110,7 @@ public class PlayingState implements GameState {
|
||||
pacman.update();
|
||||
}
|
||||
}
|
||||
scorePopups.update();
|
||||
}
|
||||
|
||||
private void advanceLevel() {
|
||||
@ -150,6 +156,7 @@ public class PlayingState implements GameState {
|
||||
pacman.draw(g);
|
||||
fruitManager.draw(g);
|
||||
drawUI(g);
|
||||
scorePopups.draw(g, GameMap.OFFSET_X, GameMap.OFFSET_Y, scorePopupFont);
|
||||
|
||||
// Phase overlays
|
||||
switch (phase) {
|
||||
@ -221,7 +228,9 @@ public class PlayingState implements GameState {
|
||||
if (ghost.isEaten()) return;
|
||||
if (ghost.isFrightened()) {
|
||||
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);
|
||||
} else {
|
||||
log.debug("Pacman loses a life");
|
||||
@ -251,6 +260,7 @@ public class PlayingState implements GameState {
|
||||
}
|
||||
|
||||
public void setScore(int score) {
|
||||
scorePopups.spawn(score);
|
||||
this.score += score;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user