Physic issues (again)

Ok, I started working on a pinball game. And I’m having issues with physics (again). It appears to me as if it’s buggy but it could be me.

Right now I just have a ball and a flipper and the behavior is off. I need this to work perfectly before I commit to this project so hopefully someone can help me out.

-- ball.script
local gravity = -981 -- Gravity force
local ball_mass = 1 -- Mass of the ball

function init(self)
	self.velocity = vmath.vector3(0, 0, 0) -- Initial velocity of the ball
	self.position = go.get_position() -- Get the initial position of the ball
end

function on_input(self, action_id, action)
	-- Input handling is not required for the ball
end

function update(self, dt)
	-- Apply gravity to the ball
	self.velocity.y = self.velocity.y + gravity * dt / ball_mass
	-- Update the ball's position based on its velocity
	self.position = self.position + self.velocity * dt
	-- Set the new position of the ball
	go.set_position(self.position)
end

function on_message(self, message_id, message, sender)
	if message_id == hash("contact_point_response") then
		-- Simple collision response
		-- Reflect the velocity vector based on the normal of the collision
		local normal = message.normal
		if normal and normal.x and normal.y then
			self.velocity = self.velocity - 2 * vmath.dot(self.velocity, normal) * normal
			-- If the collision was with the flipper, add the flipper's velocity
			if sender == hash("/right_flipper") then
				local flipper_velocity = go.get("/right_flipper", "velocity")
				self.velocity = self.velocity + flipper_velocity
			elseif sender == hash("/left_flipper") then
				local flipper_velocity = go.get("/left_flipper", "velocity")
				self.velocity = self.velocity + flipper_velocity
			end
		end
	end
end
-- flipper.script
go.property("flipper_side", "left")  -- Set this to "left" or "right" in the editor for each flipper

local flipper_speed = 400 -- Speed at which the flipper moves
local max_rotation = math.rad(60) -- Maximum rotation angle in radians

function init(self)
	msg.post(".", "acquire_input_focus") -- Acquire input focus to receive input events
	self.rotation = go.get_rotation() -- Get the initial rotation of the flipper
	self.active = false -- Is the flipper active (moving)?
end

function on_input(self, action_id, action)
	if self.flipper_side == "left" and action_id == hash("left_flipper") then
		self.active = action.pressed
	elseif self.flipper_side == "right" and action_id == hash("right_flipper") then
		self.active = action.pressed
	end
end

function update(self, dt)
	if self.active then
		local rotation_change = flipper_speed * dt
		if self.flipper_side == "left" then
			self.rotation.z = math.min(self.rotation.z + rotation_change, max_rotation)
		else
			self.rotation.z = math.max(self.rotation.z - rotation_change, -max_rotation)
		end
		go.set_rotation(self.rotation)
	end
end

And this is what I get:

What am I doing wrong? Shouldn’t this be consistent? And the ball should never pass through the flipper? All objects are kinematic. I’m not using dynamic physics.

Why not? I’d recommend it for a game that needs realistic physics like pinball.

A script property can’t be a string, the closest option is a hash. This should have given an error…

Game objects don’t have a velocity property, so this should error too.

2 Likes

The dynamic physics aren’t very good in defold. My last game the reactions were very very very slow. But also I am making this a roguelike game so I need to be able to control all aspects.

In regards to the flipper…I didn’t even do anything with it yet. Thanks for pointing that out. That is just so I can use the same GO and identify it with a property for controlling it.

ah…yes the /right_flipper. I was in between going from 2 game objects to one via property. But either way it gives the same physics. The error won’t throw unless I try to activate the flippers and as you can see I’m not even using the flippers other than them being stationary.

Thank you for reading this and pointing those out.

@britzl
BTW: I made the ball dynamic and the flippers static just to see if that made a difference. And this is just janky. WHat the heck am I doing wrong? I know defold wouldn’t allow a physics engine like this in the wild, but I have no clue what to do to fix this to make it 100% reliable/consistent.

Did you remove your own collision code as well? Maybe try enabling Bullet on the ball objects?

