/** * Unity 2D 绘制排序(对齐主站 GraphicsSettings + 预制体 SortingOrder + TilemapRenderer Individual) * * - GraphicsSettings: TransparencySortMode=CustomAxis, axis=(0, 1, -0.26) * - TilemapRenderer: SortingOrder=2, Mode=Individual, SortOrder=TopRight → 每格 -(cellX+cellY) * - nProp=1, Tilemap/Vehicle=2, Prop=3, Player=4 * - 仅贴图 WallBlock 可遮挡实体;可行走砖永在实体之下 * - 砖↔砖:格子深度(南 y 小 → 前,同 y 东 x 大 → 前)→ JumpBlock 抬高 Y → Individual order */ import { Vec3 } from 'cc'; import { CommonDefine } from '../core/Define'; import { cellToWorldCenter } from '../core/GridCoords'; /** Unity GraphicsSettings.asset m_TransparencySortAxis */ export const UNITY_TRANSPARENCY_AXIS = { x: 0, y: 1, z: -0.26 } as const; /** Unity SpriteRenderer / TilemapRenderer m_SortingOrder */ export const UNITY_SORTING_ORDER = { nProp: 1, tilemap: 2, vehicle: 2, prop: 3, player: 4, } as const; const ORDER_SCALE = 10_000; const DEPTH_SCALE = 100; /** JumpBlock 行走面比 Baseblock 抬高的最小 Y 差(px),超过则优先比 depthY */ const TILE_ELEVATION_Y_THRESHOLD = 12; export function isWallBlockTileName(tileName: string): boolean { return tileName === CommonDefine.BlockWall; } export function isWalkableTileName(tileName: string): boolean { return tileName === CommonDefine.BlockBase || tileName === CommonDefine.BlockJump; } /** Unity TilemapRenderer Individual + TopRight:baseOrder - (cellX+cellY) */ export function unityTileIndividualSortingOrder(cellX: number, cellY: number): number { return UNITY_SORTING_ORDER.tilemap - cellX - cellY; } export function unityTransparencyDepth(worldX: number, worldY: number, worldZ = 0): number { return worldX * UNITY_TRANSPARENCY_AXIS.x + worldY * UNITY_TRANSPARENCY_AXIS.y + worldZ * UNITY_TRANSPARENCY_AXIS.z; } export function unityCompositeSortKey( sortingOrder: number, depthWorldY: number, depthWorldZ = 0, bias = 0, ): number { const depth = unityTransparencyDepth(0, depthWorldY, depthWorldZ); return sortingOrder * ORDER_SCALE + depth * DEPTH_SCALE + bias; } export function unityCellCenterY(cellX: number, cellY: number): number { return cellToWorldCenter(new Vec3(cellX, cellY, 0)).y; } export interface UnitySortableTile { cellX: number; cellY: number; tileName: string; centerY: number; depthY: number; } export interface UnitySortableEntity { sortingOrder: number; cellX: number; cellY: number; centerY: number; depthY: number; isPlayer: boolean; isPickable: boolean; } /** 返回值 > 0 表示 (ax,ay) 应排在 (bx,by) 之后(更靠前) */ export function compareIsoDrawOrder(ax: number, ay: number, bx: number, by: number): number { const ka = ax + ay; const kb = bx + by; if (ka !== kb) return kb - ka; return ax - bx; } function wallFaceSamples(wallX: number, wallY: number): { x: number; y: number }[] { return [{ x: wallX, y: wallY - 1 }, { x: wallX - 1, y: wallY }]; } function compareWallActorIso( actor: { x: number; y: number }, wallX: number, wallY: number, ): number { let actorAhead = 0; for (const face of wallFaceSamples(wallX, wallY)) { const iso = compareIsoDrawOrder(actor.x, actor.y, face.x, face.y); if (iso < 0) return iso; if (iso > actorAhead) actorAhead = iso; } return actorAhead; } /** 返回值 > 0 实体更靠前;< 0 墙应挡住 */ function compareUnityEntityToWall( entity: UnitySortableEntity, tile: UnitySortableTile, ): number { const cell = { x: entity.cellX, y: entity.cellY }; for (const face of wallFaceSamples(tile.cellX, tile.cellY)) { if (face.x === cell.x && face.y === cell.y) return 1; } const tileOrder = unityTileIndividualSortingOrder(tile.cellX, tile.cellY); if (tileOrder > entity.sortingOrder) return -1; const wallIso = compareWallActorIso(cell, tile.cellX, tile.cellY); if (wallIso < 0) { if (tile.cellX === cell.x && tile.cellY === cell.y - 1) return -1; if (tile.cellY < cell.y) return 1; return -1; } const tileAhead = (tile.cellX + tile.cellY) - (cell.x + cell.y); if (wallIso > 0 && tileAhead > 0) return -1; if (wallIso > 0) return wallIso; const yCmp = Math.round(entity.depthY * DEPTH_SCALE) - Math.round(tile.depthY * DEPTH_SCALE); if (yCmp !== 0) return yCmp; if (tileAhead !== 0) return -tileAhead; return compareIsoDrawOrder(tile.cellX, tile.cellY, cell.x, cell.y); } /** 砖块格子深度:南侧(cellY 小)更靠前;同行西侧(cellX 小)更靠前 */ export function compareTileCellDepth(ax: number, ay: number, bx: number, by: number): number { if (ay !== by) return by - ay; return bx - ax; } /** * 砖↔砖:格子深度优先(下方砖挡上方砖),JumpBlock 抬高再比 depthY。 */ export function compareUnityTileToTile(a: UnitySortableTile, b: UnitySortableTile): number { const cellCmp = compareTileCellDepth(a.cellX, a.cellY, b.cellX, b.cellY); if (cellCmp !== 0) return cellCmp; const yCmp = Math.round(a.depthY * DEPTH_SCALE) - Math.round(b.depthY * DEPTH_SCALE); if (Math.abs(yCmp) >= TILE_ELEVATION_Y_THRESHOLD) return yCmp; return unityTileIndividualSortingOrder(a.cellX, a.cellY) - unityTileIndividualSortingOrder(b.cellX, b.cellY); } /** * 实体 vs 砖块 * 非 WallBlock 永在实体之下;仅墙砖可遮挡。 */ export function compareUnityEntityToTile( entity: UnitySortableEntity, tile: UnitySortableTile, ): number { if (!isWallBlockTileName(tile.tileName)) { return 1; } return compareUnityEntityToWall(entity, tile); } export function compareUnityEntityToEntity(a: UnitySortableEntity, b: UnitySortableEntity): number { const keyA = unityCompositeSortKey(a.sortingOrder, a.depthY); const keyB = unityCompositeSortKey(b.sortingOrder, b.depthY); if (keyA !== keyB) return keyA - keyB; const iso = (a.cellX + a.cellY) - (b.cellX + b.cellY); if (iso !== 0) return iso; if (a.isPlayer && !b.isPlayer) return 1; if (b.isPlayer && !a.isPlayer) return -1; return 0; }