Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/java/deltablade/DeltaBladeApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -1094,6 +1094,8 @@ protected void onUpdate(double tpf) {
pc.moveRight(tpf);
}

pc.updateIdle();

if (waveManager != null && !waveTransition) {
waveManager.update(tpf);
}
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/deltablade/DeltaBladeFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import deltablade.components.ExplosionComponent;
import deltablade.components.ExtraLetterPickupComponent;
import deltablade.components.PickupComponent;
import deltablade.components.PlayerAnimationComponent;
import deltablade.components.PlayerComponent;
import deltablade.components.StarComponent;
import javafx.scene.Group;
Expand Down Expand Up @@ -55,6 +56,24 @@ private static Node safeTexture(String name, int w, int h, Color fallback) {

@Spawns("player")
public Entity newPlayer(SpawnData data) {
Image thrusterSheet = EmbeddedTextures.getImage("player_blue_thruster.png", 384, 48);
Image bankSheet = EmbeddedTextures.getImage("player_blue_bank.png", 384, 48);
Image bowwaveSheet = EmbeddedTextures.getImage("player_blue_bowwave.png", 384, 48);

if (thrusterSheet != null && !thrusterSheet.isError()) {
PlayerAnimationComponent animComp = new PlayerAnimationComponent(thrusterSheet, bankSheet, bowwaveSheet);
ImageView view = animComp.getView();

return FXGL.entityBuilder(data)
.type(EntityType.PLAYER)
.viewWithBBox(view)
.zIndex(100)
.collidable()
.with(new PlayerComponent())
.with(animComp)
.build();
}

return FXGL.entityBuilder(data)
.type(EntityType.PLAYER)
.viewWithBBox(safeTexture("player.png", SHIP_SIZE, SHIP_SIZE, Color.DODGERBLUE))
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/deltablade/EmbeddedTextures.java

Large diffs are not rendered by default.

104 changes: 104 additions & 0 deletions src/main/java/deltablade/components/PlayerAnimationComponent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package deltablade.components;

import com.almasb.fxgl.entity.component.Component;
import javafx.geometry.Rectangle2D;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;

/**
* Component that handles animated sprite sheets for the player ship.
* Supports three animation states:
* - IDLE/THRUSTER: default yellow rear exhaust (plays continuously)
* - MOVING_UP: cyan bow wave at the nose (currently unused, ready for future)
* - BANKING: ship tilts left/right when strafing
*
* Each sprite sheet is 384x48 (8 frames of 48x48, 120ms per frame, looped).
*/
public class PlayerAnimationComponent extends Component {

public enum AnimationState {
IDLE,
BANKING_LEFT,
BANKING_RIGHT,
MOVING_UP
}

private final ImageView imageView;
private final Image thrusterSheet;
private final Image bankSheet;
private final Image bowwaveSheet;

private static final int FRAME_COUNT = 8;
private static final int FRAME_WIDTH = 48;
private static final int FRAME_HEIGHT = 48;
private static final double FRAME_DURATION = 0.120;

private AnimationState currentState = AnimationState.IDLE;
private double elapsed = 0;
private int currentFrame = 0;

private Image currentSheet;

public PlayerAnimationComponent(Image thrusterSheet, Image bankSheet, Image bowwaveSheet) {
this.thrusterSheet = thrusterSheet;
this.bankSheet = bankSheet;
this.bowwaveSheet = bowwaveSheet;
this.currentSheet = thrusterSheet;

this.imageView = new ImageView(thrusterSheet);
this.imageView.setFitWidth(FRAME_WIDTH);
this.imageView.setFitHeight(FRAME_HEIGHT);
this.imageView.setSmooth(false);
this.imageView.setPreserveRatio(false);
this.imageView.setViewport(new Rectangle2D(0, 0, FRAME_WIDTH, FRAME_HEIGHT));
}

public ImageView getView() {
return imageView;
}

public void setState(AnimationState newState) {
if (currentState != newState) {
currentState = newState;
Image newSheet = getSheetForState(newState);
if (newSheet != currentSheet) {
currentSheet = newSheet;
imageView.setImage(currentSheet);
}
}
}

public AnimationState getState() {
return currentState;
}

private Image getSheetForState(AnimationState state) {
return switch (state) {
case BANKING_LEFT, BANKING_RIGHT -> bankSheet != null ? bankSheet : thrusterSheet;
case MOVING_UP -> bowwaveSheet != null ? bowwaveSheet : thrusterSheet;
default -> thrusterSheet;
};
}

@Override
public void onUpdate(double tpf) {
elapsed += tpf;

int newFrame = (int) (elapsed / FRAME_DURATION) % FRAME_COUNT;

if (newFrame != currentFrame) {
currentFrame = newFrame;

int frameX = currentFrame * FRAME_WIDTH;
imageView.setViewport(new Rectangle2D(frameX, 0, FRAME_WIDTH, FRAME_HEIGHT));
}
}

public int getFrameWidth() {
return FRAME_WIDTH;
}

public int getFrameHeight() {
return FRAME_HEIGHT;
}
}
31 changes: 31 additions & 0 deletions src/main/java/deltablade/components/PlayerComponent.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ public class PlayerComponent extends Component {
private double fireCooldown = 0;
private static final double FIRE_RATE = 0.10;

private boolean wasMovingLeft = false;
private boolean wasMovingRight = false;

@Override
public void onUpdate(double tpf) {
tpf = Math.min(tpf, MAX_TPF);
Expand Down Expand Up @@ -43,6 +46,8 @@ public void moveLeft(double tpf) {
} else {
entity.setX(minX);
}
wasMovingLeft = true;
updateAnimationState();
}

public void moveRight(double tpf) {
Expand All @@ -53,6 +58,32 @@ public void moveRight(double tpf) {
} else {
entity.setX(maxX);
}
wasMovingRight = true;
updateAnimationState();
}

public void updateIdle() {
if (!wasMovingLeft && !wasMovingRight) {
setAnimationState(PlayerAnimationComponent.AnimationState.IDLE);
}
wasMovingLeft = false;
wasMovingRight = false;
}

private void updateAnimationState() {
if (wasMovingLeft && !wasMovingRight) {
setAnimationState(PlayerAnimationComponent.AnimationState.BANKING_LEFT);
} else if (wasMovingRight && !wasMovingLeft) {
setAnimationState(PlayerAnimationComponent.AnimationState.BANKING_RIGHT);
} else {
setAnimationState(PlayerAnimationComponent.AnimationState.IDLE);
}
}

private void setAnimationState(PlayerAnimationComponent.AnimationState state) {
if (entity.hasComponent(PlayerAnimationComponent.class)) {
entity.getComponent(PlayerAnimationComponent.class).setState(state);
}
}

public boolean canFire(int grade) {
Expand Down
Loading