Simple physics class - Handling collision

Hi everyone,

Just got back to defold after a long absence and excited to try new things!

I’ve been setting myself up to create a small class to handle physics in a new prototype i’m working on (I was surprised that there was nothing in the asset library for it actually).

Unfortunately, i’m facing an old nemesis: when colliding, the object slowly sink into the other one.
I’m using the collision_handler from one of the tutorial but it doesn’t work out.

I’ve put together a quick test project to isolate the issue, but can’t figure it out.

Could it be that the abstraction is taking too much time from the physic simulation?

The BODY class:

local class = require 'main.30log'

M = class("Body")

function M:init(position)
	self.position = position or vmath.vector()
	self.velocity = vmath.vector3()
	self.forces = {}
	self.correction = vmath.vector3()
	self.active = true
end

function M:update(dt)
	if self.active then
		self:calculatePhysics(dt)
                self.correction = vmath.vector3()
	end
end

function M:calculatePhysics(dt)
	
	local position = self.position
	local velocity = self.velocity
	
	local forces = self.forces

	local acceleration = vmath.vector3()
	for i, f in pairs(forces) do
		acceleration = acceleration + f
	end

	velocity = velocity + acceleration * dt
	position = position + velocity * dt

	self.velocity = velocity
	self.position = position
	
	return velocity, position
end

function M:impulse(f)
	self.velocity = self.velocity + f
end

function M:collision(c)
	self.handle_obstacle_contact(self, c.normal, c.distance)
end

function M.handle_obstacle_contact(self, normal, distance)
	-- project the correction vector onto the contact normal
	-- (the correction vector is the 0-vector for the first contact point)
	local proj = vmath.dot(self.correction, normal)
	-- calculate the compensation we need to make for this contact point
	local comp = (distance - proj) * normal
	-- add it to the correction vector
	self.correction = self.correction + comp -- CULPRIT!
	-- apply the compensation to the player character
	self.position = self.position + comp

	-- project the velocity onto the normal
	proj = vmath.dot(self.velocity, normal)
	-- if the projection is negative, it means that some of the velocity points towards the contact point
	if proj < 0 then
		-- remove that component in that case
		self.velocity = self.velocity - proj * normal
	end
end

return M

The PLAYER script:

local body = require 'main.body' -- Req the class

function init(self)
	self.body = body(go.get_position()) -- Add a BODY to the GO
	self.body.forces = {vmath.vector3(0, -1000, 0)} -- Add a constant force (Gravity)
end

function update(self, dt)
	self.body:update(dt) -- update the physics
	go.set_position(self.body.position) -- update position accordingly
end

function on_message(self, message_id, message, sender)
	if message_id == hash("contact_point_response") then
		self.body:collision(message) -- handle collision
	end
end

I thought that canceling the projection should be enough to stop it from sinking…

One of the reason i want to get my own physic class is also because i want to be able to throttle the physic update rate for doing slowmo… :slight_smile:

Thanks in advance for your help!

K

EDIT2: Thanks britzl!

2 Likes

The correction should be reset every frame.

3 Likes