Adds level prefabs, theme assets, audio, extensions, and deployment scripts for the Unity WebGL migration. Co-authored-by: Cursor <cursoragent@cursor.com>
608 lines
22 KiB
TypeScript
608 lines
22 KiB
TypeScript
import { _decorator, AudioSource, Vec3 } from 'cc';
|
||
import { Direction, GameState, GridType, MoveState, MoverRole, Skin, skinToTheme, themeToSkin } from '../core/Define';
|
||
import { EventManager, EventType } from '../core/EventManager';
|
||
import { JsBridge } from '../bridge/JsBridge';
|
||
import { GameAudio } from '../audio/GameAudio';
|
||
import { GameManager } from '../manager/GameManager';
|
||
import { Movement } from '../gameplay/Movement';
|
||
import { vehicleFollowsLandingTile, canStayMountedAfterLanding, moverRoleForLandingCell, shouldDismountOnTile } from '../gameplay/MoveRules';
|
||
import { VehicleController } from './VehicleController';
|
||
import { PropController } from './PropController';
|
||
import { findVehicleAtCell, tryLinkPlayerVehicle } from './RideLink';
|
||
import { PlayerAction } from '../visual/PlayerAnimPaths';
|
||
import { PlayerActionAnimator } from '../visual/PlayerActionAnimator';
|
||
import { scaledJumpArcOffset, scaledMoveSpeed, UNITY_PLAYER_MOVE_SPEED, CELL_PIXEL, PROP_COLLECT_MOVE_PROGRESS, PROP_COLLECT_TOUCH_RADIUS } from '../core/GridConstants';
|
||
import { cellToWorldCenter } from '../core/GridCoords';
|
||
import { getThemePlayerRideYOffset } from '../theme/ThemeDatabase';
|
||
import {
|
||
entityWorldPositionForRole,
|
||
moverLogicalGridPositionForRole,
|
||
worldToMoverCellForRole,
|
||
} from '../level/EntitySpawnPlacement';
|
||
import { forEachLevelEntityNode } from '../level/TileLayout';
|
||
|
||
const { ccclass } = _decorator;
|
||
|
||
export interface ExternalData {
|
||
position: { x: number; y: number; z: number };
|
||
gridType: number;
|
||
direction: string;
|
||
}
|
||
|
||
export interface ExternalDataList {
|
||
direction: number;
|
||
externalDatas: ExternalData[];
|
||
}
|
||
|
||
export interface ExternalResult {
|
||
isWin: boolean;
|
||
stepNum: number;
|
||
direction: number;
|
||
isInputEnd: boolean;
|
||
}
|
||
|
||
/**
|
||
* 玩家移动(对齐 Unity PlayerController)
|
||
* - 玩家节点为位移主体
|
||
* - 骑乘:仅目标空地联动载具;落砖下车;视觉 Y 抬高
|
||
* - 跳跃:只能落到 Jump 砖(MoveRules + moveCondition)
|
||
*/
|
||
@ccclass('PlayerController')
|
||
export class PlayerController extends Movement {
|
||
coins = 0;
|
||
private vehicle: VehicleController | null = null;
|
||
private sendFinally = false;
|
||
private animator: PlayerActionAnimator | null = null;
|
||
private sfxSource: AudioSource | null = null;
|
||
|
||
onLoad() {
|
||
this.moverRole = 'player';
|
||
this.moveSpeed = scaledMoveSpeed(UNITY_PLAYER_MOVE_SPEED);
|
||
this.animator = this.node.getComponent(PlayerActionAnimator)
|
||
?? this.node.addComponent(PlayerActionAnimator);
|
||
this.sfxSource = this.node.getComponent(AudioSource) ?? this.node.addComponent(AudioSource);
|
||
EventManager.register(EventType.LevelInit, this.onLevelInit);
|
||
EventManager.register(EventType.InputEnd, this.onInputEnd);
|
||
this.coins = 0;
|
||
}
|
||
|
||
onDestroy() {
|
||
EventManager.remove(EventType.LevelInit, this.onLevelInit);
|
||
EventManager.remove(EventType.InputEnd, this.onInputEnd);
|
||
}
|
||
|
||
start() {
|
||
super.start();
|
||
if (this.node.name === 'Player' && GameManager.instance) {
|
||
this.callChangeSkin(GameManager.instance.playerSkin);
|
||
} else {
|
||
this.animator?.setAction(this.animator?.getAction() ?? PlayerAction.Idle);
|
||
}
|
||
}
|
||
|
||
// —— 骑乘视觉(逻辑坐标在载具甲板,显示抬高) ——
|
||
|
||
private rideYOffset(): number {
|
||
const gm = GameManager.instance;
|
||
const theme = gm?.getCurLevel()?.theme ?? gm?.uiStyle;
|
||
return getThemePlayerRideYOffset(theme);
|
||
}
|
||
|
||
private rideDeckAtCell(cell: Vec3): Vec3 {
|
||
const gm = GameManager.instance!;
|
||
const config = gm.getCurLevel();
|
||
const theme = config?.theme ?? gm.uiStyle;
|
||
return entityWorldPositionForRole(cell, config ?? undefined, theme, 'vehicle');
|
||
}
|
||
|
||
/** 步行上载具时的落点(甲板 + 骑乘视觉 Y,避免到格后再闪一下) */
|
||
private rideMountWorldPosition(cell: Vec3): Vec3 {
|
||
const deck = this.rideDeckAtCell(cell);
|
||
return new Vec3(deck.x, deck.y + this.rideYOffset(), deck.z);
|
||
}
|
||
|
||
private applyMountVisualAtDeck(deck: Vec3) {
|
||
this.node.setPosition(deck.x, deck.y + this.rideYOffset(), deck.z);
|
||
}
|
||
|
||
/** 骑乘时逻辑甲板世界坐标(无视觉抬高) */
|
||
getRideDeckWorldPosition(): Vec3 {
|
||
if (this.vehicle) return this.vehicle.node.position.clone();
|
||
const p = this.node.position;
|
||
return new Vec3(p.x, p.y, p.z);
|
||
}
|
||
|
||
/** 是否处于「载具跟随」状态(仅 Idle 时锁定到甲板;移动中由 OnMoving 驱动载具) */
|
||
private isVehicleLinked(): boolean {
|
||
return !!this.vehicle && this.moveState !== MoveState.Moving;
|
||
}
|
||
|
||
snapToRideBase() {
|
||
if (!this.vehicle) return;
|
||
this.applyMountVisualAtDeck(this.vehicle.node.position);
|
||
}
|
||
|
||
applyRideVisual() {
|
||
if (!this.vehicle) return;
|
||
this.applyMountVisualAtDeck(this.vehicle.node.position);
|
||
}
|
||
|
||
/** 载具换向/flip 后重对齐骑乘高度 */
|
||
syncRideMountVisual() {
|
||
this.applyRideVisual();
|
||
}
|
||
|
||
setRideBaseFromVehicle(base: Vec3) {
|
||
this.node.setPosition(base);
|
||
this.applyRideVisual();
|
||
}
|
||
|
||
private syncVehicleToPlayerBase() {
|
||
if (!this.vehicle) return;
|
||
const p = this.node.position;
|
||
const off = this.rideYOffset();
|
||
this.vehicle.node.setPosition(p.x, p.y - off, p.z);
|
||
}
|
||
|
||
// —— 格子采样:骑乘期间始终以载具甲板为逻辑位置 ——
|
||
|
||
protected override getGridSamplePosition(): Vec3 {
|
||
// 优先 committedCell:空地格 Y 补偿会让 worldToMoverCell 误判邻格(如 (-1,-1)→(0,0))
|
||
if (this.committedCell) {
|
||
return cellToWorldCenter(this.committedCell);
|
||
}
|
||
const gm = GameManager.instance!;
|
||
const config = gm.getCurLevel();
|
||
const theme = config?.theme ?? gm.uiStyle;
|
||
if (this.vehicle) {
|
||
return moverLogicalGridPositionForRole(
|
||
this.vehicle.node.position,
|
||
config ?? undefined,
|
||
theme,
|
||
'vehicle',
|
||
);
|
||
}
|
||
return moverLogicalGridPositionForRole(
|
||
this.node.position,
|
||
config ?? undefined,
|
||
theme,
|
||
'player',
|
||
);
|
||
}
|
||
|
||
update(dt: number) {
|
||
if (this.isVehicleLinked()) {
|
||
this.applyMountVisualAtDeck(this.vehicle!.node.position);
|
||
}
|
||
super.update(dt);
|
||
}
|
||
|
||
protected override shouldApplyPlayerStandOffset(): boolean {
|
||
if (!this.vehicle) return true;
|
||
if (this.moveState !== MoveState.Moving) return false;
|
||
return !vehicleFollowsLandingTile(this.targetGridType);
|
||
}
|
||
|
||
setRideVehicle(v: VehicleController | null) {
|
||
if (this.vehicle && this.vehicle !== v) {
|
||
this.vehicle.setPlayer(null);
|
||
}
|
||
this.vehicle = v;
|
||
if (v) {
|
||
v.setPlayer(this);
|
||
Movement.callEach = true;
|
||
v.setDirection(this.direction);
|
||
Movement.callEach = false;
|
||
}
|
||
}
|
||
|
||
getRideVehicle(): VehicleController | null {
|
||
return this.vehicle;
|
||
}
|
||
|
||
// —— 上下车 ——
|
||
|
||
private unbindVehicle() {
|
||
if (!this.vehicle) return;
|
||
this.vehicle.setPlayer(null);
|
||
this.vehicle = null;
|
||
}
|
||
|
||
private alignWithVehicleBase() {
|
||
if (!this.vehicle || !this.committedCell) return;
|
||
const deck = this.rideDeckAtCell(this.committedCell);
|
||
this.vehicle.node.setPosition(deck);
|
||
this.vehicle.shareCommittedCell(this.committedCell);
|
||
this.node.setPosition(deck);
|
||
this.applyRideVisual();
|
||
}
|
||
|
||
/** Unity CheckIfCurIsRide:仅脚下为 Ride 动态格时绑定载具 */
|
||
private checkIfCurIsRide() {
|
||
if (this.curGrid !== GridType.Ride || !this.committedCell) return;
|
||
const gm = GameManager.instance!;
|
||
const vc = findVehicleAtCell(this.committedCell)
|
||
?? gm.getGameObjectAtCell(this.committedCell)?.getComponent(VehicleController);
|
||
if (!vc) return;
|
||
this.setRideVehicle(vc);
|
||
vc.setPlayer(this);
|
||
vc.setDirection(this.direction);
|
||
this.alignWithVehicleBase();
|
||
}
|
||
|
||
private onLevelInit = () => {
|
||
this.sendFinally = false;
|
||
this.coins = 0;
|
||
this.resetMoveRuntime();
|
||
this.unbindVehicle();
|
||
this.refreshVisual(PlayerAction.Idle);
|
||
this.syncCommittedCellFromPosition();
|
||
this.snapMoverToCellStand();
|
||
this.checkIfCurIsRide();
|
||
tryLinkPlayerVehicle(this);
|
||
};
|
||
|
||
private onInputEnd = () => {
|
||
const gm = GameManager.instance!;
|
||
this.externalCallResult(gm.allPropsCollected());
|
||
};
|
||
|
||
protected override snapMoverToCellStand() {
|
||
const gm = GameManager.instance;
|
||
if (!gm || !this.committedCell) return;
|
||
const config = gm.getCurLevel();
|
||
const theme = config?.theme ?? gm.uiStyle;
|
||
const landingLive = gm.calculateGridTypeAtCell(this.committedCell);
|
||
|
||
if (canStayMountedAfterLanding(landingLive)) {
|
||
const deck = this.rideDeckAtCell(this.committedCell);
|
||
if (this.vehicle) {
|
||
this.vehicle.node.setPosition(deck);
|
||
this.vehicle.shareCommittedCell(this.committedCell);
|
||
const mount = this.rideMountWorldPosition(this.committedCell);
|
||
this.node.setPosition(mount);
|
||
this.targetPosition.set(mount);
|
||
} else {
|
||
const mount = this.rideMountWorldPosition(this.committedCell);
|
||
this.node.setPosition(mount);
|
||
this.targetPosition.set(mount);
|
||
}
|
||
return;
|
||
}
|
||
|
||
const follow = !!this.vehicle && vehicleFollowsLandingTile(landingLive);
|
||
const deck = entityWorldPositionForRole(this.committedCell, config ?? undefined, theme, 'vehicle');
|
||
if (follow) {
|
||
this.vehicle!.node.setPosition(deck);
|
||
this.vehicle!.shareCommittedCell(this.committedCell);
|
||
const mount = this.rideMountWorldPosition(this.committedCell);
|
||
this.node.setPosition(mount);
|
||
this.targetPosition.set(mount);
|
||
return;
|
||
}
|
||
const pos = entityWorldPositionForRole(this.committedCell, config ?? undefined, theme, 'player');
|
||
this.node.setPosition(pos);
|
||
this.targetPosition.set(pos);
|
||
}
|
||
|
||
protected override resolveMoveStepOrigin(_targetCell: Vec3, landingGrid: GridType): Vec3 {
|
||
if (this.vehicle && vehicleFollowsLandingTile(landingGrid) && this.committedCell) {
|
||
return this.rideMountWorldPosition(this.committedCell);
|
||
}
|
||
// 下车:从骑乘视觉高度平滑移到砖面,勿先 snap 到甲板
|
||
if (this.vehicle && shouldDismountOnTile(landingGrid) && this.committedCell) {
|
||
return this.rideMountWorldPosition(this.committedCell);
|
||
}
|
||
return this.node.position.clone();
|
||
}
|
||
|
||
protected override resolveTargetWorldPosition(targetCell: Vec3, landingGrid: GridType): Vec3 {
|
||
if (this.vehicle && vehicleFollowsLandingTile(landingGrid)) {
|
||
return this.rideMountWorldPosition(targetCell);
|
||
}
|
||
const gm = GameManager.instance!;
|
||
const config = gm.getCurLevel();
|
||
const theme = config?.theme ?? gm.uiStyle;
|
||
const role: MoverRole = moverRoleForLandingCell('player', landingGrid, !!this.vehicle);
|
||
if (!this.vehicle && landingGrid === GridType.Ride) {
|
||
return this.rideMountWorldPosition(targetCell);
|
||
}
|
||
return entityWorldPositionForRole(targetCell, config ?? undefined, theme, role);
|
||
}
|
||
|
||
protected override onRotateComplete() {
|
||
if (this.vehicle) {
|
||
this.vehicle.setDirection(this.direction);
|
||
if (this.committedCell) this.alignWithVehicleBase();
|
||
}
|
||
}
|
||
|
||
/** 是否应拾取该道具(移动中需进入格子且接近触碰,避免起步即消失) */
|
||
canCollectProp(prop: PropController): boolean {
|
||
const propCell = prop.getSpawnCell();
|
||
if (!propCell) return false;
|
||
|
||
const committed = this.getCommittedCell();
|
||
if (!this.isMoving()) {
|
||
return !!committed && prop.matchesCell(committed);
|
||
}
|
||
|
||
const landing = this.getLandingCell();
|
||
if (!landing || !prop.matchesCell(landing)) return false;
|
||
|
||
const gm = GameManager.instance!;
|
||
const config = gm.getCurLevel();
|
||
const theme = config?.theme ?? gm.uiStyle;
|
||
const sample = worldToMoverCellForRole(this.node.position, config ?? undefined, theme, 'player');
|
||
if (!prop.matchesCell(sample)) return false;
|
||
|
||
if (this.getMoveStepProgress() < PROP_COLLECT_MOVE_PROGRESS) return false;
|
||
|
||
const touchR = CELL_PIXEL * PROP_COLLECT_TOUCH_RADIUS;
|
||
return Vec3.distance(this.node.position, prop.node.position) <= touchR;
|
||
}
|
||
|
||
private collectPropsOnApproach() {
|
||
const gm = GameManager.instance;
|
||
const level = gm?.curLevel;
|
||
if (!level) return;
|
||
forEachLevelEntityNode(level, (ch) => {
|
||
const prop = ch.getComponent(PropController);
|
||
if (!prop || !this.canCollectProp(prop)) return;
|
||
prop.collect(this);
|
||
});
|
||
}
|
||
|
||
protected onMoveNextSet(isJump: boolean) {
|
||
if (isJump && this.targetGridType === GridType.Jump && this.vehicle) {
|
||
this.unbindVehicle();
|
||
}
|
||
this.animator?.setAction(isJump ? PlayerAction.Jump : PlayerAction.Move);
|
||
if (isJump && this.targetGridType === GridType.Jump) {
|
||
this.targetPosition.y += scaledJumpArcOffset();
|
||
}
|
||
}
|
||
|
||
protected override playMoveAnim() {
|
||
this.animator?.setAction(PlayerAction.Move);
|
||
}
|
||
|
||
protected override syncRideAfterMoveStep() {
|
||
if (this.vehicle && vehicleFollowsLandingTile(this.targetGridType)) {
|
||
this.syncVehicleToPlayerBase();
|
||
}
|
||
}
|
||
|
||
onMoving() {
|
||
if (this.vehicle && vehicleFollowsLandingTile(this.targetGridType) && !Movement.callEach) {
|
||
this.syncVehicleToPlayerBase();
|
||
}
|
||
this.collectPropsOnApproach();
|
||
if (!this.sfxSource) return;
|
||
const action = this.animator?.getAction() ?? PlayerAction.Idle;
|
||
if (action === PlayerAction.Move) {
|
||
const key = vehicleFollowsLandingTile(this.targetGridType) && this.vehicle
|
||
? 'vehicleMove' : 'move';
|
||
void GameAudio.playSfxOnSource(this.sfxSource, key);
|
||
} else if (action === PlayerAction.Jump) {
|
||
void GameAudio.playSfxOnSource(this.sfxSource, 'jump');
|
||
}
|
||
}
|
||
|
||
protected onMoveFail(isJump: boolean) {
|
||
this.externalCallResult(false);
|
||
if (isJump) {
|
||
console.log(
|
||
`${this.node.name} 无法跳跃:跳跃只能落到 Jump 砖块(当前=${GridType[this.curGrid]} 前方=${GridType[this.nextGrid]})`,
|
||
);
|
||
} else if (this.curGrid === GridType.Jump) {
|
||
console.log(`${this.node.name} 在跳跃砖块上只能跳跃或转向,不能普通前后移动`);
|
||
} else {
|
||
console.log(
|
||
`${this.node.name} 无法移动(当前=${GridType[this.curGrid]} 前方=${GridType[this.nextGrid]} 后方=${GridType[this.lastGrid]})`,
|
||
);
|
||
}
|
||
}
|
||
|
||
protected onMoveToTarget() {
|
||
this.commitLandingCell();
|
||
this.finishMoveToTarget(true);
|
||
}
|
||
|
||
/** Unity OnMoveToTarget(bool) — 上下车 / 载具 Ride 注册 */
|
||
finishMoveToTarget(sendMsgOnStep0 = true) {
|
||
const gm = GameManager.instance!;
|
||
this.applyResultAction();
|
||
|
||
const cell = this.committedCell
|
||
?? worldToMoverCellForRole(
|
||
this.node.position,
|
||
gm.getCurLevel() ?? undefined,
|
||
gm.getCurLevel()?.theme ?? gm.uiStyle,
|
||
'player',
|
||
);
|
||
|
||
if (this.curGrid === GridType.Ride) {
|
||
const vc = findVehicleAtCell(cell)
|
||
?? gm.getGameObjectAtCell(cell)?.getComponent(VehicleController);
|
||
if (vc) {
|
||
this.setRideVehicle(vc);
|
||
vc.setPlayer(this);
|
||
vc.setDirection(this.direction);
|
||
}
|
||
} else if (this.curGrid !== GridType.None && this.vehicle) {
|
||
this.unbindVehicle();
|
||
}
|
||
|
||
PropController.tryCollectAtCell(cell, this);
|
||
|
||
if (this.vehicle) {
|
||
this.alignWithVehicleBase();
|
||
gm.removeObj(this.lastPosition);
|
||
const rideCell = this.committedCell ?? cell;
|
||
gm.addObjAtCell(rideCell, GridType.Ride, this.vehicle.node);
|
||
}
|
||
|
||
if (this.committedCell) {
|
||
this.targetGridType = gm.calculateGridTypeAtCell(this.committedCell);
|
||
}
|
||
|
||
if (this.step === 0 && sendMsgOnStep0) {
|
||
this.externalCall();
|
||
}
|
||
}
|
||
|
||
// —— 外观 / 外部接口 ——
|
||
|
||
private activeVisualTheme(): string {
|
||
const gm = GameManager.instance;
|
||
const levelTheme = gm?.getCurLevel()?.theme?.trim();
|
||
if (levelTheme) return levelTheme;
|
||
if (gm) return skinToTheme(gm.playerSkin);
|
||
return 'silu';
|
||
}
|
||
|
||
private visualOpts() {
|
||
const gm = GameManager.instance;
|
||
const theme = this.activeVisualTheme();
|
||
return { ...(gm?.getEntityVisualOptions() ?? {}), theme };
|
||
}
|
||
|
||
private refreshVisual(action = PlayerAction.Idle) {
|
||
this.animator?.configure(this.activeVisualTheme(), this.direction);
|
||
this.animator?.setAction(action);
|
||
}
|
||
|
||
/** 关卡加载后强制用地图主题(主站 CallChangeSkin 不得覆盖) */
|
||
applyLevelTheme(theme: string) {
|
||
const t = theme?.trim();
|
||
if (!t) return;
|
||
const gm = GameManager.instance;
|
||
if (gm) gm.playerSkin = themeToSkin(t);
|
||
this.animator?.configure(t, this.direction);
|
||
this.animator?.setAction(this.animator?.getAction() ?? PlayerAction.Idle, true);
|
||
this.reapplyCellStandPosition();
|
||
this.vehicle?.refreshIcon();
|
||
}
|
||
|
||
override setDirection(dir: Direction) {
|
||
super.setDirection(dir);
|
||
this.animator?.setDirection(dir, this.visualOpts());
|
||
if (Movement.callEach) return;
|
||
Movement.callEach = true;
|
||
this.vehicle?.setDirection(dir);
|
||
Movement.callEach = false;
|
||
}
|
||
|
||
callChangeSkin(n: number) {
|
||
if (n < 0 || n > Skin.sanxing) return;
|
||
if (GameManager.instance) GameManager.instance.playerSkin = n as Skin;
|
||
const levelTheme = GameManager.instance?.getCurLevel()?.theme?.trim();
|
||
if (levelTheme) {
|
||
this.applyLevelTheme(levelTheme);
|
||
return;
|
||
}
|
||
this.refreshVisual(this.animator?.getAction() ?? PlayerAction.Idle);
|
||
}
|
||
|
||
playCoinsAudio() {
|
||
GameAudio.playSfx('coins', this.node);
|
||
}
|
||
|
||
private applyResultAction() {
|
||
const gm = GameManager.instance;
|
||
if (!gm) return;
|
||
if (gm.gameState === GameState.ResultWin) {
|
||
this.animator?.setAction(PlayerAction.Win);
|
||
} else if (gm.gameState === GameState.ResultFail) {
|
||
this.animator?.setAction(PlayerAction.Fail);
|
||
} else {
|
||
this.animator?.setAction(PlayerAction.Idle);
|
||
}
|
||
}
|
||
|
||
callPlayerInfo() {
|
||
this.externalCall();
|
||
}
|
||
|
||
setName(name: string) {
|
||
this.vehicle?.setName(name);
|
||
}
|
||
|
||
getCoinsNum() {
|
||
JsBridge.call('coinsData', JSON.stringify({ coinsNum: this.coins, gameObjectName: this.node.name }));
|
||
}
|
||
|
||
addCoins() {
|
||
this.coins++;
|
||
this.playCoinsAudio();
|
||
}
|
||
|
||
setPosition(pos: Vec3) {
|
||
if (this.vehicle) {
|
||
this.setRideBaseFromVehicle(pos);
|
||
} else {
|
||
this.node.setPosition(pos);
|
||
}
|
||
}
|
||
|
||
setTargetGridType(t: GridType) {
|
||
this.targetGridType = t;
|
||
}
|
||
|
||
syncFromVehicle(targetGridType: GridType, isJump: boolean) {
|
||
this.targetGridType = targetGridType;
|
||
this.onMoveNextSet(isJump);
|
||
}
|
||
|
||
syncMoveToTargetFromVehicle() {
|
||
this.finishMoveToTarget(false);
|
||
}
|
||
|
||
externalCall() {
|
||
const gm = GameManager.instance!;
|
||
const list: ExternalDataList = { direction: this.direction, externalDatas: [] };
|
||
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(sample, d);
|
||
const cell = gm.worldToCell(wp);
|
||
list.externalDatas.push({
|
||
position: { x: cell.x, y: cell.y, z: 0 },
|
||
gridType: gm.calculateNextGridType(sample, d),
|
||
direction: gm.getRelativePosition(this.direction, d),
|
||
});
|
||
}
|
||
JsBridge.call('processData', JSON.stringify(list));
|
||
}
|
||
|
||
externalCallResult(isWin: boolean) {
|
||
const gm = GameManager.instance!;
|
||
if (this.node.name === 'Player') {
|
||
if (isWin) {
|
||
GameAudio.playSfx('success', this.node);
|
||
this.animator?.setAction(PlayerAction.Win);
|
||
} else {
|
||
GameAudio.playSfx('fail', this.node);
|
||
this.animator?.setAction(PlayerAction.Fail);
|
||
}
|
||
}
|
||
if (this.node.name === 'Player' && !this.sendFinally) {
|
||
JsBridge.call('externalResult', JSON.stringify({
|
||
isWin,
|
||
stepNum: gm.stepNum,
|
||
direction: this.direction,
|
||
isInputEnd: gm.isInputEnd,
|
||
} satisfies ExternalResult));
|
||
this.sendFinally = true;
|
||
}
|
||
if (gm.gameState !== GameState.Run) return;
|
||
gm.setGameState(isWin ? GameState.ResultWin : GameState.ResultFail);
|
||
}
|
||
}
|