Files
EVEngine/docs/usr/modules/map.md
T

184 lines
10 KiB
Markdown
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.
# Tilemap模块
**脚本入口:** `eve.Map()`
创建或载入 TileLayer,设置瓦片、投影、图层并提交渲染;可用 Pathfinder 做格子寻路与群体 Flow Field,用 Fov 做动态视野与探索记忆。
## 基本用法
```squirrel
local map = eve.Map();
local layer = map.newLayerFromFile("maps/level.json");
layer.setTile(2, 3, 7);
map.update(dt);
map.render(gfx);
```
## 对象关系与调用时机
`Map` 聚合 TileLayer 并统一更新/渲染;TileLayer 保存网格、投影、origin、层级和图集资源。JSON loader 把 Tiled 数据映射到这些对象。寻路在格子索引空间进行,与投影解耦;需要世界坐标时对路径点调用 `tileToWorld*`。
## 目标导向指南
### 载入 Tiled 地图
把 JSON 和图集放在游戏目录,用 `newLayerFromFile(path)` 创建层;初始化后设置 origin、layer 和 visible。每帧 `map.update(dt)`,渲染时 `map.render(gfx)`。
### 运行时修改瓦片
先用地图坐标换算接口把世界位置转成格子,再 `setTile(x, y, gid)`;批量生成地图时先 resize,再填充,避免重复重建图层。0 通常表示空瓦片。改瓦片后若已创建 Pathfinder,调用 `syncFromLayer()`。
### 组合项目自己的 2.5D 资产工作流
独立 PNG、特殊 pivot 或不规则 atlas 不要求采用引擎内置成品导入器。项目工作流只需生成 `eve.tileset/1` manifest,运行时载入:
```squirrel
local layer = map.newLayer(32, 32, 150, 75);
layer.applyConfig(@"{""orientation"":""isometric""}");
layer.loadTilesetManifest("assets/tiles.tileset.json");
```
每个 GID 可声明 `region`、`pivot`、`sortBias`、`footprint`、`walkable`、`cost` 与项目自定义字段。规则 atlas GID 仍走原路径。参考阶段管线位于 `tools/tile-pipeline/`,可用项目 Python 插件替换任一步;详见 `docs/dev/可组合2.5D-TileSet资产管线.md`。
逻辑格尺寸和渲染间距相互独立:
```squirrel
layer.setRenderSpacing(1.12, 1.12); // 相对逻辑 tile 尺寸
layer.setCellGap(8.0, 4.0); // 或直接指定世界像素间隔
local gapX = layer.getCellGapX();
local gapY = layer.getCellGapY();
local spacingX = layer.getRenderSpacingX();
local spacingY = layer.getRenderSpacingY();
```
间距同时用于正反坐标换算、拾取和深度排序,但不改变 GID、寻路邻接关系或移动成本。JSON 配置可使用 `"renderSpacing":[1.12,1.12]` 或 `"cellGap":[8,4]`。
需要互相产生 2.5D 遮挡的地面、角色和建筑应使用相同的 `layer`,使统一队列按脚点深度排序。`layer` 是 HUD、前景遮罩等用途的硬排序屏障;同格内的细微顺序使用 TileSet `sortBias`。
### 单体寻路(A*)
```squirrel
local pf = map.newPathfinder(layer);
pf.setTopology("auto"); // ortho4 / ortho8 / hex;auto 按 orientation
pf.blockGid(1); // 墙 GID
local path = pf.findPath(sx, sy, gx, gy);
if (path.getLength() > 0) {
local x = path.getX(0);
local y = path.getY(0);
}
```
正交/等距默认四向;`setDiagonal(true)` 后 `auto` 升为八向。交错/六角地图 `auto` 为 `hex`。
### 群体寻路(同目标 Flow Field)
多单位前往同一格时,建一次势场再各自跟随,避免每人跑一遍 A*:
```squirrel
local field = pf.buildFlowField(gx, gy);
local p1 = pf.followFlow(field, ax, ay);
local p2 = pf.findGroupPath(bx, by, gx, gy); // 内部复用缓存场
local nx = field.nextX(ax, ay);
local ny = field.nextY(ax, ay);
```
自定义网格(不绑 TileLayer):`map.newPathfinderSize(w, h)`,再用 `setBlocked` / `setCellCost`。
### 动态视野(FOV / 战争迷雾)
格子可见性与探索记忆;遮挡(opaque)与寻路可行走(walkable)分开配置。默认算法为 recursive shadowcasting;可选 `raycast` / `permissive` / `rectangle`。模式:`grid2d`(默认)、`heightmap`、`volume`。拓扑:`ortho` / `hex`。
```squirrel
local fov = map.newFov(layer);
fov.setBlockEmpty(false);
fov.blockOpaqueGid(1);
fov.setRadiusMetric("chebyshev");
fov.setTopology("auto"); // 或 "hex"
local id = fov.addRevealer(sx, sy, 8);
// 软 RPG 挂钩:游戏自行读取 actor 属性后写入
fov.setRevealerPerception(id, actor.getFinalAttribute("perception"));
fov.setPerceptionRadiusScale(0.25);
fov.setDetectionMargin(0.0);
fov.compute();
if (fov.canDetect(id, tx, ty, targetStealth)) { /* 可见且感知足够 */ }
local tex = fov.buildMaskTexture(gfx); // RGBA8 FoW 遮罩,调用方拥有
```
自定义网格:`map.newFovSize(w, h)` + `setOpaque`。移动观察者后再次 `compute()`;无变更时 `isDirty()` 为 false,`compute` 会跳过。离开视野的格子保留 `explored`,可用 `clearMemory()` 清空。
高度场:
```squirrel
fov.setMode("heightmap");
fov.setElevation(4, 1, 2.0);
fov.setCliffBlock(1.0);
```
体素 3D:
```squirrel
local fov3 = map.newFovVolume(32, 32, 8);
fov3.setOpaque3(4, 4, 2, true);
fov3.addRevealer3(4, 4, 1, 10);
fov3.setVerticalRange(4);
fov3.compute();
fov3.isVisible3(8, 4, 1);
```
### Dual Grid(双网格)
Tiled **没有**原生 dual-grid;在编辑器里画逻辑填充层,运行时解算显示层。支持正交 / 等距 / 交错 / 六角:掩码按格子索引采样,显示层继承投影参数并施加对应半步 origin 偏移。
```squirrel
local map = eve.Map();
local logic = map.newLayer(16, 12, 32, 32);
local display = map.newLayer(1, 1, 32, 32);
// display.setTileset(tex, 1, 4); // 4×4 的 15-tile 图集
logic.setTile(3, 4, 1);
map.resolveDualGrid(logic, display); // 半步偏移 + 15 片选瓦
// map.dualGridOffsetX(logic) / dualGridOffsetY(logic) 可查偏移量
```
`resolveDualGridFilled(logic, display, filledGid)` 只把指定 GID 当作填充。逻辑层可继续用于碰撞 / 寻路;默认会 `setVisible(false)`。
## 常见问题
- GID 与图集编号混淆:0 为空,其余值遵循 tileset 映射;默认空瓦片不可走(`setBlockEmpty`)。
- 世界坐标直接作为 tile 索引:先做投影换算。
- 修改 JSON 后未启用 watch/autoReload。
- 期望 Tiled 直接导出 dual-grid 显示层:不支持;用逻辑层 + `resolveDualGrid`。
- 寻路失败:`getLength()==0`(越界、不可走或不可达),不是抛异常。
## API 快查
下列方法名来自当前 Squirrel 绑定;同一模块创建的辅助对象(例如 `World`、`Body`、`Source`)的方法也列在这里。
- `applyConfig()`、`clear()`、`depthYAt()`、`fill()`、`getAutoReload()`、`getConfigPath()`、`getLayer()`、`getLayerCount()`
- `getMapHeight()`、`getMapWidth()`、`getName()`、`getObjectCount()`、`getObjectGid()`、`getObjectHeight()`、`getObjectName()`、`getObjectType()`
- `getLastVisibleTileCount()`、`getLastCustomVisualCount()`、`getLastAtlasCount()`
- `getObjectWidth()`、`getObjectX()`、`getObjectY()`、`getTile()`、`getTileHeight()`、`getTileWidth()`、`getTilesetColumns()`、`getTilesetFirstGid()`
- `getTilesetTexture()`、`getX()`、`getY()`、`isVisible()`、`loadConfig()`、`loadFromFile()`、`newLayer()`、`newLayerFromFile()`
- `newPathfinder()`、`newPathfinderSize()`、`newFov()`、`newFovSize()`、`newFovVolume()`
- `pollConfigs()`、`reloadConfig()`、`render()`、`resize()`、`resolveDualGrid()`、`resolveDualGridFilled()`、`setAutoReload()`、`setCamera()`、`setCanvas()`、`setLayer()`
- `setOrigin()`、`setTile()`、`setTileSize()`、`setTileset()`、`setTilesetTileSize()`、`setTileVisual()`、`setTileMetadata()`、`clearTileVisuals()`、`getTileVisualCount()`、`loadTilesetManifest()`、`setTint()`、`setVisible()`、`tileToWorldX()`
- `tileToWorldY()`、`update()`、`worldToTileX()`、`worldToTileY()`、`dualGridFrame()`、`dualGridMaskAt()`、`dualGridOffsetX()`、`dualGridOffsetY()`、`lastDualGridError()`
Pathfinder:`setTopology`、`getTopology`、`setDiagonal`、`blockGid`、`unblockGid`、`clearBlockedGids`、`setBlockEmpty`、`setBlocked`、`isWalkable`、`setCellCost`、`getCellCost`、`syncFromLayer`、`findPath`、`buildFlowField`、`followFlow`、`findGroupPath`、`invalidateCache`
Path:`getLength`、`getX`、`getY`、`getTotalCost`
FlowField:`getWidth`、`getHeight`、`getGoalX`、`getGoalY`、`costAt`、`nextX`、`nextY`、`isReachable`
Fov:`getWidth`、`getHeight`、`getDepth`、`setMode`、`getMode`、`setAlgorithm`、`getAlgorithm`、`setRadiusMetric`、`getRadiusMetric`、`setTopology`、`getTopology`、`setCornerPeek`、`blockOpaqueGid`、`unblockOpaqueGid`、`clearOpaqueGids`、`setBlockEmpty`、`setOpaque`、`isOpaque`、`setOpaque3`、`isOpaque3`、`syncFromLayer`、`setElevation`、`getElevation`、`setCliffBlock`、`setEyeOffset`、`setVerticalRange`、`addRevealer`、`addRevealer3`、`removeRevealer`、`clearRevealers`、`setRevealerPosition`、`setRevealerPosition3`、`setRevealerRadius`、`setRevealerFacing`、`clearRevealerFacing`、`setRevealerEnabled`、`getRevealerCount`、`setRevealerPerception`、`getRevealerPerception`、`setPerceptionRadiusScale`、`setDetectionMargin`、`getEffectiveRadius`、`canDetect`、`canDetect3`、`markDirty`、`isDirty`、`compute`、`isVisible`、`isExplored`、`isVisible3`、`isExplored3`、`getState`、`getState3`、`clearMemory`、`resetVisibleOnly`、`getMaskValue`、`getMaskByte`、`getMaskValue3`、`getMaskByte3`、`buildMaskTexture`、`buildMaskTextureSlice`
## 使用要点
- 模块对象和它创建的资源对象应保存在全局或实体状态中,不要在每帧重复创建。
- 带 `update(dt)` 的系统应在 `eve_update` 调用;绘制方法应在 `eve_render` 调用。
- 参数约束、默认值和返回类型以对应模块头文件及 `addFunc` 绑定为准;本文 API 快查与当前源码同步生成。
**源码:** [`src/modules/map/`](../../../src/modules/map/)
**设计:** [`docs/dev/寻路系统设计.md`](../../dev/寻路系统设计.md)、[`docs/dev/动态视野系统设计.md`](../../dev/动态视野系统设计.md)
**相关测试:** [`test/map.cpp`](../../../test/map.cpp)、[`test/map_path.cpp`](../../../test/map_path.cpp)、[`test/map_fov.cpp`](../../../test/map_fov.cpp)、[`test/hex_level_simulation.cpp`](../../../test/hex_level_simulation.cpp)、[`test/hex_level_data.cpp`](../../../test/hex_level_data.cpp)
**示例:** [`examples/hex-levels/`](../../../examples/hex-levels/)