Collision Issue: Player falling through the ground

Hello,

I’m trying to make a platformer game, so I started to learning how to use collision in Defold and how to resolve them. I also checked out the platformer tutorial and the pixelLinePlateformer exemple .

I was happy with my results until I found a bug…

It occurs when my player falls after a jump, sometimes it doesn’t stop on the ground and just goes through it. I tried to solve the probleme myself, but I haven’t found a solution yet. I think it mostly happens when the player falls from a great height.

bugCollision-ezgif.com-video-to-gif-converter

I’m using Defold 1.9.8. Below, you can find my player’s script, or you can check my repository on GitHub: GitHub - Kevallion/MetroidVania.

Thanks in advance for your help!

local input_left = hash("move_left")
local input_jump = hash("jump")
local input_right = hash("move_right")

local anim_run = hash("run")
local anim_idle = hash("idle")
local anim_jump = hash("jump")
local anim_fall = hash("fall")

-- physics variable
local gravity = -1800 
local acceleration = 8
local deceleration = 6
local jump_force = 600

local contact_point_response =  hash("contact_point_response")  
local group_ground_collision = hash("ground") 

-- function to update animation and save it
local function play_animation(self,pAnim)
	if self.anim ~= pAnim then
		sprite.play_flipbook("#sprite", pAnim)
		self.anim = pAnim
	end
end

-- function to update animation
local function update_animation(self)
	
	if self.dir ~= 0 then
		sprite.set_hflip("#sprite", self.dir < 0)
	end
	
	if self.isOnFloor then
		if math.abs(self.dir) > 0 then 
			play_animation(self, anim_run)
		else
			play_animation(self, anim_idle)
		end
	else
		local dir_velocity = vmath.normalize(self.velocity)
		if dir_velocity.y > 0 then 
			play_animation(self, anim_jump)
		elseif dir_velocity.y < 0 and self.velocity.y < -100 then
			play_animation(self, anim_fall)
		end
	end
end

function init(self)
	-- get_input focus
	msg.post(".", "acquire_input_focus")
	-- msg.post("@render:", "use_camera_projection")
	-- init variable
	self.anim = nil
	self.maxSpeed = 200
	self.jumpForce = jump_force
	self.velocity = vmath.vector3()
	self.correction = vmath.vector3()
	self.dir = 0
	self.isOnFloor = false
end

-- function to lerp the velocity x and apply the current velocity
local function move(self,dt)

	-- if the player is pressing left or right
	if self.dir ~= 0 then
		local desired_velocity_x = self.dir * self.maxSpeed
		self.velocity.x = vmath.lerp(dt * acceleration, self.velocity.x, desired_velocity_x)
	else
		self.velocity.x = vmath.lerp(dt * deceleration, self.velocity.x, 0)
	end
	
	-- apply velocity
	go.set_position(go.get_position() + (self.velocity * dt))
end

function fixed_update(self, dt)
	self.velocity.y = self.velocity.y + (gravity * dt)
	
	-- move player
	move(self, dt)
	update_animation(self)
	
	-- restet volatile variable
	self.dir = 0
	self.correction = vmath.vector3()
	self.isOnFloor = false
end

local NORMAL_THRESHOLD = 0.7

local function handle_obstacle_contact(self, normal, distance)

	if normal.y < NORMAL_THRESHOLD and normal.y > -NORMAL_THRESHOLD then
		normal.y = 0
	end
	
	if normal.x < NORMAL_THRESHOLD and normal.x > -NORMAL_THRESHOLD then
		normal.x = 0
	end
	
	if distance > 0 then 
		local projection = vmath.project(self.correction, normal * distance)
		
		if projection < 1 then
			local compensation = (distance - (distance * projection)) * normal
			go.set_position(go.get_position() + compensation)
			self.correction = self.correction + compensation
		end
	end
	
	if math.abs(normal.x)  > NORMAL_THRESHOLD then 
		self.velocity.x = 0
	end
	
	if normal.y > NORMAL_THRESHOLD then
			self.isOnFloor = true 
			self.velocity.y = 0
	end

	if normal.y < 0 then
		self.velocity.y = 0
	end
end

function on_message(self, message_id, message, sender)
	if message_id == contact_point_response then 
		if message.group == group_ground_collision then
			handle_obstacle_contact(self,message.normal, message.distance)
		end
	end
end

function on_input(self, action_id, action)
	if action_id == input_left then 
		self.dir = -1
	elseif action_id == input_right then 
		self.dir = 1
	end

	if action_id == input_jump and action.pressed then 
		if self.isOnFloor  then 
			self.velocity.y = self.jumpForce
			self.isOnFloor = false
		end
	end
end


You are not putting a limit to the vertical velocity which means that you your player character will from one frame to another fall through or into the ground far enough that you end up on the other side. You can solve this either by capping the vertical velocity to a reasonable value or by using a ray cast to detect downward collision.

4 Likes

Hi, thanks for your quick response. I tested clamping the value, and it works fine now.

2 Likes