Thanks for the tip. I just removed my code. Not sure how that would change dynamic objects, but it did. And now my old issues from a previous game are back. Look how it slows down as it falls. Shouldn’t is speed up or at least remain constant as it falls? Velocity means it should be moving faster. But if the engine is saying it really has not moved far enough I think it would at least be moving the same speed not actually slow down as it does with dynamic objects.

@britzl This has to be a bug. I just now switched the physics engine from 2D to 3D and it actually performs like you’d think.

3D:

in 2D the objects look like they slow down for a soft landing.

Yeah, I’m not sure why it’d slow down. It looks like some force is still applied?

I’m not applying any force.

Can you please share a minimal project that we can take a look at?

Sure. As it’s minimal already :slight_smile:

There is actually no code on this (I have it all commented out) just in case the update or init was some how messing with the ball.

Run this then switch phyics to 3D and then it looks “right” use 2D physics and it has the soft landing look.
Pinball Roguelike.zip (69.3 KB)

I fixed my code and I’m using kinematic objects and it works as intended. But I really would like to know why the dynamic object with no code doesn’t work as expected. My first game I made felt “off” and people are saying it’s too slow. So I think this is the issue. Resolving this dynamic gravity issue will hopefully fix my first game to feel/play better.

Thank you

This is using kinematic objects and my code setting the gravity and response.

-- ball.script
local gravity = -981 -- Gravity force
local ball_mass = 1 -- Mass of the ball

function init(self)
	self.velocity = vmath.vector3(0, 0, 0) -- Initial velocity of the ball
	self.position = go.get_position() -- Get the initial position of the ball
	self.collision_response = vmath.vector3(0, 0, 0) -- New variable to store the collision response
end

function on_input(self, action_id, action)
	-- Input handling is not required for the ball
end

function fixed_update(self, dt)
	-- Apply gravity to the ball
	self.velocity.y = self.velocity.y + gravity * dt / ball_mass
	-- Apply the collision response
	self.velocity = self.velocity + self.collision_response
	self.collision_response = vmath.vector3(0, 0, 0) -- Reset the collision response
	-- Update the ball's position based on its velocity
	self.position = self.position + self.velocity * dt
	-- Set the new position of the ball
	go.set_position(self.position)
end

function on_message(self, message_id, message, sender)
	if message_id == hash("contact_point_response") then
		-- Simple collision response
		-- Reflect the velocity vector based on the normal of the collision
		local normal = message.normal
		if normal and normal.x and normal.y then
			local reflection = self.velocity - 2 * vmath.dot(self.velocity, normal) * normal
			-- If the collision was with the flipper, add the flipper's velocity
			if sender == hash("/flipper") then
				local flipper_velocity = go.get("/flipper", "velocity")
				reflection = reflection + flipper_velocity
			end
			-- Store the collision response
			self.collision_response = reflection - self.velocity
		end
	end
end

The issue is caused by the units used by the physics engine:

Setting your gravity to -900 and your scale to 0.01 will give you a satisfying result:

The ball is now 40 * 0.01 = 0.4 meters in diameter and the gravity is 900 * 0.01 = 9 m/s2.

The reason why there’s a difference between 2D and 3D is that you’re working with completely different scales. In a 3D game you can for instance have things that are 1 unit wide, for instance these dungeon pieces by Kay Lousberg:

When you position those walls and floor pieces you position them 1 unit apart, but when rendered in 3D using a perspective camera they obviously show as a few hundred pixels wide, depending on your camera settings.

In a 2D game you might have tiles in a tilemap that are 64x64 pixels and a player character that is 100x300 pixels. If 1 pixel translates to 1 meter in the physics world you obviously need to apply a scale to get the objects down to a size that the physics engine is designed for.

3 Likes

Thank you.

When I did “physics simulation” with manually moving objects, I found that collision detection was more accurate when using one trigger and one kinematic collision object instead of two collision objects. In this case, I took the collision point and the normal using a raycast.

1 Like

Thanks I’ll give that a shot. I wish they would update the physics engine to be more responsive. But maybe it’s a bridge too far. I’m not a physics dev so I’m not sure why it’s hard to ensure the collisions are processed in a timely manner.