I’m starting out with Lua and Defold though I have made a couple of games using Stencyl.
I’m trying to move an object in a very simple way (no acceleration, just starting and stopping upon key press and key release in four directions (for a top-down RPG style movement)). I’ve been playing with the logo that appears in the blank slate project (and copying code from the platformer tutorial).
At the moment I’ve got as far as printing self.velocity but the logo doesn’t actually move:
if action_id == hash(“left”) and action.pressed then
self.velocity = - 100
print(self.velocity)
It’s worth mentioning that the second part of my code (below) successfully makes the logo “jump”:
elseif action_id == hash("right") and action.pressed then
pos.x = pos.x + 100
go.set_position(pos)
I did think that self.velocity would be automatically recognised by the box2d physics engine. It appears that is not the case. Is all movement just done by “pos.x = pos.x + 100” then?
Wow, I was just watching one of your tutorials. Thanks for stepping in. I got the movement work nice and smooth and even made a little progress with the other parts of my games, so it’s been a useful afternoon for me.
I’m trying to get it so that you can only move Left if you’re not already moving in any other direction. At the moment, if you hold down left and then press right, the character stops, or jerks about a bit.
Here’s my code. Any help?
function on_input(self, action_id, action)
if action_id == hash("left") and action.pressed and Xmovement ~= ("right") then
Xmovement = "left"
elseif action_id == hash("right") and action.pressed and Xmovement ~= ("left") then
Xmovement = "right"
end
if action_id == hash("left") and action.released then
Xmovement = "0"
end
if action_id == hash("right") and action.released then
Xmovement = "0"
end
Here’s some code for very neat four way movement based on the arrow keys. My next step is not allowing the movement to stop unless the remainder of the position divided by tile size is 0 (which is tile based movement). I love lua!
function update(self, dt)
local pos = go.get_position()
print (Xmovement)
print (Ymovement)
if Xmovement == "left" then
pos.x = pos.x - 5
go.set_position(pos)
elseif Xmovement == "right" then
pos.x = pos.x + 5
go.set_position(pos)
end
if Ymovement == "up" then
pos.y = pos.y + 5
go.set_position(pos)
elseif Ymovement == "down" then
pos.y = pos.y - 5
go.set_position(pos)
end
end
function on_input(self, action_id, action)
if Xmovement == ("0") and Ymovement == ("0") and action_id == hash("left") and action.pressed then
Xmovement = "left"
elseif Xmovement == ("0") and Ymovement == ("0") and action_id == hash("right") and action.pressed then
Xmovement = "right"
end
if Xmovement == ("0") and Ymovement == ("0") and action_id == hash("up") and action.pressed then
Ymovement = "up"
elseif Xmovement == ("0") and Ymovement == ("0") and action_id == hash("down") and action.pressed then
Ymovement = "down"
end
if action_id == hash("right") and action.released and Xmovement == ("right") then
Xmovement = "0"
end
if action_id == hash("left") and action.released and Xmovement == ("left") then
Xmovement = "0"
end
if action_id == hash("up") and action.released and Ymovement == ("up") then
Ymovement = "0"
end
if action_id == hash("down") and action.released and Ymovement == ("down") then
Ymovement = "0"
end
end
You should make use of dt (delta time) received in update() to adjust the distance moved to reflect elapsed time since last frame. Now you always move 5 pixels per frame regardless if the frame took 0.016 seconds or 1 second to render.
I’ve wrapped the input handling that you are doing in your code above in a utility module in my Ludobits library. You can see an example of how it can be used here:
Using my input module your snippet of code could be simplified down to the following:
local input = require "ludobits.m.input"
function update(self, dt)
local pos = go.get_position()
if input.is_pressed(hash("left")) then
pos.x = pos.x - 5
elseif input.is_pressed(hash("right")) then
pos.x = pos.x + 5
end
if input.is_pressed(hash("up")) then
pos.y = pos.y + 5
elseif input.is_pressed(hash("down")) then
pos.y = pos.y - 5
end
go.set_position(pos)
end
function on_input(self, action_id, action)
input.update(action_id, action)
end
Wow Britzl, I’m glad you were able to make something useful out of my code.
I’m curious about what you mentioned about dt. I think it wouldn’t work for me, as it’s important that my characters move in regular increments. This is because I am doing grid-based movement. At the moment, I have it set up so that even if you release the UP key (or press any other arrow key), your character will keep moving up at a rate of 5 pixels per frame until “pos.y % 250 == 0 and pos.x % 250 ==0” where pos is the location (and 250 is a multiple of speed).
If I used your delta time code, then I wouldn’t be able to use that coding as the pos.y would change irregularly depending on the delta time, (instead of neatly changing by 5 pixels) right?
I’m including my code in case it is useful for anybody else.
-- THIS CODE GETS INPUT FOCUS, AND CREATES SOME VARIABLES
function init(self)
msg.post(".", "acquire_input_focus")
local pieces = { "go", "go2" }
up = 0
down = 0
left = 0
right = 0
speed = 5
tilesize = 200
end
--THIS CODE TAKES THE INPUT
function on_input(self, action_id, action)
if action_id == hash("left") and action.pressed then
left =1
elseif action_id == hash("left") and action.released then
left =0
end
if action_id == hash("right") and action.pressed then
right =1
elseif action_id == hash("right") and action.released then
right =0
end
if action_id == hash("up") and action.pressed then
up =1
elseif action_id == hash("up") and action.released then
up =0
end
if action_id == hash("down") and action.pressed then
down =1
elseif action_id == hash("down") and action.released then
down =0
end
end
--THIS CODE CREATES A STRING BASED ON THE INPUT AND TURNS IT INTO ONE "direction" VARIABLE (U,D,L,R or 0)
--IT ALSO DOESN'T ALLOW YOU TO CHANGE DIRECTION OR STOP UNLESS ALIGNED TO THE GRID
function update(self, dt)
pos = go.get_position()
inputstring = (up..down..left..right)
print(direction)
if inputstring == "1000" and pos.x%tilesize==0 and pos.y%tilesize==0 then
direction = "u"
elseif inputstring == "0100" and pos.x%tilesize==0 and pos.y%tilesize==0 then
direction = "d"
elseif inputstring == "0010" and pos.x%tilesize==0 and pos.y%tilesize==0 then
direction = "l"
elseif inputstring == "0001" and pos.x%tilesize==0 and pos.y%tilesize==0 then
direction = "r"
elseif inputstring == "0000" and pos.x%tilesize==0 and pos.y%tilesize==0 then
direction = "0"
end
--THIS CODE TURNS THE "direction" variable into movement
if direction == "u" then
pos.y = pos.y + speed
go.set_position(pos)
elseif direction == "d" then
pos.y = pos.y - speed
go.set_position(pos)
elseif direction == "l" then
pos.x = pos.x - speed
go.set_position(pos)
elseif direction == "r" then
pos.x = pos.x + speed
go.set_position(pos)
end
end
Some notes on the abovecode:
This code is perfect for tilebased movement, like in an RPG (think early zelda, or the first pokemon games).
The tilesize variable defines the size of one square in the grid.
speed defines the speed.
it’s vital that tilesize is a multiple of speed, and that the character starts on the grid. (x and y position must be a multiple of tilesize)
it doesn’t use delta time. Speed is pixels per frame, regardless of dt.
Ok, but if you do not use dt you must be ok with the fact that on a slow system it will take longer for the game object to move the required distance than on a system that manages 60 fps (or whatever value you have specified in game.project). If you want to move the game object smoothly a fixed distance based on the user pressing a directional key I’d consider animating using go.animate() instead of moving in the update() function.
I might look into that if i run into problems later.
Does anyone have any other ideas about grid based movement and how best to manage it? In many games that use animated values, i find that there is a short pause after the character reaches one square on the grid before starting another square, which I don’t like too much.
I’d keep a list of the grid tiles/cells to visit and immediately when the next cell in the list is reached I’d start moving towards the next one. There really should be no delay in that case. Maybe the game you are referring to is doing some kind of path finding calculation between each cell and for some reason delaying slightly? Or maybe enemy units are processed in-between each grid cell?
One problem is that tweening position results in some unusual numbers.
My character can’t start a movement unless x.position%200 = 0 and when you use animate, the object sometimes only moves 199.2131823. That’s relatively easy to solve by being more forgiving with the grid.
The other problem is, well, one I can’t explain. I’m using the animation now (and using the easing_linear function) but there’s some weird physics going on. The second half of the tween takes much longer than the first half.