围墙空气墙
问题根源
原系统用单点碰撞(Round(nx), Round(ny) 只检测中心1个点),角色精灵 36px(7.2格) 宽,中心停在 x=0.5 时精灵左半完全覆盖 5px 围墙,视觉"穿墙"。


1. AABB 矩形碰撞检测 — scripts/小人/导航.lua
lua-- 角色碰撞体尺寸(格):脚底占据的地面矩形 -- 半宽 0.5 格(2.5px),半高 0.3 格(1.5px) local ACTOR_HALF_W = 0.5 local ACTOR_HALF_H = 0.3 -- 边界:格 gx 占据连续空间 [gx, gx+1),上界用 < 不用 <= local function InBounds(x, y) return x >= 0 and y >= 0 and x < (MapData.data.width or 240) and y < (MapData.data.height or 160) end -- AABB 矩形碰撞:检测以(cx,cy)为中心、半宽halfW、半高halfH的矩形 -- 是否与任何墙格重叠或越界 local function IsRectBlocked(cx, cy, halfW, halfH, blocked) local minGX = math.floor(cx - halfW) local maxGX = math.floor(cx + halfW - 1e-6) local minGY = math.floor(cy - halfH) local maxGY = math.floor(cy + halfH - 1e-6) for gx = minGX, maxGX do for gy = minGY, maxGY do if not InBounds(gx, gy) or blocked[CellKey(gx, gy)] then return true end end end return false end -- 逐帧碰撞:用 AABB 替代单点 Round function Navigation.CanMove(x, y, dx, dy, step, profession) local blocked = BuildBlockedCells(profession or {}) local nx = x + dx * step local ny = y + dy * step if IsRectBlocked(nx, ny, ACTOR_HALF_W, ACTOR_HALF_H, blocked) then return false, x, y -- 被阻挡,返回原位置 end return true, nx, ny end


2. 围墙空气墙(不可见碰撞缓冲)— scripts/数据/地图/校园主地图.lua
lualocal function GetFreeWallSegments(wall) local style = wall.style or "普通墙" -- 围墙/栅栏:碰撞占2格宽(视觉1格 + 内侧1格空气墙) -- 普通墙/破损墙:1格 local thick = (style == "围墙" or style == "栅栏") and 2 or 1 -- 竖段:w=thick 向校园内侧延伸 for dy = 0, (y1 - y0) do result[#result + 1] = { x = x0, y = y0 + dy, w = thick, h = 1, -- 围墙w=2,普通墙w=1 style = style, -- ...其他字段 } end -- 横段:h=thick 同理 end
BuildBlockedCells 遍历 segment.w/h 逐格标记 blocked[CellKey] = true,x=0(可见墙)和 x=1(空气墙)两列都阻挡通行。


3. 围墙逐格 Y-sorting 渲染 — scripts/页面/校园地图.lua
lua-- 逐格拆分+borderRadius=0(防圆点),每格zIndex=CharZ(y) local function CreateFreeWallView(wall) local style = wall.style or "普通墙" local color = style == "围墙" and {110,88,60,255} or ... local visualPx = 5 -- 只渲染1格(5px)可见,空气墙不渲染 for gy = y0, y1 do segments[#segments + 1] = UI.Panel { position = "absolute", left = x0 * 5, top = gy * 5, width = visualPx, height = 5, -- 只画5px,不是thick*5 backgroundColor = color, borderWidth = 0, borderRadius = 0, -- 关键:防圆角变圆点 pointerEvents = "none", zIndex = CharZ(gy), -- 逐格Y排序 } end return segments end -- 调用处展开逐段数组 for i = 1, #(data.walls or {}) do local wallSegments = CreateFreeWallView(data.walls[i]) for j = 1, #wallSegments do campusChildren[#campusChildren + 1] = wallSegments[j] end end


4. 贴墙滑行(主角操控,不变)— scripts/系统/主角操控服务.lua
lualocal canMove, nx, ny = Navigation.CanMove(spatial.x, spatial.y, dx, dy, step, profile) if not canMove then -- 斜向撞墙时尝试单轴滑行 local slideX = math.abs(dx) > 0.01 and Navigation.CanMove(spatial.x, spatial.y, dx, 0, step, profile) local slideY = math.abs(dy) > 0.01 and Navigation.CanMove(spatial.x, spatial.y, 0, dy, step, profile) if slideX then nx, ny = spatial.x + dx * step, spatial.y elseif slideY then nx, ny = spatial.x, spatial.y + dy * step else return false -- 两轴都挡,完全不动 end end


关键参数对照表
| 参数 | 值 | 作用 |
| ACTOR_HALF_W | 0.5格(2.5px) | 角色碰撞半宽,配合2格墙推到x≈1.5 |
| ACTOR_HALF_H | 0.3格(1.5px) | 角色碰撞半高 |
| 围墙 thick | 2格 | x=0可见墙+x=1空气墙,共2列阻挡 |
| 围墙渲染 visualPx | 5px(1格) | 只渲染x=0,x=1空气墙不可见 |
| 角色-围墙间距 | ~2.5px(0.5格) | 中心x=1.5 - 墙右缘5px = 2.5px |
| borderRadius | 0 | 防止5×5方块圆角变圆点 |
| zIndex | CharZ(y) | 逐格Y排序,前后遮挡自然 |
| InBounds 上界 | < 不用 <= | 格[gx,gx+1),x=width越界 |

