Files
EVEngine/docs/usr/modules/map.md
T
西风逍遥游andCursor Agent c0d9fe4a7d test(hex): 程序化六边形关卡套件(FOV / 光照 / 寻路 / 拾取 / 粒子) (#50)
* test(hex): add procedural hex dungeon levels for engine QA

Cross-module simulation levels exercise hex tilemap procgen, FOV,
2D lighting, spatial pickup + inventory, and particles. Includes a
playable examples/hex-levels demo with level select and a pipeline crawl.

Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>

* test(hex): expand levels 06–15 and fog-raid pipeline

Add Flow Field swarm, cell-cost detours, multi-revealer perception,
FoW mask/algorithms, camera pick, dual-grid, procgen variants,
QuadTree cull, multi-light, particle/stash transfer, and a fog-raid
pipeline. Update the playable hex-levels demo for levels 6–9.

Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>

* test(hex): add fixture JSON data and data-driven hex tests

Expand catalog/items/loot/seeds/perception fixtures, handcrafted maps,
and particle configs. Wire the hex-levels demo to load items and particle
JSON plus fixtures.nut loot tables. Register hex_level_data.cpp coverage.

Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>

* test(hex): fix data fixtures assertions for zeroerr and sparse gens

Avoid CHECK_EQ on bag->addItem (double-eval), and only enforce minWalkable
on dungeon.bsp in the seeds matrix smoke.

Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>

* feat(hex): boot each level from distinct catalog configs

Enrich catalog/fixtures with per-level FOV, light, cell-cost, and enable
flags. Wire examples/hex-levels to apply those boot settings on level
switch. Add hex.data.catalog.bootEachLevelDistinctConfig to verify each
catalog entry starts with its own applied configuration.

Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>

* docs(hex): note per-level boot configs in data README

Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>

* test(hex): add levels 16–20 and extra data-driven coverage

Expand simulation with facing-cone FOV, flow+cell-cost, seed
reproducibility, corner peek, equipment equip, and torchEscort /
catalogRaid pipelines. Add fixture tests for perception gates, all loot
tables, enable-flag consistency, seed path samples, map markers,
defaults, equipment slots, and mist particles on dungeon.

Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>

* docs(hex): clarify expanded test coverage in README

Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>

* test(hex): add levels 21–25 and broader fixture validation

Cover hex world↔tile roundtrip, FOV memory clear, findGroupPath,
multi-algo FOV smoke, blocked-cell sync, and costlyFogPickup pipeline.
Add data tests for loot/item referential integrity, feature tags,
particle schema, algorithm registration, ring-door path, categories,
detection margin, and door walkability.

Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>

* feat(hex): expand catalog to 31 levels (0–30) and playable cycle

Add levels 16–30 with distinct boot configs (facing cone, flow+cost,
maze/WFC/drunkard, mist particles, raid combo). Regenerate fixtures.nut
with LEVEL_IDS; main.nut cycles all catalog levels via N/B. Add
simulation cases 26–30 and catalog presence tests.

Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>

* fix(hex): harden level 02/17 against non-portable procgen paths

macOS CI failed hex.level.02.dynamicFov (empty path) and
hex.level.17.flowPlusCellCost (path length == 2) because spawn↔exit
layouts from DTL/std distributions differ across libstdc++ vs libc++.
Use handcrafted hex maps so FOV memory and flow+cost assertions are
platform-stable.

Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-08-13 11:54:41 +08:00

156 lines
8.4 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()`。
### 单体寻路(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()`
- `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()`、`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/)