瓦片地图与地形
地图是 整数网格 + 图集 + 视锥/视口裁剪,不是每块一张 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