lualove2d

player movement not working when moving in a specific direction (Lua/Love2d)


I'm making a project in Love2d and, on top of a simple movement system, I tried to implement something where the player will "teleport" in the direction they are moving when the "e" key is pressed. Code looks like this

if key == "e" then
    activeunit.x = activeunit.x + 100*activeunit.dx
    activeunit.y = activeunit.y + 100*activeunit.dy
end

the activeunit table is there because there are 2 controllable circles, but this issue was present before that

normally this works as intended, except when the player is moving upwards and to the right, aka when the "w" and "d" keys are held, aka when dx = 1 and dy = -1, I'm not sure what is causing this. Additionally, this issue partially disappears when movement controls are moved from wasd to the the arrow keys - the player teleports, but by 200 pixels instead of the intended 100.

in case the issue is with some of the other code, here is its entirety (all of it is movement stuff right now)

local love = require "love"

local units = {}
units[1] = {
    x = 0,
    y = 0,
    speed = 100,
    dx = 0,
    dy = 0,
    radius = 10,
    isactive = true
}
units[2] = {
    x = 0,
    y = 0,
    speed = 200,
    dx = 0,
    dy = 0,
    radius = 10,
    isactive = false
}
local x = 0
local y = 0
local speed = 100
local dx = 0
local dy = 0
local radius = 10
local time = 0
local ground = 500-radius
local activeunit = units[1]
local activeunitnumber = 1
local nextunitnumber = 2
local tvelocity = 200
local fallspeed = 0

function love.load()
    
end

function love.update(dt)
    if love.keyboard.isDown("left") then
        activeunit.x = activeunit.x - activeunit.speed*dt
        activeunit.dx = -1
    end
    if love.keyboard.isDown("up") then
        activeunit.y = activeunit.y - activeunit.speed*dt
        activeunit.dy = -1
    end
    if love.keyboard.isDown("down") then
        activeunit.y = activeunit.y + activeunit.speed*dt
        activeunit.dy = 1
    end
    if love.keyboard.isDown("right") then
        activeunit.x = activeunit.x + activeunit.speed*dt
        activeunit.dx = 1
    end
    if love.keyboard.isDown("up") == false and love.keyboard.isDown("down") == false then
        activeunit.dy = 0
    end
    if love.keyboard.isDown("left") == false and love.keyboard.isDown("right") == false then
        activeunit.dx = 0 
    end
    if activeunit.y > ground then activeunit.y = ground end
    time = time + 1*dt
    
    --[[if y < ground then 
        while fallspeed < tvelocity do
             fallspeed = fallspeed + 10*dt
        end
        y = y + fallspeed*dt
    end]]
end

function love.keypressed(key, scancode)
    if key == "q" then
        activeunit.speed = activeunit.speed * 2
    end
    if key == "e" then
        activeunit.x = activeunit.x + 100*activeunit.dx
        activeunit.y = activeunit.y + 100*activeunit.dy
        if activeunit.dx == 1 and activeunit.dy == -1 then
            activeunit.x = activeunit.x + 100
            activeunit.y = activeunit.y - 100
        end
    end
    if key == "tab" then
        activeunit.isactive = false
        nextunitnumber = activeunitnumber + 1
        if nextunitnumber > 2 then nextunitnumber = 1 end 
        units[nextunitnumber].isactive = true
        activeunit = units[nextunitnumber]
        activeunitnumber = nextunitnumber
    end
    --[[if key == "space" then
        fallspeed = 0
        while 
    end]]
end

function love.draw()
    love.graphics.circle("fill", units[1].x, units[1].y, radius)
    love.graphics.circle("fill", units[2].x, units[2].y, radius)
    love.graphics.line(0,500,800,500)
    love.graphics.print(activeunit.dx,0,500)
    love.graphics.print(activeunit.dy,0,515)
    love.graphics.print(activeunit.speed,0,530)
    love.graphics.print(activeunit.x,0,545)
    love.graphics.print(activeunit.y,0,560)
    love.graphics.print(activeunitnumber,0,575)
    --love.graphics.rectangle("fill",375,100,50,100)
end

