Files
cocos/assets/scripts/visual/VisualAssets.ts
刘宇飞 cba5105908 Initial Cocos Creator port of main-site Unity WebGL game.
Includes core gameplay, 600 exported levels, visual assets, web bridge, and bootstrap scene.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 14:57:46 +08:00

148 lines
5.6 KiB
TypeScript

import {
Node, Sprite, SpriteFrame, UITransform, resources, Color, Graphics,
ImageAsset, Texture2D,
} from 'cc';
import { Direction } from '../core/Define';
import { SpawnKind } from '../level/LevelTypes';
type SpriteKey = 'player_F' | 'player_B' | 'ship_F' | 'ship_B' | 'coin' | 'tile';
const PATHS: Record<SpriteKey, string> = {
player_F: 'textures/silu/player_F',
player_B: 'textures/silu/player_B',
ship_F: 'textures/silu/ship_F',
ship_B: 'textures/silu/ship_B',
coin: 'textures/ui/coin',
tile: 'textures/silu/Baseblock',
};
export class VisualAssets {
private static frames = new Map<SpriteKey, SpriteFrame>();
private static loading: Promise<void> | null = null;
static async preload(): Promise<void> {
if (this.loading) return this.loading;
this.loading = (async () => {
const keys = Object.keys(PATHS) as SpriteKey[];
const results = await Promise.all(keys.map((k) => this.loadOne(k)));
const ok = results.filter(Boolean).length;
console.log(`[VisualAssets] 贴图加载 ${ok}/${keys.length}`);
if (ok === 0) {
console.warn('[VisualAssets] 未加载到贴图,将使用色块。请确认 assets/resources/textures 已导入');
}
})().catch((e) => {
console.error('[VisualAssets] preload failed', e);
this.loading = null;
});
return this.loading;
}
private static loadOne(key: SpriteKey): Promise<boolean> {
if (this.frames.has(key)) return Promise.resolve(true);
const base = PATHS[key];
return new Promise((resolve) => {
resources.load(`${base}/spriteFrame`, SpriteFrame, (err, sf) => {
if (!err && sf) {
this.frames.set(key, sf);
resolve(true);
return;
}
resources.load(base, SpriteFrame, (err2, sf2) => {
if (!err2 && sf2) {
this.frames.set(key, sf2);
resolve(true);
return;
}
resources.load(base, ImageAsset, (err3, img) => {
if (!err3 && img) {
const tex = new Texture2D();
tex.image = img;
const frame = new SpriteFrame();
frame.texture = tex;
this.frames.set(key, frame);
resolve(true);
} else {
console.warn(`[VisualAssets] 加载失败: ${base}`, err3 || err2 || err);
resolve(false);
}
});
});
});
});
}
static getFrame(key: SpriteKey): SpriteFrame | null {
return this.frames.get(key) ?? null;
}
static applyPlayerSprite(node: Node, direction: Direction) {
const isFront = direction === Direction.South || direction === Direction.East;
const flipX = direction === Direction.West || direction === Direction.East;
const key: SpriteKey = isFront ? 'player_F' : 'player_B';
this.applySprite(node, key, flipX);
}
static applyVehicleSprite(node: Node, direction: Direction, uiStyle = 'default') {
void uiStyle;
const isFront = direction === Direction.South || direction === Direction.East;
const flipX = direction === Direction.West || direction === Direction.East;
const key: SpriteKey = isFront ? 'ship_F' : 'ship_B';
this.applySprite(node, key, flipX);
}
static setupEntityVisual(node: Node, kind: SpawnKind, direction?: Direction) {
if (kind === 'player') {
this.applyPlayerSprite(node, direction ?? Direction.South);
return;
}
if (kind === 'vehicle') {
this.applyVehicleSprite(node, direction ?? Direction.North);
return;
}
if (kind === 'prop') {
this.applySprite(node, 'coin', false, 0.85);
return;
}
if (kind === 'prop_decor') {
this.applySprite(node, 'coin', false, 0.6, 120);
}
}
/** Sprite 与 Graphics 不能共存,二选一 */
static applySprite(node: Node, key: SpriteKey, flipX: boolean, scale = 1, alpha = 255) {
let ui = node.getComponent(UITransform);
if (!ui) {
ui = node.addComponent(UITransform);
ui.setContentSize(48, 48);
}
const sf = this.getFrame(key);
const spr = node.getComponent(Sprite);
const g = node.getComponent(Graphics);
if (sf) {
if (g) g.destroy();
const sprite = spr || node.addComponent(Sprite);
sprite.spriteFrame = sf;
sprite.sizeMode = Sprite.SizeMode.CUSTOM;
const w = ui.contentSize.width * scale;
const h = ui.contentSize.height * scale;
ui.setContentSize(w, h);
sprite.color = new Color(255, 255, 255, alpha);
} else {
if (spr) spr.destroy();
const graphics = g || node.addComponent(Graphics);
graphics.fillColor = key === 'coin'
? new Color(255, 220, 0, alpha)
: new Color(80, 160, 255, alpha);
const w = ui.contentSize.width * 0.45 * scale;
graphics.clear();
graphics.rect(-w, -w, w * 2, w * 2);
graphics.fill();
}
const sx = flipX ? -Math.abs(scale) : Math.abs(scale);
node.setScale(sx, Math.abs(scale), 1);
}
}