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>
This commit is contained in:
2026-06-16 15:30:58 +08:00
parent cba5105908
commit d393302388
6248 changed files with 17322729 additions and 11036 deletions

View File

@@ -3,36 +3,57 @@ import { Direction, GridType } from '../core/Define';
import { EventManager, EventType } from '../core/EventManager';
import { JsBridge } from '../bridge/JsBridge';
import { GameManager } from '../manager/GameManager';
import { scaledMoveSpeed, UNITY_VEHICLE_MOVE_SPEED } from '../core/GridConstants';
import { Movement } from '../gameplay/Movement';
import { PlayerController, ExternalDataList } from './PlayerController';
import { syncRidePositions } from './RideLink';
import { VisualAssets } from '../visual/VisualAssets';
const { ccclass } = _decorator;
/**
* 载具移动(对齐 Unity VehicleController
* - 仅能在空地移动None↔None、Ride→None
* - 有骑手时每帧同步玩家;落格更新 Ride 注册
*/
@ccclass('VehicleController')
export class VehicleController extends Movement {
private player: PlayerController | null = null;
onLoad() {
this.moverRole = 'vehicle';
this.moveSpeed = scaledMoveSpeed(UNITY_VEHICLE_MOVE_SPEED);
this.moveState = 0;
}
/** 不调用 super.start(),避免 component.start 晚于 LevelInit 绑定后再次用 spawn 方向刷贴图 */
start() {
super.start();
this.syncCommittedCellFromPosition();
if (this.player) {
this.syncFacingFromRider();
}
this.setIcon();
}
setPlayer(p: PlayerController | null) {
this.player = p;
if (!p) return;
if (p.direction !== this.direction) {
super.setDirection(p.direction);
}
this.setIcon();
}
getPlayer(): PlayerController | null {
return this.player;
}
setPosition(pos: Vec3) {
this.node.setPosition(pos);
}
setName(name: string) {
/* 可挂 Label 显示名称 */
setName(_name: string) {
/* 可挂 Label */
}
override setDirection(dir: Direction) {
@@ -44,45 +65,95 @@ export class VehicleController extends Movement {
Movement.callEach = false;
}
private entityVisualOptions() {
const gm = GameManager.instance;
const levelTheme = gm?.getCurLevel()?.theme?.trim();
return {
...(gm?.getEntityVisualOptions() ?? {}),
theme: levelTheme ?? gm?.uiStyle ?? 'default',
};
}
/** 载具四向贴图:有骑手时跟随骑手 direction转向时 setIcon 直接换图 */
private iconDirection(): Direction {
return this.player?.direction ?? this.direction;
}
setIcon() {
const style = GameManager.instance?.uiStyle ?? 'default';
VisualAssets.applyVehicleSprite(this.node, this.direction, style);
const dir = this.iconDirection();
if (this.player && dir !== this.direction) {
super.setDirection(dir);
}
VisualAssets.applyVehicleIconForDirection(
this.node,
dir,
this.entityVisualOptions(),
);
this.player?.syncRideMountVisual();
}
/** 骑乘时逻辑朝向与骑手保持一致Scratch 回传等) */
private syncFacingFromRider() {
if (!this.player || this.player.direction === this.direction) return;
super.setDirection(this.player.direction);
}
/** 主题 / 软重置后按当前逻辑朝向重刷贴图 */
refreshIcon() {
this.syncFacingFromRider();
this.setIcon();
}
override callJump() {
/* 载具不可跳跃 */
}
protected override syncRideAfterMoveStep() {
if (this.player) syncRidePositions('vehicle', this.player, this);
}
protected onMoveNextSet(isJump: boolean) {
if (Movement.callEach) return;
Movement.callEach = true;
this.player?.syncFromVehicle(this.targetGridType, isJump);
if (this.player) {
this.player.setTargetGridType(this.targetGridType);
this.player.syncFromVehicle(this.targetGridType, isJump);
}
Movement.callEach = false;
}
protected onMoving() {
if (this.player) syncRidePositions('vehicle', this.player, this);
if (Movement.callEach) return;
Movement.callEach = true;
if (this.player) {
this.player.onMoving();
this.player.setPosition(this.node.worldPosition);
}
this.player?.onMoving();
Movement.callEach = false;
}
protected onMoveToTarget() {
GameManager.instance!.removeObj(this.lastPosition);
GameManager.instance!.addObj(this.node.worldPosition, GridType.Ride, this.node);
if (Movement.callEach) return;
Movement.callEach = true;
if (this.player) {
this.player.setPosition(this.node.worldPosition);
this.player.syncMoveToTargetFromVehicle();
this.commitLandingCell();
const gm = GameManager.instance!;
gm.removeObj(this.lastPosition);
if (this.committedCell) {
gm.addObjAtCell(this.committedCell, GridType.Ride, this.node);
} else {
gm.addObj(this.node.position, GridType.Ride, this.node);
}
Movement.callEach = false;
this.externalCall();
if (!Movement.callEach && this.player) {
Movement.callEach = true;
this.player.setRideBaseFromVehicle(this.node.position);
if (this.committedCell) this.player.shareCommittedCell(this.committedCell);
this.player.syncMoveToTargetFromVehicle();
Movement.callEach = false;
}
if (this.step === 0) this.externalCall();
}
protected onMoveFail(isJump: boolean) {
if (!GameManager.instance!.multMode) {
EventManager.dispatch(EventType.InputEnd, GameManager.instance!.gameState);
}
protected onMoveFail(_isJump: boolean) {
this.externalCall();
EventManager.dispatch(EventType.InputEnd, GameManager.instance!.gameState);
}
callVehicleInfo() {
@@ -92,23 +163,22 @@ export class VehicleController extends Movement {
externalCall() {
const gm = GameManager.instance!;
const list: ExternalDataList = { direction: this.direction, externalDatas: [] };
const self = gm.worldToCell(this.node.worldPosition);
const sample = this.getGridSamplePosition();
const self = gm.worldToCell(sample);
list.externalDatas.push({
position: { x: self.x, y: self.y, z: 0 },
gridType: this.curGrid,
direction: 'self',
});
for (let d = Direction.North; d <= Direction.West; d++) {
const wp = gm.nextGridPosition(this.node.worldPosition, d);
const wp = gm.nextGridPosition(sample, d);
const cell = gm.worldToCell(wp);
list.externalDatas.push({
position: { x: cell.x, y: cell.y, z: 0 },
gridType: gm.calculateNextGridType(this.node.worldPosition, d),
gridType: gm.calculateNextGridType(sample, d),
direction: gm.getRelativePosition(this.direction, d),
});
}
const json = JSON.stringify(list);
if (gm.multMode) JsBridge.call(`process${this.node.name}`, json);
else JsBridge.call('processVehicleData', json);
JsBridge.call('processVehicleData', JSON.stringify(list));
}
}