Solution

  • Additionally, this issue partially disappears when movement controls are moved from wasd to the the arrow keys - the player teleports, but by 200 pixels instead of the intended 100.

    This turns out to be a hardware issue: the user's keyboard physically lacks sufficient key rollover, which is the ability to press many, or certain combinations, of keys simultaneously. With an n-key rollover keyboard, there is no issue of pressing any combination of WASD and E.

    Suggestion: Move the teleport bind to a key that does not conflict with any movement bind (e.g., WASD + SPACE.

    Additionally, with the current example, this bit of very specific, conditional code always adds 100 additional pixels of movement to the teleport when traveling in the up-right direction.

    if activeunit.dx == 1 and activeunit.dy == -1 then
        activeunit.x = activeunit.x + 100
        activeunit.y = activeunit.y - 100
    end
    

    Removing this fixes the problem of additional teleport distance.


    Some other tips:

    Prefer not x to x == false.

    Instead of maintaining the same control strings in multiple locations, making a control map is advisable, so you only have to change the control scheme in a single location. Then use, for example,love.keyboard.isDown(CONTROLS.LEFT) when you need it.

    local CONTROLS = {          
        UP = "w",      
        DOWN = "s",    
        LEFT = "a",                     
        RIGHT = "d",                    
        TELEPORT = "space"                  
    }
    

    Create a constructor function for your units as to avoid code repetition. You may find as the scope of your game increases, a more proper OOP pattern may help you stay organized.

    A quick refactoring (omitting some unused code for clarity):

    local love = require "love"
    
    local CONTROLS = {
        UP       = 'w',
        DOWN     = 's',
        LEFT     = 'a',
        RIGHT    = 'd',
        BOOST    = 'q',
        TELEPORT = 'space',
        SWITCH   = 'tab',
        QUIT     = 'escape'
    }
    
    local function create_unit(x, y, speed)
        return {
            x = x,
            y = y,
            speed = speed,
            dx = 0,
            dx = 0,
            radius = 10,
            teleport = 100
        }
    end
    
    local game = {
        units = { create_unit(50, 50, 100), create_unit(75, 75, 200) },
        activeunit = 1
    }
    
    function game.getactive()
        local nth = game.activeunit
        return game.units[nth], nth
    end
    
    local isdown = love.keyboard.isDown
    
    function love.update(dt)
        local unit = game.getactive()
        local dist = unit.speed * dt
    
        if isdown(CONTROLS.UP) then
            unit.y = unit.y - dist
            unit.dy = -1
        end
    
        if isdown(CONTROLS.DOWN) then
            unit.y = unit.y + dist
            unit.dy = 1
        end
    
        if isdown(CONTROLS.LEFT) then
            unit.x = unit.x - dist
            unit.dx = -1
        end
    
        if isdown(CONTROLS.RIGHT) then
            unit.x = unit.x + dist
            unit.dx = 1
        end
    
        if not isdown(CONTROLS.UP) and not isdown(CONTROLS.DOWN) then
            unit.dy = 0
        end
    
        if not isdown(CONTROLS.LEFT) and not isdown(CONTROLS.RIGHT) then
            unit.dx = 0
        end
    end
    
    local actions = {
        [CONTROLS.BOOST] = function (unit)
            unit.speed = unit.speed * 2
        end,
        [CONTROLS.SWITCH] = function (unit)
            game.activeunit = game.activeunit + 1
    
            if game.activeunit > #game.units then
                game.activeunit = 1
            end
        end,
        [CONTROLS.TELEPORT] = function (unit)
            local tp = unit.teleport
    
            unit.x = unit.x + tp * unit.dx
            unit.y = unit.y + tp * unit.dy
        end,
        [CONTROLS.QUIT] = love.event.quit
    }
    
    function love.keypressed(key)
        local action = actions[key]
    
        if action then
            action(game.getactive())
        end
    end
    
    function love.draw()
        local unit, nth = game.getactive()
    
        for n, unit in ipairs(game.units) do
            love.graphics.setColor(0.6, 0.7, 0.8, (n == nth) and 1 or 0.6)
            love.graphics.circle('fill', unit.x, unit.y, unit.radius)
        end
    
        love.graphics.setColor(1, 1, 1, 1)
        love.graphics.line(0, 500, 800, 500)
        love.graphics.print(unit.dx, 0, 500)
        love.graphics.print(unit.dy, 0, 515)
        love.graphics.print(unit.speed, 0, 530)
        love.graphics.print(unit.x, 0, 545)
        love.graphics.print(unit.y, 0, 560)
        love.graphics.print(nth, 0, 575)
    end
    

    game.gif