(SOLVED) Issue with applying a force for dynamic player object jumping - Defold

Hi, I am making a platformer, and I have modelled my player with dynamic physics, as they will be interacting with blocks, pushing them etc. The movement works fine, but I am having trouble getting the jumping to work correctly?

Currently, when I press the jump key, the player performs a jump, but the magnitude is so small, even though the force is great. I have tried increasing the force, and reducing the mass of the player, but the issue still remains.

Any help would be greatly appreciated :slight_smile:

local right = hash("right") -- using a hash of the action_ids saves time
local left = hash("left") -- prehashing also saves resources, as it does not have to be hashed each time input detected
local jump = hash("jump")
local idle = hash("idle") -- prehashing of animation names, saves resources
local walk = hash("walk")

local current_anim = idle -- set to idle by default, as this is the starting animation
local r_colliding = false -- sets flags to false at start of level
local l_colliding = false -- sets flags to false at start of level
local on_ground = false -- is player currently on the ground

local jump_force = vmath.vector3(0,5000,0)

local x_movement = 200 -- not hardcoded, so can be changed if need be
local move_direction = 0


function init(self)
	msg.post(".", "acquire_input_focus") -- allows the script to handle inputs
end

local function animate(anim) -- dedicated for animations, improves modularity

	if current_anim ~= anim then -- checks that the animation is not already being played
		sprite.play_flipbook("#sprite", anim)   
		current_anim = anim -- updates the current animation
	end

	if move_direction == 1 then -- if facing right
		sprite.set_hflip("#sprite",false) -- flip -> false
	elseif move_direction == -1 then -- if facing left
		sprite.set_hflip("#sprite",true) -- flip -> true
	end

end

local function update_collision_state(message)
	if message.normal.x > 0.7 then -- if collision on left
		r_colliding = false
		l_colliding = true
	elseif message.normal.x < -0.7 then -- if collision on right
		r_colliding = true
		l_colliding = false
	end

	if message.normal.y > 0.7 then -- if colliding with the ground
		on_ground = true
	end
		
end

function on_message(self, message_id, message, sender) -- when the object recieves a message
	if message_id == hash("contact_point_response") then -- if collision, call function
		update_collision_state(message)
	end
end


function fixed_update(self, dt)

	local pos = go.get_position() -- Gets the current position
	pos.x = pos.x + x_movement * move_direction *  dt --  x_position + x_movement, * dir & diff between prev frame to ensure movement not dependent on fps
	go.set_position(pos) -- updates pos

	if move_direction == 1 and l_colliding then -- code to reset the values of l_colliding and r_colliding, so if moving right l_colliding to false
		l_colliding = false
	elseif move_direction == -1 and r_colliding then
		r_colliding = false
	end

	if (move_direction == 1 and not r_colliding) or (move_direction == -1 and not l_colliding) then -- code to play correct animation, if player walking and not colliding in same direction
		animate(walk)
	else 
		animate(idle)
	end

end

function on_input(self, action_id, action)
	-- If the key is released, stop movement
	if action.released then
		if action_id == right and move_direction == 1 then
			move_direction = 0 -- Stop moving right when right is released
		elseif action_id == left and move_direction == -1 then
			move_direction = 0 -- Stop moving left when left is released
		end
	else
		-- Handle right input
		if action_id == right and move_direction == 0 then
			move_direction = 1 -- Move right if no direction is currently active
			-- Handle left input
		elseif action_id == left and move_direction == 0 then
			move_direction = -1 -- Move left if no direction is currently active
		end

	end

	if action_id == jump and on_ground then -- if jump action, and player on ground then jump is true
		msg.post("#collisionobject", "apply_force",{
			force = jump_force,
			position = go.get_world_position()
		})
		on_ground = false -- player no longer on the ground
    end
end

I would recommend that you switch over to the b2d.* set of functions to get more granular control over box2d. There’s a tutorial on box2d jump physics here:

https://www.iforce2d.net/b2dtut/jumping

As you can see the tutorial recommends to apply an impulse instead of a force. You have both options available in the b2d namespace:

1 Like

Thank you for this, I’m not quite sure what to enter for each of the arguments here.

b2d.body.apply_linear_impulse(body,jump_force,pos)

As for the body, I have tried setting that to:

local body = b2d.get_body("#collisionobject")

but the game crashes, apologies, I’m not familiar with how box2d works.

