feat(sts-viewer): add spawn/despawn tweens and async interruptions
Add visual tweens for Buff and Card game objects. Update spawners to use `GameHostScene` and `addInterruption` to ensure animations complete before the next state change occurs.
This commit is contained in:
parent
c29b9a43b3
commit
dfbdaa3499
|
|
@ -129,4 +129,24 @@ export class Buff extends Phaser.GameObjects.Container {
|
||||||
this.tooltipContainer.destroy(fromScene);
|
this.tooltipContainer.destroy(fromScene);
|
||||||
super.destroy(fromScene);
|
super.destroy(fromScene);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
playSpawnTween() {
|
||||||
|
this.setScale(0);
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: this,
|
||||||
|
scale: 1,
|
||||||
|
duration: 200,
|
||||||
|
ease: "Back.Out",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
playDespawnTween() {
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: this,
|
||||||
|
scale: 0,
|
||||||
|
duration: 200,
|
||||||
|
ease: "Back.In",
|
||||||
|
onComplete: () => this.destroy(),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
|
import { GameHostScene } from "boardgame-phaser";
|
||||||
import { BuffData, Buff } from "./Buff";
|
import { BuffData, Buff } from "./Buff";
|
||||||
import { SpawnerCallback } from "boardgame-phaser";
|
import { SpawnerCallback } from "boardgame-phaser";
|
||||||
|
import { CombatState } from "boardgame-core/samples/slay-the-spire-like";
|
||||||
|
|
||||||
export type BuffDataWithIndex = BuffData & { x: number };
|
export type BuffDataWithIndex = BuffData & { x: number };
|
||||||
export class BuffSpawner implements SpawnerCallback<BuffDataWithIndex, Buff> {
|
export class BuffSpawner implements SpawnerCallback<BuffDataWithIndex, Buff> {
|
||||||
constructor(
|
constructor(
|
||||||
public scene: Phaser.Scene,
|
public scene: GameHostScene<CombatState>,
|
||||||
public buffContainer: Phaser.GameObjects.Container,
|
public buffContainer: Phaser.GameObjects.Container,
|
||||||
) {}
|
) {}
|
||||||
getKey(t: BuffDataWithIndex) {
|
getKey(t: BuffDataWithIndex) {
|
||||||
|
|
@ -13,10 +15,13 @@ export class BuffSpawner implements SpawnerCallback<BuffDataWithIndex, Buff> {
|
||||||
onSpawn(t: BuffDataWithIndex) {
|
onSpawn(t: BuffDataWithIndex) {
|
||||||
const buff = new Buff(this.scene, t.x, 0, t);
|
const buff = new Buff(this.scene, t.x, 0, t);
|
||||||
this.buffContainer.add(buff);
|
this.buffContainer.add(buff);
|
||||||
|
buff.playSpawnTween();
|
||||||
|
this.scene.addInterruption(new Promise((r) => setTimeout(r, 50)));
|
||||||
return buff;
|
return buff;
|
||||||
}
|
}
|
||||||
onDespawn(obj: Buff) {
|
onDespawn(obj: Buff) {
|
||||||
obj.destroy();
|
this.scene.addInterruption(new Promise((r) => setTimeout(r, 50)));
|
||||||
|
obj.playDespawnTween();
|
||||||
}
|
}
|
||||||
onUpdate(t: BuffDataWithIndex, obj: Buff) {
|
onUpdate(t: BuffDataWithIndex, obj: Buff) {
|
||||||
obj.setPosition(t.x, 0);
|
obj.setPosition(t.x, 0);
|
||||||
|
|
|
||||||
|
|
@ -165,10 +165,10 @@ export class CardContainer extends Phaser.GameObjects.Container {
|
||||||
return this._selected;
|
return this._selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
playSpawnTween(delay = 0): void {
|
playSpawnTween(delay = 0) {
|
||||||
this.setAlpha(0);
|
this.setAlpha(0);
|
||||||
this.setScale(0.5);
|
this.setScale(0.5);
|
||||||
this.scene.tweens.add({
|
return this.scene.tweens.add({
|
||||||
targets: this,
|
targets: this,
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
|
|
@ -178,8 +178,8 @@ export class CardContainer extends Phaser.GameObjects.Container {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
playDespawnTween(onComplete?: () => void): void {
|
playDespawnTween(onComplete?: () => void) {
|
||||||
this.scene.tweens.add({
|
return this.scene.tweens.add({
|
||||||
targets: this,
|
targets: this,
|
||||||
alpha: 0,
|
alpha: 0,
|
||||||
scale: 0.5,
|
scale: 0.5,
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import type {
|
||||||
GameCard,
|
GameCard,
|
||||||
} from "boardgame-core/samples/slay-the-spire-like";
|
} from "boardgame-core/samples/slay-the-spire-like";
|
||||||
import { CardContainer } from "./CardContainer";
|
import { CardContainer } from "./CardContainer";
|
||||||
|
import { GameHostScene } from "boardgame-phaser";
|
||||||
|
|
||||||
export interface CardSpawnData {
|
export interface CardSpawnData {
|
||||||
cardId: string;
|
cardId: string;
|
||||||
|
|
@ -17,7 +18,7 @@ const HAND_MARGIN = 100;
|
||||||
|
|
||||||
export class CardSpawner implements Spawner<CardSpawnData, CardContainer> {
|
export class CardSpawner implements Spawner<CardSpawnData, CardContainer> {
|
||||||
constructor(
|
constructor(
|
||||||
private scene: Phaser.Scene,
|
private scene: GameHostScene<CombatState>,
|
||||||
private getState: () => CombatState,
|
private getState: () => CombatState,
|
||||||
private onCardClick: (cardId: string) => void,
|
private onCardClick: (cardId: string) => void,
|
||||||
) {}
|
) {}
|
||||||
|
|
@ -47,6 +48,7 @@ export class CardSpawner implements Spawner<CardSpawnData, CardContainer> {
|
||||||
});
|
});
|
||||||
|
|
||||||
container.playSpawnTween(data.index * 40);
|
container.playSpawnTween(data.index * 40);
|
||||||
|
this.scene.addInterruption(new Promise((r) => setTimeout(r, 40)));
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,8 +65,9 @@ export class CardSpawner implements Spawner<CardSpawnData, CardContainer> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onDespawn(obj: CardContainer): void {
|
onDespawn(obj: CardContainer, data: CardSpawnData): void {
|
||||||
obj.playDespawnTween(() => obj.destroy());
|
obj.playDespawnTween(() => obj.destroy());
|
||||||
|
this.scene.addInterruption(new Promise((r) => setTimeout(r, 40)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private getCardPosition(
|
private getCardPosition(
|
||||||
|
|
@ -104,7 +107,7 @@ export class CardSpawner implements Spawner<CardSpawnData, CardContainer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createCardSpawner(
|
export function createCardSpawner(
|
||||||
scene: Phaser.Scene,
|
scene: GameHostScene<CombatState>,
|
||||||
getState: () => CombatState,
|
getState: () => CombatState,
|
||||||
onCardClick: (cardId: string) => void,
|
onCardClick: (cardId: string) => void,
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import type {
|
import type {
|
||||||
CombatEntity,
|
CombatEntity,
|
||||||
|
CombatState,
|
||||||
EffectTable,
|
EffectTable,
|
||||||
EnemyEntity,
|
EnemyEntity,
|
||||||
} from "boardgame-core/samples/slay-the-spire-like";
|
} from "boardgame-core/samples/slay-the-spire-like";
|
||||||
import { BuffData } from "./Buff";
|
import { BuffData } from "./Buff";
|
||||||
import { createSpawnUpdate } from "boardgame-phaser";
|
import { createSpawnUpdate } from "boardgame-phaser";
|
||||||
import { BuffDataWithIndex, BuffSpawner } from "./BuffSpawner";
|
import { BuffDataWithIndex, BuffSpawner } from "./BuffSpawner";
|
||||||
|
import { GameHostScene } from "boardgame-phaser";
|
||||||
|
|
||||||
export type CombatUnitData = {
|
export type CombatUnitData = {
|
||||||
key: string;
|
key: string;
|
||||||
|
|
@ -30,15 +32,22 @@ export class CombatUnitContainer extends Phaser.GameObjects.Container {
|
||||||
private hpText!: Phaser.GameObjects.Text;
|
private hpText!: Phaser.GameObjects.Text;
|
||||||
private buffContainer!: Phaser.GameObjects.Container;
|
private buffContainer!: Phaser.GameObjects.Container;
|
||||||
private intentText!: Phaser.GameObjects.Text | null;
|
private intentText!: Phaser.GameObjects.Text | null;
|
||||||
|
private hostScene: GameHostScene<CombatState>;
|
||||||
private updateBuffs!: (buffs: Iterable<BuffData & { x: number }>) => void;
|
private updateBuffs!: (buffs: Iterable<BuffData & { x: number }>) => void;
|
||||||
|
|
||||||
private currentEntity!: CombatEntity;
|
private currentEntity!: CombatEntity;
|
||||||
private currentName: string;
|
private currentName: string;
|
||||||
private currentIsPlayer: boolean;
|
private currentIsPlayer: boolean;
|
||||||
|
|
||||||
constructor(scene: Phaser.Scene, x: number, y: number, data: CombatUnitData) {
|
constructor(
|
||||||
|
scene: GameHostScene<CombatState>,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
data: CombatUnitData,
|
||||||
|
) {
|
||||||
super(scene, x, y);
|
super(scene, x, y);
|
||||||
scene.add.existing(this);
|
scene.add.existing(this);
|
||||||
|
this.hostScene = scene;
|
||||||
|
|
||||||
this.currentEntity = data.entity;
|
this.currentEntity = data.entity;
|
||||||
this.currentName = data.name;
|
this.currentName = data.name;
|
||||||
|
|
@ -92,7 +101,7 @@ export class CombatUnitContainer extends Phaser.GameObjects.Container {
|
||||||
|
|
||||||
this.buffContainer = this.scene.add.container(0, CONTAINER_HEIGHT / 2 - 40);
|
this.buffContainer = this.scene.add.container(0, CONTAINER_HEIGHT / 2 - 40);
|
||||||
this.updateBuffs = createSpawnUpdate(
|
this.updateBuffs = createSpawnUpdate(
|
||||||
new BuffSpawner(this.scene, this.buffContainer),
|
new BuffSpawner(this.hostScene, this.buffContainer),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!this.currentIsPlayer) {
|
if (!this.currentIsPlayer) {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,17 @@
|
||||||
import Phaser from "phaser";
|
|
||||||
import type { Spawner } from "boardgame-phaser";
|
import type { Spawner } from "boardgame-phaser";
|
||||||
import { spawnEffect } from "boardgame-phaser";
|
import { spawnEffect } from "boardgame-phaser";
|
||||||
import type { CombatState, CombatEntity, EnemyEntity } from "boardgame-core/samples/slay-the-spire-like";
|
import type { CombatState } from "boardgame-core/samples/slay-the-spire-like";
|
||||||
import { CombatUnitContainer, type CombatUnitData } from "./CombatUnitContainer";
|
import {
|
||||||
|
CombatUnitContainer,
|
||||||
|
type CombatUnitData,
|
||||||
|
} from "./CombatUnitContainer";
|
||||||
|
import { GameHostScene } from "boardgame-phaser";
|
||||||
|
|
||||||
export class CombatUnitSpawner implements Spawner<CombatUnitData, CombatUnitContainer> {
|
export class CombatUnitSpawner implements Spawner<
|
||||||
constructor(private scene: Phaser.Scene) {}
|
CombatUnitData,
|
||||||
|
CombatUnitContainer
|
||||||
|
> {
|
||||||
|
constructor(private scene: GameHostScene<CombatState>) {}
|
||||||
|
|
||||||
*getData(): Iterable<CombatUnitData> {
|
*getData(): Iterable<CombatUnitData> {
|
||||||
const combat = this.getCombatState();
|
const combat = this.getCombatState();
|
||||||
|
|
@ -70,6 +76,6 @@ export class CombatUnitSpawner implements Spawner<CombatUnitData, CombatUnitCont
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createCombatUnitSpawner(scene: Phaser.Scene) {
|
export function createCombatUnitSpawner(scene: GameHostScene<CombatState>) {
|
||||||
return spawnEffect(new CombatUnitSpawner(scene));
|
return spawnEffect(new CombatUnitSpawner(scene));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue