You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Love2d中角色重力与爬梯子功能冲突问题求助

问题描述

我正在为开发的游戏实现角色非爬梯状态下的重力效果,但每次添加该功能时,爬梯子功能就会出现异常;修复爬梯问题后,重力效果又失效,陷入循环。我尝试过直接操作x/y坐标、使用dx/dy变量,甚至求助ChatGPT都无法解决,无报错信息,属于逻辑bug。我的代码如下:

-- Initialize the game
function love.load()
    -- Set the window dimensions
    love.window.setMode(800, 600)
    
    -- Set the title of the window
    love.window.setTitle("Mappy")
    
    -- Load the game assets
    characterImage = love.graphics.newImage("mappy.png")
    itemImage = love.graphics.newImage("item.png")
    enemyImage = love.graphics.newImage("enemy.png")
    
    -- Check if the image files loaded successfully
    if not characterImage or not itemImage or not enemyImage then
        error("Failed to load game assets. Make sure the image files exist and are accessible.")
    end
    
    -- Set the character's initial position
    characterX = 400
    characterY = love.graphics.getHeight() - 180
    
    -- Set the item's initial position
    itemX = 100
    itemY = 100
    
    -- Set the enemy's initial position
    enemyX = 700
    enemyY = 500
    
    -- Create a map
    map = {
        "#################################",
        "#                               #",
        "#         ^           ^         #",
        "#   ##### ^ ######### ^ #####   #",
        "#         ^           ^         #",
        "#         ^           ^         #",
        "#   ##### ^ ######### ^ #####   #",
        "#         ^           ^         #",
        "#         ^           ^         #",
        "#   ##### ^ ######### ^ #####   #",
        "#         ^           ^         #",
        "#         ^           ^         #",
        "#   ##### ^ ######### ^ #####   #",
        "#         ^           ^         #",
        "#         ^           ^         #",
        "#################################",
    }
    
    -- Set the tile size
    tileSize = 32
    
    -- Set up the camera
    camera = {
        x = 0,
        y = 0
    }
    
    -- Set initial ladder interaction state
    touchingLadder = false
end

-- Update the game state
function love.update(dt)
    -- Move the character based on user input
    local dx, dy = 0, 0
    if love.keyboard.isDown("up") and touchingLadder then
        dy = -200
    end
    if love.keyboard.isDown("down") and touchingLadder then
        dy = 200
    end
    if love.keyboard.isDown("left") then
        dx = -200
    end
    if love.keyboard.isDown("right") then
        dx = 200
    end
    
    -- Check for collision with walls
    local wallCollision = checkWallCollision(characterX + dx * dt, characterY + dy * dt, characterImage:getWidth(), characterImage:getHeight())
    
    -- Move the character only if there is no collision with walls
    if not wallCollision then
        characterX = characterX + dx * dt
        characterY = characterY + dy * dt
    end
    
    -- Update the camera position to follow the character
    camera.x = characterX - love.graphics.getWidth() / 2
    camera.y = characterY - love.graphics.getHeight() / 2
    
    -- Check for collision between character and item
    if checkCollision(characterX, characterY, characterImage:getWidth(), characterImage:getHeight(),
                      itemX, itemY, itemImage:getWidth(), itemImage:getHeight()) then
        -- Respawn the item at a new random position
        itemX = love.math.random(100, 700)
        itemY = love.math.random(100, 500)
    end
    
    -- Check for collision between character and enemy
    if checkCollision(characterX, characterY, characterImage:getWidth(), characterImage:getHeight(),
                      enemyX, enemyY, enemyImage:getWidth(), enemyImage:getHeight()) then
        -- Game over
        love.event.quit()
    end
    
    -- Update ladder interaction state
    touchingLadder = checkLadderCollision(characterX, characterY, characterImage:getWidth(), characterImage:getHeight())
end

-- Draw the game state
function love.draw()
    -- Set the drawing transformation based on the camera position
    love.graphics.push()
    love.graphics.translate(-camera.x, -camera.y)
    
    -- Draw the map
    for y = 1, #map do
        local row = map[y]
        for x = 1, #row do
            local char = row:sub(x, x)
            if char == "#" then
                love.graphics.rectangle("fill", (x - 1) * tileSize, (y - 1) * tileSize, tileSize, tileSize)
            elseif char == "^" then
                love.graphics.rectangle("line", (x - 1) * tileSize, (y - 1) * tileSize, tileSize, tileSize)
            end
        end
    end
    
    -- Draw the character
    love.graphics.draw(characterImage, characterX, characterY)
    
    -- Draw the item
    love.graphics.draw(itemImage, itemX, itemY)
    
    -- Draw the enemy
    love.graphics.draw(enemyImage, enemyX, enemyY)
    
    -- Reset the drawing transformation
    love.graphics.pop()
end

-- Check collision between two rectangles
function checkCollision(x1, y1, w1, h1, x2, y2, w2, h2)
    return x1 < x2 + w2 and
           x2 < x1 + w1 and
           y1 < y2 + h2 and
           y2 < y1 + h1
end

-- Check collision with walls
function checkWallCollision(x, y, width, height)
    -- Calculate the character's bounding box
    local characterLeft = x
    local characterRight = x + width
    local characterTop = y
    local characterBottom = y + height
    
    -- Check for collision with walls
    for y = 1, #map do
        local row = map[y]
        for x = 1, #row do
            local char = row:sub(x, x)
            if char == "#" then
                local wallLeft = (x - 1) * tileSize
                local wallRight = x * tileSize
                local wallTop = (y - 1) * tileSize
                local wallBottom = y * tileSize
                
                -- Check if the character's bounding box intersects with the wall
                if characterRight > wallLeft and characterLeft < wallRight and
                   characterBottom > wallTop and characterTop < wallBottom then
                    return true
                end
            end
        end
    end
    
    return false
end

-- Check collision with ladders
function checkLadderCollision(x, y, width, height)
    -- Calculate the character's bounding box
    local characterLeft = x
    local characterRight = x + width
    local characterTop = y
    local characterBottom = y + height
    
    -- Check for collision with ladders
    for y = 1, #map do
        local row = map[y]
        for x = 1, #row do
            local char = row:sub(x, x)
            if char == "^" then
                local ladderLeft = (x - 1) * tileSize
                local ladderRight = x * tileSize
                local ladderTop = (y - 1) * tileSize
                local ladderBottom = y * tileSize
                
                -- Check if the character's bounding box intersects with the ladder
                if characterRight > ladderLeft and characterLeft < ladderRight and
                   characterBottom > ladderTop and characterTop < ladderBottom then
                    return true
                end
            end
        end
    end
    
    return false
end
解决方案

核心问题是重力与爬梯逻辑未做互斥处理——当角色接触梯子时,重力会强制下拉角色,干扰爬梯操作;而如果为了修复爬梯禁用重力,又会导致非爬梯状态下重力失效。以下是修复后的完整代码及关键改动说明:

修改后的完整代码

-- Initialize the game
function love.load()
    -- Set the window dimensions
    love.window.setMode(800, 600)
    
    -- Set the title of the window
    love.window.setTitle("Mappy")
    
    -- Load the game assets
    characterImage = love.graphics.newImage("mappy.png")
    itemImage = love.graphics.newImage("item.png")
    enemyImage = love.graphics.newImage("enemy.png")
    
    -- Check if the image files loaded successfully
    if not characterImage or not itemImage or not enemyImage then
        error("Failed to load game assets. Make sure the image files exist and are accessible.")
    end
    
    -- Set the character's initial position
    characterX = 400
    characterY = love.graphics.getHeight() - 180
    
    -- Set the item's initial position
    itemX = 100
    itemY = 100
    
    -- Set the enemy's initial position
    enemyX = 700
    enemyY = 500
    
    -- Create a map
    map = {
        "#################################",
        "#                               #",
        "#         ^           ^         #",
        "#   ##### ^ ######### ^ #####   #",
        "#         ^           ^         #",
        "#         ^           ^         #",
        "#   ##### ^ ######### ^ #####   #",
        "#         ^           ^         #",
        "#         ^           ^         #",
        "#   ##### ^ ######### ^ #####   #",
        "#         ^           ^         #",
        "#         ^           ^         #",
        "#   ##### ^ ######### ^ #####   #",
        "#         ^           ^         #",
        "#         ^           ^         #",
        "#################################",
    }
    
    -- Set the tile size
    tileSize = 32
    
    -- Set up the camera
    camera = {
        x = 0,
        y = 0
    }
    
    -- Set initial ladder interaction state
    touchingLadder = false
    -- 新增重力参数,可根据游戏手感调整
    gravity = 500
end

