Hello, i just started working with LUA and Defold, and have been doing the tutorials. When i finished the movement tutorial, there were some extra exercises i wanted to complete. I want to make a movementspeed cap for my object, but i cant seem to solve it.
local acceleration = self.input * 200
local dv = acceleration * dt
local v0 = self.velocity
local v1 = self.velocity + dv
local movement = (v0 + v1) * dt * 0.5
local p = go.get_position()
go.set_position(p + movement)
self.velocity = v1
self.input = vmath.vector3()
I wonder if there is a way to assign a max and min value for my vector v1, which will make the movement varible have a max, and min value? Thanks for help!
Hey, sorry for bumping but it’s the first answer on google.
I’m new too to Defold and Lua, AND working with vector3.
I don’t get how to use min/max on vectors to limit speed.
I did something different:
if vmath.length_sqr(movement) > max_speed then --max_speed = 120
movement = vmath.normalize(movement) * max_speed
end
Only problem is that spaceship fly off when it reaches my supposed max_speed when it should have be limited to max_speed.
EDIT:
I noticed that I forgot about delta time multiplication (movement = vmath.normalize(movement) * max_speed * dt)
Got stuck on this myself, it’s still the first result on Google so I wanted to continue the thread.
I’ve already tried the above two methods. Comparing the movement’s length_sqr to the max speed causes the ship to increase in speed drastically, and using vmath.length just slows it down instead.
I’ve also considered using math.min, but I don’t know how you would use it in this context. You can’t use it on movement since it’s a vector, and the only scalar that changes speed is the acceleration, which never reaches max_speed.
Here’s my update function, pretty much identical to the tutorial aside from maybe 1 or 2 things:
if vmath.length_sqr(self.input) > 1 then
self.input = vmath.normalize(self.input)
end
local acceleration = self.input * self.speed * dt
local v0 = self.velocity
local v1 = self.velocity + acceleration
local movement = (v0 + v1) * dt * 0.5
local p = go.get_position()
go.set_position(p + movement)
self.velocity = v1
self.input = vmath.vector3()
on_input just changes self.input depending on key press.
Not exactly the same code as you had but it should handle direction, speed, acceleration and max speed:
go.property(acceleration, 100)
go.property(max_speed, 1000)
function update(self, dt)
-- there shouldn't really be any need to do this
local direction = vmath.normalize(self.input)
if vmath.length(direction) > 0 then
-- increase speed if we are moving in any direction
self.speed = self.speed + self.acceleration * dt
-- cap speed
self.speed = math.min(self.speed, self.max_speed)
else
-- decrease speed here when no key is pressed
-- how you want this to happen is up to you
-- speed = 0 immediately?
-- speed decreased every frame?
end
-- move the game object
local p = go.get_position()
p = p + direction * self.speed * dt
go.set_position(p)
-- reset input
self.input = vmath.vector3()
end
function on_input(self, action_id, action)
-- update self.input based on currently pressed keys
end
That’s much simpler than the tutorial code, thanks. Only thing I had to change was the first line of code, because direction was vmath.vector3(nan, nan, nan) for some reason. self.input was initialized to vmath.vector3().
Here’s my replacement code for that, for future reference:
local direction = vmath.vector3()
if vmath.length(self.input) > 0 then
direction = vmath.normalize(self.input)
end
Yeah, I looked at that example recently and found an issue at that line.
If you move left, then immediately try to move right, you get a self.direction of (-1, 0, 0) and a self.input of (1, 0, 0). This means that you end up adding the two together to get a zero vector, and try to normalize it. This sets self.direction to a vector of (nan, nan, nan), which breaks the script. I’d assume this happens with up and down too.
My quick and dirty solution was to check for a nan vector and replace it with a zero vector before it got reused:
-- nan is not equal with itself, so use this for comparison check
if self.direction.x ~= self.direction.x then
self.direction = vmath.vector3()
end
Good point. My bad! The idea was to allow for some inertia in changing direction, but that complicated the example (and introduced the bug you found). I’ve changed the example to directly use self.input as direction.
Here is my attempt to this. Note that probably this is not what you want. My idea is that, the closer to the “real world physics” the more the result is natural. On the other hand you pay this in some hardness in constant tuning.
go.property("speed_decay", 2.0)
go.property("force", 500.0)
function init(self)
msg.post(".", "acquire_input_focus")
self.input = vmath.vector3(0.0)
self.speed = vmath.vector3(0.0)
self.position = go.get_position(".")
end
function update(self, dt)
self.speed = self.speed + dt * self.force * self.input
self.speed = self.speed * (1.0 - self.speed_decay * dt)
self.position = self.position + dt * self.speed
go.set_position(self.position)
self.input = vmath.vector3(0.0)
end
function on_input(self, action_id, action)
if action_id == hash("up") then
self.input.y = 1.0
elseif action_id == hash("down") then
self.input.y = -1.0
elseif action_id == hash("left") then
self.input.x = -1.0
elseif action_id == hash("right") then
self.input.x = 1.0
end
local length = vmath.length(self.input)
if length > 0.1 then self.input = self.input / length end
end
EDIT: moved the normalization at the end of on_input; there is no need to check for normalization at each frame.
@britzl Thank you for formatting the code… Still I don’t understand how to do that, really!
Hey guys! Noob in Defold here!
This is my first post. Hopefully somebody will find it usefull.
Anyway, i think i figred out the most simple solution. Here’s what the whole code file look likes:
local max_speed = 200
function init(self)
msg.post(".", "acquire_input_focus")
self.velocity = vmath.vector3() -- [1]
self.input = vmath.vector3()
end
function update(self, dt)
if vmath.length_sqr(self.input) > 1 then
self.input = vmath.normalize(self.input)
end
local acceleration = self.input * 200 -- [2]
local dv = acceleration * dt -- [3]
local v0 = self.velocity -- [4]
local v1 = self.velocity + dv -- [5]
if vmath.length_sqr(v1) > max_speed*max_speed then
v1 = vmath.normalize(v1) * max_speed
print(v1)
end
local movement = (v0 + v1) * dt * 0.5 -- [6]
local p = go.get_position()
go.set_position(p + movement) -- [7]
self.velocity = v1 -- [8]
self.input = vmath.vector3()
end
function on_input(self, action_id, action)
if action_id == hash("up") then
self.input.y = 1 -- [1]
elseif action_id == hash("down") then
self.input.y = -1 -- [1]
elseif action_id == hash("left") then
self.input.x = -1 -- [1]
elseif action_id == hash("right") then
self.input.x = 1 -- [1]
elseif action_id == hash("click") and action.pressed then
print("CLICK!")
end
end
The only things i added to the code in the tuturial are:
local max_speed = 200
--and
if vmath.length_sqr(v1) > max_speed*max_speed then
v1 = vmath.normalize(v1) * max_speed
print(v1)
end
What i think i noticed is that some people forget that vmath.length_sqr(self.input) returns a squared value of components of vector3 (x^2 + y^2 + z^2). And if we want to compare it to some “max_speed” value, we should square it : max_speed*max_speed
I’m not at all sure if this is by any means an efficient solution and a “good practice”, but it looks rather simple and clean to me. And most importantly it works!!