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
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