Lua复刻吃豆人固定瓦片移动故障修复求助
Hey there! Let's tackle those frustrating movement bugs in your Mrs. Pac-Man Lua remake. I’ve gone through your code and pinpointed a few key issues causing the reverse freeze and wall-clipping/position glitches. Let's break down the problems and fix them step by step.
- 1-based vs 0-based Map Indexing: Lua uses 1-indexed arrays, but your Pac-Man coordinates are likely 0-based (matching the
nodemaplayout). This means accessingnodemap[mrspacman.y - ...][mrspacman.x + ...]can lead to invalid indices (like0), which Lua treats asnil—breaking your collision checks and allowing wall-clipping. - No Instant Reverse Handling: In classic Pac-Man, players can reverse direction immediately, even if not centered on a tile. Your current code only allows direction changes at tile centers, causing the freeze when pressing reverse mid-move.
- Precision Problems with Tile Center Checks: Using
math.floor(mrspacman.x*10)/10introduces floating-point precision errors. Values that should be exact integers (like1.0) might become0.999999999, making your tile center check fail and locking Pac-Man in a moving state. - Unchecked Mid-Tile Movement: When moving between tiles, you don't check if the next tile is a wall—this lets Pac-Man clip through walls when changing angles.
1. Fix Map Indexing
Convert your 0-based player coordinates to 1-based when accessing the nodemap to avoid invalid indices:
-- When checking a target tile, add 1 to x/y to match Lua's 1-indexed arrays local targetTileY = math.floor(mrspacman.y - math2.sin(targetDir)) + 1 local targetTileX = math.floor(mrspacman.x + math2.cos(targetDir)) + 1 if nodemap[targetTileY][targetTileX] ~= 1 then -- Tile is walkable end
2. Allow Instant Reverse Direction
Add a check for reverse direction presses—let Pac-Man flip immediately without waiting for a tile center:
if mrspacman.nextDirection then local targetDir = mrspacman.nextDirection -- Check if the queued direction is exactly opposite to current local isReverse = math.abs(targetDir - mrspacman.dz) == 180 if isReverse then -- Instantly switch to reverse mrspacman.dz = targetDir mrspacman.nextDirection = false else -- Original logic: only switch non-reverse directions at tile centers local epsilon = 0.01 local isAtCenter = (math.abs(mrspacman.x - math.floor(mrspacman.x)) < epsilon) and (math.abs(mrspacman.y - math.floor(mrspacman.y)) < epsilon) if isAtCenter then local targetTileY = math.floor(mrspacman.y - math2.sin(targetDir)) + 1 local targetTileX = math.floor(mrspacman.x + math2.cos(targetDir)) + 1 if nodemap[targetTileY][targetTileX] ~= 1 then mrspacman.dz = targetDir mrspacman.nextDirection = false end end end end
3. Improve Tile Center Precision
Replace exact equality checks with a small "epsilon" value to account for floating-point errors:
local epsilon = 0.01 -- Adjust based on your speed (0.1 works here) local isAtTileCenter = (math.abs(mrspacman.x - math.floor(mrspacman.x)) < epsilon) and (math.abs(mrspacman.y - math.floor(mrspacman.y)) < epsilon)
This avoids missing tile center checks due to tiny precision gaps.
4. Block Mid-Tile Wall Clipping
Check if the next tile is walkable before moving into it, even mid-tile:
if not isAtTileCenter then local nextX = mrspacman.x + (math2.cos(mrspacman.dz)*mrspacman.speed) local nextY = mrspacman.y - (math2.sin(mrspacman.dz)*mrspacman.speed) local currentTileX = math.floor(mrspacman.x) local currentTileY = math.floor(mrspacman.y) local nextTileX = math.floor(nextX) local nextTileY = math.floor(nextY) -- If we're entering a new tile, check if it's a wall if nextTileX ~= currentTileX or nextTileY ~= currentTileY then if nodemap[nextTileY + 1][nextTileX + 1] == 1 then -- Hit a wall: snap back to current tile center mrspacman.x = currentTileX mrspacman.y = currentTileY return -- Skip this movement step end end -- Safe to move mrspacman.x = nextX mrspacman.y = nextY end
Here’s the combined, updated code incorporating all fixes:
-- Helper: Check if Pac-Man is at a tile center (with precision tolerance) local epsilon = 0.01 local isAtTileCenter = (math.abs(mrspacman.x - math.floor(mrspacman.x)) < epsilon) and (math.abs(mrspacman.y - math.floor(mrspacman.y)) < epsilon) -- Handle queued direction changes if mrspacman.nextDirection then local targetDir = mrspacman.nextDirection local isReverse = math.abs(targetDir - mrspacman.dz) == 180 if isReverse then -- Instant reverse direction mrspacman.dz = targetDir mrspacman.nextDirection = false else -- Only switch non-reverse directions at tile centers if isAtTileCenter then local targetTileY = math.floor(mrspacman.y - math2.sin(targetDir)) + 1 local targetTileX = math.floor(mrspacman.x + math2.cos(targetDir)) + 1 if nodemap[targetTileY][targetTileX] ~= 1 then mrspacman.dz = targetDir mrspacman.nextDirection = false end end end end -- Handle movement if isAtTileCenter then -- Check if forward tile is walkable before moving local targetTileY = math.floor(mrspacman.y - math2.sin(mrspacman.dz)) + 1 local targetTileX = math.floor(mrspacman.x + math2.cos(mrspacman.dz)) + 1 if nodemap[targetTileY][targetTileX] ~= 1 then mrspacman.x = mrspacman.x + (math2.cos(mrspacman.dz)*mrspacman.speed) mrspacman.y = mrspacman.y - (math2.sin(mrspacman.dz)*mrspacman.speed) end else -- Calculate next position local nextX = mrspacman.x + (math2.cos(mrspacman.dz)*mrspacman.speed) local nextY = mrspacman.y - (math2.sin(mrspacman.dz)*mrspacman.speed) local currentTileX = math.floor(mrspacman.x) local currentTileY = math.floor(mrspacman.y) local nextTileX = math.floor(nextX) local nextTileY = math.floor(nextY) -- Check if entering a new wall tile if nextTileX ~= currentTileX or nextTileY ~= currentTileY then if nodemap[nextTileY + 1][nextTileX + 1] == 1 then -- Snap to current tile center and stop mrspacman.x = currentTileX mrspacman.y = currentTileY else -- Move to next position mrspacman.x = nextX mrspacman.y = nextY end else -- Stay in current tile, continue moving mrspacman.x = nextX mrspacman.y = nextY end end
- Make sure your
mrspacman.nextDirectionis set correctly (e.g., left = 180°, right = 0°, up = 90°, down = 270°) to match yourmath2table. - Remove the
math.floor(mrspacman.x*10)/10lines—they’re causing unnecessary precision loss. Let Pac-Man’s coordinates stay as raw floats, using the epsilon check instead.
内容的提问来源于stack exchange,提问作者crazicrafter1

