Files
cocos/assets/scripts/level/UnityDrawSort.ts
2026-07-14 16:02:20 +08:00

187 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 等距绘制排序(重设计)
*
* 单一全序标量,无成环、无特判互打:
* frontKey = -(cellX + cellY) * CELL // 更小 x+y → 屏幕更靠下 → 更靠前
* + typeRank // 同对角条带内:地板 < 金币 < 载具 < 角色 < 墙
*
* 移动中采样拆开:
* - 角色:朝前落点 / 朝后起点
* - 载具:朝前起点(防浮到前砖上)/ 朝后落点(防途中比落点更靠前而不被下方砖挡)
* 贴墙南/西立面格上的实体:抬到该墙之上(露脚)。
* 角色永远盖过同条带载具。
*/
import { Vec3 } from 'cc';
import { CommonDefine } from '../core/Define';
import { cellToWorldCenter } from '../core/GridCoords';
export const UNITY_TRANSPARENCY_AXIS = { x: 0, y: 1, z: -0.26 } as const;
export const UNITY_SORTING_ORDER = {
nProp: 1,
tilemap: 2,
vehicle: 2,
prop: 3,
player: 4,
} as const;
const CELL = 100;
/** 同对角条带内层级:地板 < 金币 < 载具 < 角色 < 墙立面 */
const RANK_FLOOR = 0;
const RANK_PICKABLE = 20;
const RANK_VEHICLE = 30;
const RANK_PLAYER = 35;
const RANK_WALL = 40;
export function isWallBlockTileName(tileName: string): boolean {
return tileName === CommonDefine.BlockWall;
}
export function isWalkableTileName(tileName: string): boolean {
return tileName === CommonDefine.BlockBase || tileName === CommonDefine.BlockJump;
}
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 * 10_000 + depth * 100 + 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;
}
export function compareTileCellDepth(ax: number, ay: number, bx: number, by: number): number {
return compareIsoDrawOrder(ax, ay, bx, by);
}
function wallFaceSamples(wallX: number, wallY: number): { x: number; y: number }[] {
return [{ x: wallX, y: wallY - 1 }, { x: wallX - 1, y: wallY }];
}
function typeRankForTile(tileName: string): number {
return isWallBlockTileName(tileName) ? RANK_WALL : RANK_FLOOR;
}
function typeRankForEntity(entity: UnitySortableEntity): number {
if (entity.isPickable) return RANK_PICKABLE;
if (entity.isPlayer) return RANK_PLAYER;
return RANK_VEHICLE;
}
/** 越大越靠前(后绘制) */
export function isoDrawFrontKey(cellX: number, cellY: number, typeRank: number): number {
return -(cellX + cellY) * CELL + typeRank;
}
export function tileDrawFrontKey(tile: UnitySortableTile): number {
return isoDrawFrontKey(tile.cellX, tile.cellY, typeRankForTile(tile.tileName));
}
export function entityDrawFrontKey(entity: UnitySortableEntity): number {
return isoDrawFrontKey(entity.cellX, entity.cellY, typeRankForEntity(entity));
}
/**
* 若实体踩在某墙南/西立面上,应露在该墙前面。
* 返回需要超过的墙 frontKey否则 null。
*/
export function entityFaceLiftOverWall(
entity: UnitySortableEntity,
tile: UnitySortableTile,
): number | null {
if (!isWallBlockTileName(tile.tileName)) return null;
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 tileDrawFrontKey(tile);
}
}
return null;
}
/** 实体相对整张关卡的 frontKey含贴墙抬升 */
export function entityDrawFrontKeyWithWalls(
entity: UnitySortableEntity,
walls: UnitySortableTile[],
): number {
let key = entityDrawFrontKey(entity);
for (const w of walls) {
const lift = entityFaceLiftOverWall(entity, w);
if (lift != null && key <= lift) key = lift + 1;
}
return key;
}
export function compareUnityTileToTile(a: UnitySortableTile, b: UnitySortableTile): number {
const d = tileDrawFrontKey(a) - tileDrawFrontKey(b);
if (d !== 0) return d;
return a.cellX - b.cellX;
}
export function compareUnityEntityToEntity(a: UnitySortableEntity, b: UnitySortableEntity): number {
const d = entityDrawFrontKey(a) - entityDrawFrontKey(b);
if (d !== 0) return d;
if (a.isPlayer && !b.isPlayer) return 1;
if (b.isPlayer && !a.isPlayer) return -1;
return a.sortingOrder - b.sortingOrder;
}
/**
* 实体 vs 砖:统一用 frontKey。
* (保留函数供外部/测试调用;排序主路径用 build 里的 key
*/
export function compareUnityEntityToTile(
entity: UnitySortableEntity,
tile: UnitySortableTile,
): number {
if (!isWallBlockTileName(tile.tileName)) {
// 地板永在实体下
return 1;
}
const lift = entityFaceLiftOverWall(entity, tile);
const eKey = lift != null ? lift + 1 : entityDrawFrontKey(entity);
return eKey - tileDrawFrontKey(tile);
}