As for the jump_force, I have used a vector_3, but I don’t think that this is right either:

local jump_force = vmath.vector3(0,5000,0)

The game shouldn’t crash. Could you report the callstack from the crash?

ERROR:SCRIPT: Assets/player.script:6: no instance could be found in the current script environment
stack traceback:
  [C]:-1: in function get_body
  Assets/player.script:6: in main chunk

WARNING:RESOURCE: Unable to create resource: /Assets/player.scriptc: FORMAT_ERROR
WARNING:RESOURCE: Unable to create resource: /_generated_757f44b5.goc: FORMAT_ERROR
ERROR:GAMEOBJECT: Could not instantiate game object from prototype /_generated_757f44b5.goc.
WARNING:RESOURCE: Unable to create resource: /main/main.collectionc: FORMAT_ERROR

It definitely seems to be that body line, as when I remove it the crashing stops

Right, it’s not what we call a crash (we mean if it crashes the engine and the program stops running)

What does your script look like?
Are you calling it from within the scope of a life cycle function?

Ah I see, thank you for the clarification there.

I have not called it within the scope of a life cycle function, I have called at the very top of the code

local right = hash("right") -- using a hash of the action_ids saves time
local left = hash("left") -- prehashing also saves resources, as it does not have to be hashed each time input detected
local jump = hash("jump")
local idle = hash("idle") -- prehashing of animation names, saves resources
local walk = hash("walk")
local body = b2d.get_body("#collisionobject")

local current_anim = idle -- set to idle by default, as this is the starting animation
local r_colliding = false -- sets flags to false at start of level
local l_colliding = false -- sets flags to false at start of level
local on_ground = false -- is player currently on the ground

local jump_force = vmath.vector3(0,500,0)

local x_movement = 200 -- not hardcoded, so can be changed if need be
local move_direction = 0


function init(self)
	msg.post(".", "acquire_input_focus") -- allows the script to handle inputs
end

local function animate(anim) -- dedicated for animations, improves modularity

	if current_anim ~= anim then -- checks that the animation is not already being played
		sprite.play_flipbook("#sprite", anim)   
		current_anim = anim -- updates the current animation
	end

	if move_direction == 1 then -- if facing right
		sprite.set_hflip("#sprite",false) -- flip -> false
	elseif move_direction == -1 then -- if facing left
		sprite.set_hflip("#sprite",true) -- flip -> true
	end

end

local function update_collision_state(message)
	if message.normal.x > 0.7 then -- if collision on left
		r_colliding = false
		l_colliding = true
	elseif message.normal.x < -0.7 then -- if collision on right
		r_colliding = true
		l_colliding = false
	end

	if message.normal.y > 0.7 then -- if colliding with the ground
		on_ground = true
	end
		
end

function on_message(self, message_id, message, sender) -- when the object recieves a message
	if message_id == hash("contact_point_response") then -- if collision, call function
		update_collision_state(message)
	end
end


function fixed_update(self, dt)

	local pos = go.get_position() -- Gets the current position
	pos.x = pos.x + x_movement * move_direction *  dt --  x_position + x_movement, * dir & diff between prev frame to ensure movement not dependent on fps
	go.set_position(pos) -- updates pos

	if move_direction == 1 and l_colliding then -- code to reset the values of l_colliding and r_colliding, so if moving right l_colliding to false
		l_colliding = false
	elseif move_direction == -1 and r_colliding then
		r_colliding = false
	end

	if (move_direction == 1 and not r_colliding) or (move_direction == -1 and not l_colliding) then -- code to play correct animation, if player walking and not colliding in same direction
		animate(walk)
	elseif on_ground then
		animate(idle)
	end

end

function on_input(self, action_id, action)
	-- If the key is released, stop movement
	if action.released then
		if action_id == right and move_direction == 1 then
			move_direction = 0 -- Stop moving right when right is released
		elseif action_id == left and move_direction == -1 then
			move_direction = 0 -- Stop moving left when left is released
		end
	else
		-- Handle right input
		if action_id == right and move_direction == 0 then
			move_direction = 1 -- Move right if no direction is currently active
			-- Handle left input
		elseif action_id == left and move_direction == 0 then
			move_direction = -1 -- Move left if no direction is currently active
		end

	end

	if action_id == jump and on_ground then -- if jump action, and player on ground then jump is true
		local pos = go.get_world_position()
		b2d.body.apply_linear_impulse(body,jump_force,pos)
		on_ground = false -- player no longer on the ground
	end
