Files
cocos/assets/scripts/visual/EntityDisplayRefs.ts
刘宇飞 d393302388 Complete Cocos Creator port with level bundles, themes, and tooling.
Adds level prefabs, theme assets, audio, extensions, and deployment scripts for the Unity WebGL migration.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 15:30:58 +08:00

158 lines
5.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Size, SpriteFrame } from 'cc';
import { CELL_PIXEL } from '../core/GridConstants';
import { getThemeEntityDisplayCellBoxes } from '../theme/ThemeDatabase';
/** 内置默认themes-database 未加载时使用) */
export const DEFAULT_ENTITY_CELL_BOX = {
player: { w: 0.68, h: 0.9 },
vehicle: { w: 0.96, h: 0.88 },
prop: { w: 0.52, h: 0.69 },
propGround: { w: 0.52, h: 0.69 },
} as const;
/** 装饰物相对可拾取物的缩放比 */
const PROP_DECOR_RATIO = 0.45 / 0.69;
const cellW = (ratio: number) => CELL_PIXEL * ratio;
const cellH = (ratio: number) => CELL_PIXEL * ratio;
export function getEntityCellBox(theme?: string) {
if (theme) return getThemeEntityDisplayCellBoxes(theme);
return { ...DEFAULT_ENTITY_CELL_BOX };
}
export const DEFAULT_PLAYER_ANCHOR_Y = 0.35;
/** Unity silu ship pivot y = 0.45 */
export const DEFAULT_VEHICLE_ANCHOR_Y = 0.45;
export function getEntityDisplaySizes(theme?: string) {
const box = getEntityCellBox(theme);
return {
player: {
width: cellW(box.player.w),
height: cellH(box.player.h),
},
vehicle: {
width: cellW(box.vehicle.w),
height: cellH(box.vehicle.h),
},
prop: {
width: cellW(box.prop.w),
height: cellH(box.prop.h),
},
propGround: {
width: cellW(box.propGround.w),
height: cellH(box.propGround.h),
},
prop_decor: {
width: cellW(box.prop.w) * PROP_DECOR_RATIO,
height: cellH(box.prop.h) * PROP_DECOR_RATIO,
},
};
}
/** @deprecated 使用 getEntityDisplaySizes(theme) */
export const ENTITY_CELL_BOX = DEFAULT_ENTITY_CELL_BOX;
/** @deprecated 使用 getEntityDisplaySizes(theme) */
export const SANXING_ENTITY_DISPLAY = {
get player() { return getEntityDisplaySizes().player; },
get vehicle() { return getEntityDisplaySizes().vehicle; },
get prop() { return getEntityDisplaySizes().prop; },
get prop_decor() { return getEntityDisplaySizes().prop_decor; },
};
export type EntityDisplayKind = 'player' | 'vehicle' | 'prop' | 'propGround' | 'prop_decor';
type SpawnKindLike = 'player' | 'vehicle' | 'prop' | 'prop_decor';
export function entityPlaceholderSize(kind: SpawnKindLike, theme?: string): { width: number; height: number } {
const sizes = getEntityDisplaySizes(theme);
if (kind === 'prop_decor') return sizes.prop_decor;
if (kind === 'prop') return sizes.prop;
if (kind === 'vehicle') return sizes.vehicle;
return sizes.player;
}
export function spriteOriginalSize(sf: SpriteFrame): { width: number; height: number } {
const rect = sf.rect;
if (rect.width > 0 && rect.height > 0) {
return { width: rect.width, height: rect.height };
}
let w = sf.originalSize?.width ?? 0;
let h = sf.originalSize?.height ?? 0;
if (w <= 0 || h <= 0) {
w = sf.texture?.width ?? 1;
h = sf.texture?.height ?? 1;
}
return { width: Math.max(1, w), height: Math.max(1, h) };
}
/** 等比缩放完整落入主题包围盒contain */
export function fitEntityDisplaySize(
kind: EntityDisplayKind,
sf: SpriteFrame,
scaleMul = 1,
theme?: string,
): { width: number; height: number; anchorX: number; anchorY: number } {
const s = computeEntityUniformScale(kind, sf, scaleMul, theme);
return sizeFromUniformScale(sf, s, kind, theme);
}
/** 单帧 contain 缩放系数(序列动画各帧共用,避免画布尺寸不同导致比例跳变) */
export function computeEntityUniformScale(
kind: EntityDisplayKind,
sf: SpriteFrame,
scaleMul = 1,
theme?: string,
): number {
const mul = Math.max(0.1, Math.min(4, scaleMul));
const { width: ow, height: oh } = spriteOriginalSize(sf);
const ref = getEntityDisplaySizes(theme)[kind];
return Math.min(ref.width * mul / ow, ref.height * mul / oh);
}
export function sizeFromUniformScale(
sf: SpriteFrame,
uniformScale: number,
kind: EntityDisplayKind = 'player',
theme?: string,
): { width: number; height: number; anchorX: number; anchorY: number } {
const { width: ow, height: oh } = spriteOriginalSize(sf);
let anchorY = 0;
if (kind === 'player') {
anchorY = DEFAULT_PLAYER_ANCHOR_Y;
} else if (kind === 'vehicle') {
anchorY = DEFAULT_VEHICLE_ANCHOR_Y;
} else if (kind !== 'prop' && kind !== 'propGround' && kind !== 'prop_decor') {
anchorY = DEFAULT_PLAYER_ANCHOR_Y;
}
return {
width: ow * uniformScale,
height: oh * uniformScale,
anchorX: 0.5,
anchorY,
};
}
/**
* 序列帧脚点对齐:各帧保留原始比例,仅调 anchorY。
* 脚离画布底边像素一致时anchorY = baseAnchorY * refOh / oh
*/
export function playerSeqFrameAnchorY(
sf: SpriteFrame,
refSf: SpriteFrame,
baseAnchorY = DEFAULT_PLAYER_ANCHOR_Y,
): number {
const oh = Math.max(1, spriteOriginalSize(sf).height);
const refOh = Math.max(1, spriteOriginalSize(refSf).height);
if (oh === refOh) return baseAnchorY;
return baseAnchorY * refOh / oh;
}
/** 动态 SpriteFrame 补全 originalSizeImageAsset 直载时 Cocos 默认为 0 */
export function ensureSpriteFrameSize(sf: SpriteFrame, width: number, height: number) {
if (sf.originalSize.width > 0 && sf.originalSize.height > 0) return;
sf.originalSize = new Size(Math.max(1, width), Math.max(1, height));
}