同步主站 Cocos 当前工程:关卡资源、运行脚本与打包工具。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-12 14:40:34 +08:00
parent ae75fce80b
commit fac515a669
5609 changed files with 27744634 additions and 5653 deletions

View File

@@ -1,6 +1,6 @@
import {
_decorator, Button, Component, director, find,
Label, Node, Sprite, SpriteFrame, UITransform, view, AudioSource, Color, Layers,
Label, Node, Sprite, SpriteFrame, UITransform, view, Color, Layers,
tween, Tween, Vec3, Widget,
} from 'cc';
import { EventManager, EventType } from '../core/EventManager';
@@ -9,7 +9,7 @@ import { ViewController } from '../controller/ViewController';
import { LineGridRenderer } from '../gameplay/LineGridRenderer';
import { Movement } from '../gameplay/Movement';
import { GameAudio } from '../audio/GameAudio';
import { loadThemeCharacterPortrait, loadUIIcon, UIIconKey } from './UIStyleAssets';
import { loadThemeCharacterPortrait, loadUIIcon, loadDirectionIcon, UIIconKey } from './UIStyleAssets';
import {
getThemeHudIconScale, getThemePortraitFlipX, getThemePortraitScale,
} from '../theme/ThemeRegistry';
@@ -31,6 +31,12 @@ const UNITY_PAD_TOP = 20;
const UNITY_PORTRAIT = 194;
const UNITY_PAD_LEFT = 40;
const UNITY_PAD_TOP_PORTRAIT = 28;
/** Unity UIMain/ImageDirection401×176左下锚点距边 31 */
const UNITY_DIR_W = 401;
const UNITY_DIR_H = 176;
const UNITY_DIR_PAD = 31;
/** 左下角 X/Y 罗盘:先隐藏,改好后再打开 */
const SHOW_DIRECTION_GIZMO = false;
const REF_WIDTH = 2560;
const scaleUi = (v: number) => v * (DESIGN_WIDTH / REF_WIDTH);
@@ -42,6 +48,9 @@ const PAD_TOP = Math.round(scaleUi(UNITY_PAD_TOP));
const PORTRAIT_SIZE = Math.round(scaleUi(UNITY_PORTRAIT));
const PAD_LEFT = Math.round(scaleUi(UNITY_PAD_LEFT));
const PAD_TOP_PORTRAIT = Math.round(scaleUi(UNITY_PAD_TOP_PORTRAIT));
const DIR_W = Math.round(scaleUi(UNITY_DIR_W) * 2.5);
const DIR_H = Math.round(scaleUi(UNITY_DIR_H) * 2.5);
const PAD_DIR = Math.round(scaleUi(UNITY_DIR_PAD));
/** 点击时短暂放大再回弹 */
const BTN_POP_SCALE = 1.14;
@@ -68,6 +77,7 @@ export class UIMain extends Component {
private nodePlaySpeed: IconSlot | null = null;
private nodeAudio: IconSlot | null = null;
private portraitSprite: Sprite | null = null;
private directionSprite: Sprite | null = null;
private textLabel: Label | null = null;
private uiBuilt = false;
private readonly buttons: BtnLayout[] = [];
@@ -188,6 +198,7 @@ export class UIMain extends Component {
this.buildTextArea();
this.buildThemePortrait();
this.buildDirectionGizmo();
this.node.setSiblingIndex(this.node.parent!.children.length - 1);
this.layoutPanel();
view.on('canvas-resize', this.layoutPanel, this);
@@ -242,6 +253,78 @@ export class UIMain extends Component {
widget.alignMode = Widget.AlignMode.ON_WINDOW_RESIZE;
}
/** 对齐 Unity UIMain/ImageDirection每关左下角等距 X/Y 罗盘 */
private buildDirectionGizmo() {
const overlay = this.node.parent;
if (!overlay) return;
let root = overlay.getChildByName('ImageDirection');
if (!SHOW_DIRECTION_GIZMO) {
if (root?.isValid) root.active = false;
this.directionSprite = null;
return;
}
if (!root) {
root = new Node('ImageDirection');
root.parent = overlay;
}
root.layer = HUD_LAYER;
root.active = true;
const ui = root.getComponent(UITransform) ?? root.addComponent(UITransform);
ui.setAnchorPoint(0, 0);
ui.setContentSize(DIR_W, DIR_H);
const sprite = root.getComponent(Sprite) ?? root.addComponent(Sprite);
sprite.sizeMode = Sprite.SizeMode.CUSTOM;
sprite.color = new Color(255, 255, 255, 255);
this.directionSprite = sprite;
this.layoutDirectionGizmo();
this.scheduleOnce(() => this.layoutDirectionGizmo(), 0);
void this.refreshDirectionGizmo();
}
private layoutDirectionGizmo() {
const root = this.directionSprite?.node ?? this.node.parent?.getChildByName('ImageDirection');
if (!root?.isValid) return;
const debugBar = this.node.parent?.getChildByName('GameplayDebugBar');
const debugLift = debugBar?.active ? 64 : 0;
const widget = root.getComponent(Widget) ?? root.addComponent(Widget);
widget.isAlignBottom = true;
widget.isAlignLeft = true;
widget.isAlignTop = false;
widget.isAlignRight = false;
widget.left = PAD_DIR;
widget.bottom = PAD_DIR + debugLift;
widget.alignMode = Widget.AlignMode.ON_WINDOW_RESIZE;
widget.updateAlignment();
}
private async refreshDirectionGizmo() {
if (!this.directionSprite) return;
const sf = await loadDirectionIcon();
if (!sf || !this.directionSprite?.isValid) return;
this.applyDirectionSprite(sf);
}
private applyDirectionSprite(sf: SpriteFrame) {
if (!this.directionSprite?.isValid) return;
const root = this.directionSprite.node;
const ui = root.getComponent(UITransform) ?? root.addComponent(UITransform);
const { width: ow, height: oh } = spriteOriginalSize(sf);
const scale = Math.min(DIR_W / Math.max(ow, 1), DIR_H / Math.max(oh, 1));
const w = Math.max(1, Math.round(ow * scale));
const h = Math.max(1, Math.round(oh * scale));
ui.setAnchorPoint(0, 0);
ui.setContentSize(w, h);
this.directionSprite.spriteFrame = sf;
this.directionSprite.sizeMode = Sprite.SizeMode.CUSTOM;
this.layoutDirectionGizmo();
}
private layoutPanel = () => {
syncEmbeddedCamerasOrtho();
this.syncOverlaySize();
@@ -269,6 +352,7 @@ export class UIMain extends Component {
const portrait = this.portraitSprite?.node.parent
?? this.node.parent?.getChildByName('ImageBall');
portrait?.getComponent(Widget)?.updateAlignment();
this.layoutDirectionGizmo();
};
private ensureIconButton(name: string, onClick: () => void): IconSlot {
@@ -487,16 +571,12 @@ export class UIMain extends Component {
}
private applyAudioVolume() {
const vol = this.audioMute ? 0 : 1;
const scene = director.getScene();
if (!scene) return;
for (const src of scene.getComponentsInChildren(AudioSource)) {
src.volume = vol;
}
GameAudio.setMuted(this.audioMute);
}
private onLevelInit = () => {
this.setText('');
this.applyAudioVolume();
this.layoutDirectionGizmo();
};
}

View File

@@ -71,6 +71,13 @@ export function clearUIIconCache() {
frameCache.clear();
}
/** Unity UIMain/ImageDirection等距 X/Y 罗盘resources/textures/ui/ui-direction */
export async function loadDirectionIcon(): Promise<SpriteFrame | null> {
const sf = await loadSpriteAt('textures/ui/ui-direction');
if (!sf) console.warn('[UIStyleAssets] 方向罗盘贴图加载失败');
return sf;
}
/** 地图左上角角色肖像(优先 entities.portrait否则 playerFront */
export async function loadThemeCharacterPortrait(
options: EntityVisualOptions = {},