end

This error means that you are calling it outside of a life cycle function, which is not supported.

I’m still having trouble unfortunately, as the maximum height seems to have some sort of cap? The player is jumping really low (too low to overcome the obstacles). I have tried playing with the mass, and adjusting the gravity, but the gravity is already at a desirable level, around -300.

local right = hash("right") -- using a hash of the action_ids saves time
local left = hash("left") -- prehashing also saves resources, as it does not have to be hashed each time input detected
local jump = hash("jump")
local idle = hash("idle") -- prehashing of animation names, saves resources
local walk = hash("walk")


local current_anim = idle -- set to idle by default, as this is the starting animation
local r_colliding = false -- sets flags to false at start of level
local l_colliding = false -- sets flags to false at start of level
local on_ground = false -- is player currently on the ground

local jump_force = vmath.vector3(0,100,0)

local x_movement = 200 -- not hardcoded, so can be changed if need be
local move_direction = 0


function init(self)
	msg.post(".", "acquire_input_focus") -- allows the script to handle inputs
end

local function animate(anim) -- dedicated for animations, improves modularity

	if current_anim ~= anim then -- checks that the animation is not already being played
		sprite.play_flipbook("#sprite", anim)   
		current_anim = anim -- updates the current animation
	end

	if move_direction == 1 then -- if facing right
		sprite.set_hflip("#sprite",false) -- flip -> false
	elseif move_direction == -1 then -- if facing left
		sprite.set_hflip("#sprite",true) -- flip -> true
	end

end

local function update_collision_state(message)
	if message.normal.x > 0.7 then -- if collision on left
		r_colliding = false
		l_colliding = true
	elseif message.normal.x < -0.7 then -- if collision on right
		r_colliding = true
		l_colliding = false
	end

	if message.normal.y > 0.7 then -- if colliding with the ground
		on_ground = true
	end
		
end

function on_message(self, message_id, message, sender) -- when the object recieves a message
	if message_id == hash("contact_point_response") then -- if collision, call function
		update_collision_state(message)
	end
end


function fixed_update(self, dt)
	

	local pos = go.get_position() -- Gets the current position
	pos.x = pos.x + x_movement * move_direction *  dt --  x_position + x_movement, * dir & diff between prev frame to ensure movement not dependent on fps
	go.set_position(pos) -- updates pos

	if move_direction == 1 and l_colliding then -- code to reset the values of l_colliding and r_colliding, so if moving right l_colliding to false
		l_colliding = false
	elseif move_direction == -1 and r_colliding then
		r_colliding = false
	end

	if (move_direction == 1 and not r_colliding) or (move_direction == -1 and not l_colliding) then -- code to play correct animation, if player walking and not colliding in same direction
		animate(walk)
	elseif on_ground then
		animate(idle)
	end

end

function on_input(self, action_id, action)
	
	
	-- If the key is released, stop movement
	if action.released then
		if action_id == right and move_direction == 1 then
			move_direction = 0 -- Stop moving right when right is released
		elseif action_id == left and move_direction == -1 then
			move_direction = 0 -- Stop moving left when left is released
		end
	else
		-- Handle right input
		if action_id == right and move_direction == 0 then
			move_direction = 1 -- Move right if no direction is currently active
			-- Handle left input
		elseif action_id == left and move_direction == 0 then
			move_direction = -1 -- Move left if no direction is currently active
		end

	end

	if action_id == jump and on_ground then -- if jump action, and player on ground then jump is true
		local body = b2d.get_body("#collisionobject")
		local pos = go.get_world_position()
		b2d.body.apply_linear_impulse(body,jump_force*1000,pos)
		on_ground = false -- player no longer on the ground
	end
end

If anyone knows a solution to this, it would be much appreciated :slight_smile:

Can you please share your project or a minimal repro case as a zip file?

I tried your script in a sample project of my own. I set physics gravity to -1000 and physics scale to 0.01. When jumping I apply a vertical impulse of 600 times body mass. Everything behaves as expected as far as I can tell.

Adjusting it to these settings, to the one you mentioned - this seems to have solved the problem. I think the physics scale was perhaps too high, and the gravity too low. Thank you :slight_smile:

1 Like