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:

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.