|
|
|
@ -2,6 +2,7 @@ import { _decorator, Camera, Collider2D, Component, Contact2DType, Director, ERi |
|
|
|
import { tetriNode } from '../tetriCard/tetriNode'; |
|
|
|
import { tetriNode } from '../tetriCard/tetriNode'; |
|
|
|
import { tetriFloorNode } from '../tetriCard/tetriFloorNode'; |
|
|
|
import { tetriFloorNode } from '../tetriCard/tetriFloorNode'; |
|
|
|
import { tetriWeaponCarrier } from '../tetriCard/tetriWeaponCarrier'; |
|
|
|
import { tetriWeaponCarrier } from '../tetriCard/tetriWeaponCarrier'; |
|
|
|
|
|
|
|
import { role } from '../tetriCard/role'; |
|
|
|
import { TetriType } from '../game/ConfigProjectData'; |
|
|
|
import { TetriType } from '../game/ConfigProjectData'; |
|
|
|
import { GEvent } from '../../mx/module/event/GEvent'; |
|
|
|
import { GEvent } from '../../mx/module/event/GEvent'; |
|
|
|
import { StatusType } from '../game/GameData'; |
|
|
|
import { StatusType } from '../game/GameData'; |
|
|
|
@ -132,7 +133,7 @@ export class tetriMap extends Component { |
|
|
|
highLineFollowSpeed: number = 10; |
|
|
|
highLineFollowSpeed: number = 10; |
|
|
|
/**缩放目标(镜头拉远;背景/地面 Sprite 用 1/scale 补偿铺满视口) */ |
|
|
|
/**缩放目标(镜头拉远;背景/地面 Sprite 用 1/scale 补偿铺满视口) */ |
|
|
|
private _scaleTarget: number = 1; |
|
|
|
private _scaleTarget: number = 1; |
|
|
|
/** 设计分辨率高度变化时短暂停物理 */ |
|
|
|
/** 设计分辨率高度变化后等待布局再同步刚体 */ |
|
|
|
private _physicsStabilizeScheduled = false; |
|
|
|
private _physicsStabilizeScheduled = false; |
|
|
|
private _lastDesignHeight = 0; |
|
|
|
private _lastDesignHeight = 0; |
|
|
|
/** 游戏相机(通过 UIManager 注入) */ |
|
|
|
/** 游戏相机(通过 UIManager 注入) */ |
|
|
|
@ -366,6 +367,7 @@ export class tetriMap extends Component { |
|
|
|
this._cameraTargetOrthoHeight = this._designHalfHeight; |
|
|
|
this._cameraTargetOrthoHeight = this._designHalfHeight; |
|
|
|
this._cameraTargetY = cam.node.position.y; |
|
|
|
this._cameraTargetY = cam.node.position.y; |
|
|
|
this._cameraTargetZ = cam.node.position.z; |
|
|
|
this._cameraTargetZ = cam.node.position.z; |
|
|
|
|
|
|
|
this._syncGameCameraToUiCamera(true); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (gg.data.doc.chapter == 1 && gg.data.getStatus(StatusType.IsShowPlayGuidePop) == 0) { |
|
|
|
if (gg.data.doc.chapter == 1 && gg.data.getStatus(StatusType.IsShowPlayGuidePop) == 0) { |
|
|
|
@ -1230,9 +1232,11 @@ export class tetriMap extends Component { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* B(收敛版):仅在「设计分辨率高度」变化时处理(预览切 750x1334↔750x1600)。 |
|
|
|
* B(收敛版): |
|
|
|
* 禁止局中 setResolutionPolicy / syncSceneToPhysics —— 二者会把坐标系重映射,导致图2错位。 |
|
|
|
* - 仅「设计高度」变化时处理(750×1334 ↔ 750×1600) |
|
|
|
* 仅窗口黑边变化(设计分辨率不变)时什么都不做,靠 A + 锁定 designHalfHeight 防塌。 |
|
|
|
* - 黑边/窗口变化:不处理 |
|
|
|
|
|
|
|
* - 双相机:GameCamera ortho 跟随 UICamera |
|
|
|
|
|
|
|
* - 刚体:Canvas 父级缩放后须 syncPositionToPhysics(禁止关物理 / rb.enabled) |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private _bindViewportResize(): void { |
|
|
|
private _bindViewportResize(): void { |
|
|
|
this._unbindViewportResize(); |
|
|
|
this._unbindViewportResize(); |
|
|
|
@ -1245,43 +1249,137 @@ export class tetriMap extends Component { |
|
|
|
screen.off('window-resize', this._onViewportResize, this); |
|
|
|
screen.off('window-resize', this._onViewportResize, this); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public stabilizeAfterViewportChange(): void { |
|
|
|
|
|
|
|
this._onViewportResize(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private _onViewportResize(): void { |
|
|
|
private _onViewportResize(): void { |
|
|
|
|
|
|
|
if (!gg.game?.isBattleContextActive?.()) return; |
|
|
|
|
|
|
|
|
|
|
|
const designH = view.getDesignResolutionSize().height; |
|
|
|
const designH = view.getDesignResolutionSize().height; |
|
|
|
if (!(designH > 0) || Math.abs(designH - this._lastDesignHeight) < 1) { |
|
|
|
if (!(designH > 0) || Math.abs(designH - this._lastDesignHeight) < 1) { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
const oldHalf = this._designHalfHeight > 0 ? this._designHalfHeight : (this._lastDesignHeight * 0.5); |
|
|
|
|
|
|
|
const newHalf = designH * 0.5; |
|
|
|
|
|
|
|
if (oldHalf > 0 && this._cameraBaseOrthoHeight > 0) { |
|
|
|
|
|
|
|
this._cameraBaseOrthoHeight *= newHalf / oldHalf; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this._designHalfHeight = newHalf; |
|
|
|
|
|
|
|
this._lastDesignHeight = designH; |
|
|
|
this._lastDesignHeight = designH; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this._syncGameCameraToUiCamera(true); |
|
|
|
|
|
|
|
this._scheduleViewportStabilize(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 设计高度变化后:GameCamera 与 UICamera 保持同一缩放比例,避免 GameRoot 横向偏移 */ |
|
|
|
|
|
|
|
private _syncGameCameraToUiCamera(snapNow = false): void { |
|
|
|
|
|
|
|
const uiCam = gg.ui?.UICamera; |
|
|
|
|
|
|
|
const gameCam = this._gameCamera?.isValid ? this._gameCamera : (gg.ui?.GameCamera ?? null); |
|
|
|
|
|
|
|
if (!uiCam?.isValid || !gameCam?.isValid) return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const uiOrtho = uiCam.orthoHeight; |
|
|
|
|
|
|
|
const lockedHalf = this._designHalfHeight; |
|
|
|
|
|
|
|
if (!(uiOrtho > 0) || !(lockedHalf > 0) || !(this._cameraBaseOrthoHeight > 0)) return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const zoom = Math.max(0.05, this._scaleTarget || 1); |
|
|
|
|
|
|
|
const gameToUi = this._cameraBaseOrthoHeight / lockedHalf; |
|
|
|
|
|
|
|
this._cameraTargetOrthoHeight = (uiOrtho * gameToUi) / zoom; |
|
|
|
|
|
|
|
this._cameraTargetY = (this._cameraTargetOrthoHeight - lockedHalf) / 2; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (snapNow) { |
|
|
|
|
|
|
|
gameCam.orthoHeight = this._cameraTargetOrthoHeight; |
|
|
|
|
|
|
|
const p = gameCam.node.position; |
|
|
|
|
|
|
|
gameCam.node.setPosition(p.x, this._cameraTargetY, this._cameraTargetZ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private _scheduleViewportStabilize(): void { |
|
|
|
if (this._physicsStabilizeScheduled) return; |
|
|
|
if (this._physicsStabilizeScheduled) return; |
|
|
|
this._physicsStabilizeScheduled = true; |
|
|
|
this._physicsStabilizeScheduled = true; |
|
|
|
// 不要开关全局 PhysicsSystem2D.enable:与激励视频等其它挂起互相覆盖,
|
|
|
|
// 等 Canvas/Widget 布局稳定后再对齐刚体
|
|
|
|
// 会导致 enable 永久为 false → 子弹碰撞失效、怪不掉血。此处只刹速度即可。
|
|
|
|
|
|
|
|
this.scheduleOnce(() => { |
|
|
|
this.scheduleOnce(() => { |
|
|
|
this._physicsStabilizeScheduled = false; |
|
|
|
this.scheduleOnce(() => { |
|
|
|
if (this._tetrisTable?.isValid) { |
|
|
|
this._physicsStabilizeScheduled = false; |
|
|
|
const rbs = this._tetrisTable.getComponentsInChildren(RigidBody2D) ?? []; |
|
|
|
this._resyncAllTetriRigidBodiesFromNodes(); |
|
|
|
for (let i = 0; i < rbs.length; i++) { |
|
|
|
this._syncGameCameraToUiCamera(true); |
|
|
|
const rb = rbs[i]; |
|
|
|
role.refreshFollowOffsetsAfterViewportChange(); |
|
|
|
if (!rb?.isValid || rb.type !== ERigidBody2DType.Dynamic) continue; |
|
|
|
}, 0); |
|
|
|
const t = this._findInParents(rb.node, (n) => n.getComponent(tetriNode)).comp; |
|
|
|
}, 0); |
|
|
|
if (!t?.isSettled) continue; |
|
|
|
} |
|
|
|
rb.linearVelocity = new Vec2(0, 0); |
|
|
|
|
|
|
|
rb.angularVelocity = 0; |
|
|
|
/** |
|
|
|
|
|
|
|
* 分辨率变化后:节点世界坐标已变,刚体仍停在旧位 → 碰撞全乱。 |
|
|
|
|
|
|
|
* 无重力 floor 无 RigidBody2D,须单独冷重启 PolygonCollider2D。 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private _resyncAllTetriRigidBodiesFromNodes(): void { |
|
|
|
|
|
|
|
const visited = new Set<Node>(); |
|
|
|
|
|
|
|
const pushRb = (rb: RigidBody2D) => { |
|
|
|
|
|
|
|
const body = (rb as { _body?: { syncPositionToPhysics?: (resetVel?: boolean) => void } })._body; |
|
|
|
|
|
|
|
body?.syncPositionToPhysics?.(false); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
const visit = (n: Node) => { |
|
|
|
|
|
|
|
if (!n?.isValid || visited.has(n)) return; |
|
|
|
|
|
|
|
visited.add(n); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const floor = n.getComponent(tetriFloorNode); |
|
|
|
|
|
|
|
if (floor) { |
|
|
|
|
|
|
|
if (floor.isSettled) { |
|
|
|
|
|
|
|
floor.refreshPhysicsCollidersAfterViewportChange(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
this._updateHighLine(); |
|
|
|
|
|
|
|
if (this._gameCamera?.isValid && this._cameraTargetOrthoHeight > 0) { |
|
|
|
const tn = n.getComponent(tetriNode); |
|
|
|
this._gameCamera.orthoHeight = this._cameraTargetOrthoHeight; |
|
|
|
const rb = n.getComponent(RigidBody2D) ?? n.getComponentInChildren(RigidBody2D); |
|
|
|
const camPos = this._gameCamera.node.position; |
|
|
|
if (rb?.isValid) { |
|
|
|
this._gameCamera.node.setPosition(camPos.x, this._cameraTargetY, this._cameraTargetZ); |
|
|
|
if (tn) { |
|
|
|
|
|
|
|
const falling = !tn.isSettled && !tn.isPhysicsFrozen; |
|
|
|
|
|
|
|
tn.syncRigidBodyPoseFromNode(!falling); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
pushRb(rb); |
|
|
|
|
|
|
|
rb.angularVelocity = 0; |
|
|
|
|
|
|
|
if (rb.type === ERigidBody2DType.Static || rb.type === ERigidBody2DType.Kinematic) { |
|
|
|
|
|
|
|
rb.linearVelocity = new Vec2(0, 0); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
const cols = n.getComponentsInChildren(PolygonCollider2D) ?? []; |
|
|
|
|
|
|
|
for (let i = 0; i < cols.length; i++) { |
|
|
|
|
|
|
|
this._coldRefreshCollider2D(cols[i]); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
const cols = n.getComponentsInChildren(PolygonCollider2D) ?? []; |
|
|
|
|
|
|
|
for (let i = 0; i < cols.length; i++) { |
|
|
|
|
|
|
|
this._coldRefreshCollider2D(cols[i]); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}, 0); |
|
|
|
|
|
|
|
|
|
|
|
const ch = n.children; |
|
|
|
|
|
|
|
for (let i = 0; i < ch.length; i++) visit(ch[i]); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (this._tetrisTable?.isValid) { |
|
|
|
|
|
|
|
for (const child of this._tetrisTable.children) visit(child); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (this.latestFallingNode?.isValid) visit(this.latestFallingNode); |
|
|
|
|
|
|
|
if (this._floorTaizi?.isValid) visit(this._floorTaizi); |
|
|
|
|
|
|
|
if (this._groundTable?.isValid) visit(this._groundTable); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this._refreshAllPinnedFloorsAfterViewport(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private _coldRefreshCollider2D(col: PolygonCollider2D): void { |
|
|
|
|
|
|
|
if (!col?.isValid) return; |
|
|
|
|
|
|
|
const was = col.enabled; |
|
|
|
|
|
|
|
col.enabled = false; |
|
|
|
|
|
|
|
col.enabled = was; |
|
|
|
|
|
|
|
if (typeof col.apply === 'function') col.apply(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 无重力板钉住后再冷启碰撞体,并消邻域竖直冲量,避免穿透/炸塔 */ |
|
|
|
|
|
|
|
private _refreshAllPinnedFloorsAfterViewport(): void { |
|
|
|
|
|
|
|
if (!this._tetrisTable?.isValid) return; |
|
|
|
|
|
|
|
const floors = this._tetrisTable.getComponentsInChildren(tetriFloorNode) ?? []; |
|
|
|
|
|
|
|
for (let i = 0; i < floors.length; i++) { |
|
|
|
|
|
|
|
const f = floors[i]; |
|
|
|
|
|
|
|
if (!f?.isSettled || !f.node?.isValid) continue; |
|
|
|
|
|
|
|
f.refreshPhysicsCollidersAfterViewportChange(); |
|
|
|
|
|
|
|
this._dampDynamicsNearFloorRegionImpl(f.node, 3, false); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private _resizeUIForScale(curScale: number): void { |
|
|
|
private _resizeUIForScale(curScale: number): void { |
|
|
|
@ -1734,7 +1832,10 @@ export class tetriMap extends Component { |
|
|
|
} |
|
|
|
} |
|
|
|
if (this._gameCamera?.isValid && this._cameraBaseOrthoHeight > 0) { |
|
|
|
if (this._gameCamera?.isValid && this._cameraBaseOrthoHeight > 0) { |
|
|
|
const halfH = this._designHalfHeight > 0 ? this._designHalfHeight : (gg.ui.height / 2); |
|
|
|
const halfH = this._designHalfHeight > 0 ? this._designHalfHeight : (gg.ui.height / 2); |
|
|
|
this._cameraTargetOrthoHeight = this._cameraBaseOrthoHeight / this._scaleTarget; |
|
|
|
const zoom = Math.max(0.05, this._scaleTarget || 1); |
|
|
|
|
|
|
|
const uiOrtho = gg.ui?.UICamera?.orthoHeight; |
|
|
|
|
|
|
|
const gameToUi = this._cameraBaseOrthoHeight / Math.max(1, halfH); |
|
|
|
|
|
|
|
this._cameraTargetOrthoHeight = (uiOrtho > 0 ? uiOrtho * gameToUi : this._cameraBaseOrthoHeight) / zoom; |
|
|
|
this._cameraTargetY = (this._cameraTargetOrthoHeight - halfH) / 2; |
|
|
|
this._cameraTargetY = (this._cameraTargetOrthoHeight - halfH) / 2; |
|
|
|
this._cameraTargetZ = 1000; |
|
|
|
this._cameraTargetZ = 1000; |
|
|
|
} |
|
|
|
} |
|
|
|
|