Add Wuzhang finale and lazy unit loading

This commit is contained in:
2026-06-26 22:53:47 +09:00
parent eb5328d6b1
commit 8ef5800584
13 changed files with 970 additions and 235 deletions

127
src/game/data/unitAssets.ts Normal file
View File

@@ -0,0 +1,127 @@
import Phaser from 'phaser';
export type UnitDirection = 'south' | 'east' | 'north' | 'west';
const unitWalkFrameCount = 4;
export const unitSheetFrameSize = 313;
const unitWalkFrameRate = 8;
const unitIdleFrameRate = 4;
const unitSheetRows: Record<UnitDirection, number> = {
south: 0,
east: 1,
north: 2,
west: 3
};
type UnitSheetAsset = {
key: string;
url: string;
actionKey: string;
actionUrl: string;
};
const unitSheetModules = import.meta.glob('../../assets/images/units/unit-*.png', {
eager: true,
import: 'default'
}) as Record<string, string>;
function textureKeyFromPath(path: string) {
const fileName = path.split('/').pop() ?? path;
return fileName.replace(/\.png$/i, '');
}
const unitSheetUrls = Object.fromEntries(
Object.entries(unitSheetModules).map(([path, url]) => [textureKeyFromPath(path), url])
);
const unitSheets = Object.entries(unitSheetUrls)
.filter(([key]) => !key.endsWith('-actions'))
.map(([key, url]) => ({
key,
url,
actionKey: `${key}-actions`,
actionUrl: unitSheetUrls[`${key}-actions`]
}))
.filter((sheet): sheet is UnitSheetAsset => Boolean(sheet.actionUrl))
.sort((left, right) => left.key.localeCompare(right.key));
const unitSheetAssetsByKey = new Map(unitSheets.map((sheet) => [sheet.key, sheet]));
export function unitTextureVariantKeys(baseKey: string) {
const prefix = `${baseKey}-`;
const keys = unitSheets
.map((sheet) => sheet.key)
.filter((key) => key === baseKey || key.startsWith(prefix))
.sort();
return keys.length > 0 ? keys : [baseKey];
}
export function loadUnitSheets(scene: Phaser.Scene, keys: Iterable<string>, onReady: () => void) {
const uniqueKeys = Array.from(new Set(keys));
const missingSheets = uniqueKeys
.filter((key) => !scene.textures.exists(key) || !scene.textures.exists(`${key}-actions`))
.map((key) => unitSheetAssetsByKey.get(key) ?? missingUnitSheet(key));
if (missingSheets.length <= 0) {
onReady();
return;
}
scene.load.once('complete', onReady);
missingSheets.forEach(({ key, url, actionKey, actionUrl }) => {
if (!scene.textures.exists(key)) {
scene.load.spritesheet(key, url, {
frameWidth: unitSheetFrameSize,
frameHeight: unitSheetFrameSize
});
}
if (!scene.textures.exists(actionKey)) {
scene.load.spritesheet(actionKey, actionUrl, {
frameWidth: unitSheetFrameSize,
frameHeight: unitSheetFrameSize
});
}
});
scene.load.start();
}
export function ensureUnitAnimations(scene: Phaser.Scene, keys: Iterable<string>) {
Array.from(new Set(keys)).forEach((key) => {
if (!scene.textures.exists(key)) {
return;
}
(['south', 'east', 'north', 'west'] as UnitDirection[]).forEach((direction) => {
const frames = Array.from({ length: unitWalkFrameCount }, (_, frameIndex) => ({
key,
frame: unitSheetRows[direction] * unitWalkFrameCount + frameIndex
}));
const walkAnimationKey = `${key}-walk-${direction}`;
const idleAnimationKey = `${key}-idle-${direction}`;
if (!scene.anims.exists(walkAnimationKey)) {
scene.anims.create({
key: walkAnimationKey,
frames,
frameRate: unitWalkFrameRate,
repeat: -1
});
}
if (!scene.anims.exists(idleAnimationKey)) {
scene.anims.create({
key: idleAnimationKey,
frames,
frameRate: unitIdleFrameRate,
repeat: -1
});
}
});
});
}
function missingUnitSheet(key: string): never {
throw new Error(`Missing unit sheet asset for ${key}`);
}