精灵平滑移动、墙碰撞体积
一、
渲染层用连续坐标(scripts/页面/校园地图.lua,三处统一):
lua-- NPC 定位(第408-410行) local contLeft = (character.x or 0) * 5 - 18 - charLeft local contTop = (character.y or 0) * 5 - 36 - charTop -- 主角定位(第504-506行) local contLeft = px * 5 - 18 - charLeft local contTop = py * 5 - 36 - charTop -- 动态更新(每帧SetStyle,第1148-1150行) local anchorX = (ch.x or 0) * 5 - 18 - cl local anchorY = (ch.y or 0) * 5 - 36 - ct
碰撞层用CellOf格对齐(scripts/小人/导航.lua):
lua-- 角色"所在格":四舍五入到最近格,碰撞只检测这个格 local function CellOf(value) return math.floor((value or 0) + 0.5) end local function IsCellBlocked(cx, cy, blocked) local gx = CellOf(cx) local gy = CellOf(cy) if not InBounds(gx, gy) then return true end return blocked[CellKey(gx, gy)] == true end -- CanMove 用连续坐标,但碰撞检测最近格 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 IsCellBlocked(nx, ny, blocked) then return false, x, y -- 阻挡返回原位置 end return true, nx, ny end
主角操控(scripts/系统/主角操控服务.lua):
lua-- 连续坐标移动,碰撞阻挡后贴墙滑行 local step = PLAYER_SPEED * math.max(0, dt or 0) local canMove, nx, ny = Navigation.CanMove(spatial.x, spatial.y, dx, dy, step, ...) if not canMove then -- 贴墙滑行:斜向被挡时尝试单轴 local slideX = Navigation.CanMove(spatial.x, spatial.y, dx, 0, step, ...) local slideY = Navigation.CanMove(spatial.x, spatial.y, 0, dy, step, ...) 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 ActorSpatialService.SetPosition(playerId, nx, ny, direction)


二、围墙/房间墙碰撞体积分析 — 核心代码
房间墙段生成(scripts/数据/地图/校园主地图.lua,第125-203行):
luafunction M.GetRoomWallSegments(room) if room.wallsEnabled ~= true then return {} end local thickness = 1 local rx, ry = room.x or 0, room.y or 0 local rw, rh = room.w or 0, room.h or 0 -- top墙:y=ry,x从rx到rx+rw-1,每段1格宽,h=thickness=1 for dx = 0, rw-1 do result[#result+1] = { side="top", x=rx+dx, y=ry, w=1, h=thickness, ... } -- bottom墙:y=ry+rh-1 result[#result+1] = { side="bottom", x=rx+dx, y=ry+rh-thickness, w=1, h=thickness, ... } end -- left墙:x=rx,y从ry到ry+rh-1,每段w=thickness=1 for dy = 0, rh-1 do result[#result+1] = { side="left", x=rx, y=ry+dy, w=thickness, h=1, ... } -- right墙:x=rx+rw-1 result[#result+1] = { side="right", x=rx+rw-thickness, y=ry+dy, w=thickness, h=1, ... } end return result end
自由墙段(第207-264行):
lualocal function GetFreeWallSegments(wall) local thick = 1 -- 自由墙也只有1格厚 -- 竖段:x=x0,y从y0到y1,每段w=thick=1 for dy = 0, y1-y0 do result[#result+1] = { side="free_v", x=x0, y=y0+dy, w=thick, h=1, ... } end end
BuildBlockedCells 标记阻挡格(scripts/小人/导航.lua):
lualocal function BuildBlockedCells(profession) local blocked = {} for i = 1, #segments do if not passable then local width = math.max(1, Round(segment.w or 1)) -- =1 local height = math.max(1, Round(segment.h or 1)) -- =1 for dx = 0, width-1 do for dy = 0, height-1 do blocked[CellKey(Round(segment.x)+dx, Round(segment.y)+dy)] = true end end end end return blocked end


三、四面墙间距不一致的根因
| 墙 | 阻挡格 | 角色被挡在 | 距墙格数 | 精灵覆盖墙? |
| left墙 x=rx | CellOf(x)=rx | x≈rx+0.5(floor(0.5+0.5)=1=rx+1可走,x=0.4时CellOf=0=rx阻挡) | 0.5格 | 是,左边缘-15.5px覆盖0-5px墙 |
| right墙 x=rx+rw-1 | CellOf(x)=rx+rw-1 | x≈rx+rw-1.5 | 0.5格 | 是,对称覆盖 |
| top墙 y=ry | CellOf(y)=ry | y≈ry+0.5(房间内)/ y≈ry-0.5(房间外) | 0.5格 | 是,上边缘覆盖 |
| bottom墙 y=ry+rh-1 | CellOf(y)=ry+rh-1 | y≈ry+rh-1.5 | 0.5格 | 是 |
四面墙碰撞间距其实一致(都是0.5格)。图3看起来"漂浮更远"是因为角色在房间外(top墙上方草地),被墙阻挡在y≈ry-0.5位置,精灵向上延伸36px,看起来离墙远。图1/2在房间内,精灵脚贴地面,视觉上不同。
真正的问题:精灵36px宽/高相对5px格子太大,中心停在0.5格时精灵边缘必然覆盖相邻墙格像素。要解决视觉覆盖需要空气墙(把阻挡格内移1格)或缩小精灵尺寸。