-- Update the game state
function love.update(dt)
    -- 提前更新梯子接触状态,确保当前帧逻辑使用最新状态
    touchingLadder = checkLadderCollision(characterX, characterY, characterImage:getWidth(), characterImage:getHeight())
    
    -- Move the character based on user input
    local dx, dy = 0, 0
    if love.keyboard.isDown("up") and touchingLadder then
        dy = -200
    elseif love.keyboard.isDown("down") and touchingLadder then
        dy = 200
    else
        -- 非爬梯状态下应用重力
        dy = gravity
    end
    
    if love.keyboard.isDown("left") then
        dx = -200
    end
    if love.keyboard.isDown("right") then
        dx = 200
    end
    
    -- 分别处理水平和垂直方向的碰撞,避免互相干扰
    -- 先处理水平移动碰撞
    local horizontalCollision = checkWallCollision(characterX + dx * dt, characterY, characterImage:getWidth(), characterImage:getHeight())
    if not horizontalCollision then
        characterX = characterX + dx * dt
    end
    
    -- 再处理垂直移动碰撞(包含重力和爬梯移动)
    local verticalCollision = checkWallCollision(characterX, characterY + dy * dt, characterImage:getWidth(), characterImage:getHeight())
    if not verticalCollision then
        characterY = characterY + dy * dt
    end
    
    -- Update the camera position to follow the character
    camera.x = characterX - love.graphics.getWidth() / 2
    camera.y = characterY - love.graphics.getHeight() / 2
    
    -- Check for collision between character and item
    if checkCollision(characterX, characterY, characterImage:getWidth(), characterImage:getHeight(),
                      itemX, itemY, itemImage:getWidth(), itemImage:getHeight()) then
        -- Respawn the item at a new random position
        itemX = love.math.random(100, 700)
        itemY = love.math.random(100, 500)
    end
    
    -- Check for collision between character and enemy
    if checkCollision(characterX, characterY, characterImage:getWidth(), characterImage:getHeight(),
                      enemyX, enemyY, enemyImage:getWidth(), enemyImage:getHeight()) then
        -- Game over
        love.event.quit()
    end
end

-- Draw the game state
function love.draw()
    -- Set the drawing transformation based on the camera position
    love.graphics.push()
    love.graphics.translate(-camera.x, -camera.y)
    
    -- Draw the map
    for y = 1, #map do
        local row = map[y]
        for x = 1, #row do
            local char = row:sub(x, x)
            if char == "#" then
                love.graphics.rectangle("fill", (x - 1) * tileSize, (y - 1) * tileSize, tileSize, tileSize)
            elseif char == "^" then
                love.graphics.rectangle("line", (x - 1) * tileSize, (y - 1) * tileSize, tileSize, tileSize)
            end
        end
    end
    
    -- Draw the character
    love.graphics.draw(characterImage, characterX, characterY)
    
    -- Draw the item
    love.graphics.draw(itemImage, itemX, itemY)
    
    -- Draw the enemy
    love.graphics.draw(enemyImage, enemyX, enemyY)
    
    -- Reset the drawing transformation
    love.graphics.pop()
end

-- Check collision between two rectangles
function checkCollision(x1, y1, w1, h1, x2, y2, w2, h2)
    return x1 < x2 + w2 and
           x2 < x1 + w1 and
           y1 < y2 + h2 and
           y2 < y1 + h1
end

-- Check collision with walls
function checkWallCollision(x, y, width, height)
    -- Calculate the character's bounding box
    local characterLeft = x
    local characterRight = x + width
    local characterTop = y
    local characterBottom = y + height
    
    -- Check for collision with walls
    for y = 1, #map do
        local row = map[y]
        for x = 1, #row do
            local char = row:sub(x, x)
            if char == "#" then
                local wallLeft = (x - 1) * tileSize
                local wallRight = x * tileSize
                local wallTop = (y - 1) * tileSize
                local wallBottom = y * tileSize
                
                -- Check if the character's bounding box intersects with the wall
                if characterRight > wallLeft and characterLeft < wallRight and
                   characterBottom > wallTop and characterTop < wallBottom then
                    return true
                end
            end
        end
    end
    
    return false
end

-- Check collision with ladders
function checkLadderCollision(x, y, width, height)
    -- Calculate the character's bounding box
    local characterLeft = x
    local characterRight = x + width
    local characterTop = y
    local characterBottom = y + height
    
    -- Check for collision with ladders
    for y = 1, #map do
        local row = map[y]
        for x = 1, #row do
            local char = row:sub(x, x)
            if char == "^" then
                local ladderLeft = (x - 1) * tileSize
                local ladderRight = x * tileSize
                local ladderTop = (y - 1) * tileSize
                local ladderBottom = y * tileSize
                
                -- Check if the character's bounding box intersects with the ladder
                if characterRight > ladderLeft and characterLeft < ladderRight and
                   characterBottom > ladderTop and characterTop < ladderBottom then
                    return true
                end
            end
        end
    end
    
    return false
end

关键改动说明

  1. 新增重力参数:在love.load中添加gravity = 500,统一管理重力加速度,方便调整手感。
  2. 提前更新梯子状态:将touchingLadder = checkLadderCollision(...)移到love.update开头,确保当前帧的移动逻辑使用最新的梯子接触状态,避免状态滞后导致的逻辑错误。
  3. 互斥处理重力与爬梯:在垂直移动逻辑中,使用else分支确保只有未接触梯子且未按下爬梯方向键时,才应用重力;爬梯时完全禁用重力,避免两者互相干扰。
  4. 拆分碰撞检测:将水平和垂直方向的碰撞检测分开处理,避免水平移动的碰撞影响垂直方向的重力/爬梯移动,提升碰撞逻辑的稳定性。

内容的提问来源于stack exchange,提问作者TriggerFox Games

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 19:32:02