跳到主要内容

瓦片地图与地形

地图是 整数网格 + 图集 + 视锥/视口裁剪,不是每块一张 DOM <img> 再开 GL。格子 id、碰撞、chunk 脏标记留在 CPU;GPU 吃图集纹理和可见 chunk 的 instance / mesh。世界单位 = 瓦片边长(或高度图采样间距);相机只提供 viewProj。

1. 场景怎么切

CPU:cells[] / chunk 脏位 / 碰撞层
↓ 可见 range 或视锥
GPU:一张 atlas + 可见格实例(或 chunk 静态 mesh)

2D 正交先读 Canvas Tilemap 的网格心智,光栅器换成实例化。不要每格 drawArrays,也不要把瓦片当 DOM。

export type TilesetGpu = {
texture: WebGLTexture;
tileWidth: number;
tileHeight: number;
columns: number;
};

export type TilemapDoc = {
columns: number;
rows: number;
cells: Uint16Array;
};

export function cellAt(map: TilemapDoc, tx: number, ty: number): number {
if (tx < 0 || ty < 0 || tx >= map.columns || ty >= map.rows) return 0;
return map.cells[ty * map.columns + tx] ?? 0;
}

export function visibleRange(
camera: { x: number; y: number; scale: number },
cssW: number,
cssH: number,
map: TilemapDoc,
tileW: number,
tileH: number,
): { tx0: number; ty0: number; tx1: number; ty1: number } {
const x1 = camera.x + cssW * camera.scale;
const y1 = camera.y + cssH * camera.scale;
return {
tx0: Math.max(0, Math.floor(Math.min(camera.x, x1) / tileW) - 1),
ty0: Math.max(0, Math.floor(Math.min(camera.y, y1) / tileH) - 1),
tx1: Math.min(map.columns - 1, Math.ceil(Math.max(camera.x, x1) / tileW) + 1),
ty1: Math.min(map.rows - 1, Math.ceil(Math.max(camera.y, y1) / tileH) + 1),
};
}

可见 40×25 打进 instance(世界 xy + atlas UV),一次 drawArraysInstanced。静态地面按 16×16 烘 chunk mesh,改一块只废对应 chunk。动画格、可破坏墙不要进静态 chunk。

适用:正交 RPG、俯视地图、高度图地形的地表。
不适用:每瓦片独立 DOM 标注(那是 overlay);矢量平面图可编辑拓扑(SVG 地图)。

2. 哪层必须 CPU,哪层进 GPU

归属
gid 网格、碰撞、寻路CPU 文档,不要从像素读「能不能走」
可见 range、chunk 脏CPU,相机/改砖时
atlas、chunk VBO、地形 height 纹理GPU 投影
POI 名称DOM,见 Overlay

图集一次 texStorage2D,夹紧 MAX_TEXTURE_SIZE,斜视地面开各向异性,见 纹理。像素风关线性过滤;整数缩放才不抖。缝:对相机平移在 drawing buffer 像素上 snap 一次,不要每格 round。

高度图地形:CPU 持有分辨率与世界间距;GPU 顶点或 VS 采样 height 纹理。碰撞用同一份高度采样的 CPU 副本或降采样,不要 readPixels 深度缓冲当物理。视锥裁剪在 CPU 对 chunk AABB;不要为每块瓦片开 draw。

跨源图集无 CORS 会 taint,之后导出失败,见 CORS。WebGPU 只在要 GPU-driven chunk 剔除时探测;生产默认 WebGL2 合批。

3. 失败形态

症状原因
格子间 1px 缝浮点相机 + 线性采样,未 snap
地图越大越卡没按可见 range 画,或每格一次 draw
真机黑块超过 MAX_TEXTURE_SIZE,或压缩格式未探测
角色陷进墙碰撞读了颜色/深度
破坏一块全图闪整张地表一张 mesh,没 chunk

权威资料

核对日期:2026-08-26