Best approach to a pinball flipper

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Fabiofrosaa

Hello everyone,

I’m making a 3d pinball in Godot.
Right now I have the ball as a Rigid Body and the flippers as kinematic bodies controlled by the folling code in the _physics_process:

rotation_degrees.y = Input.get_action_strength(“L2”) * 60

This way so I can have the analog trigger control the angle of the flipper.
It is working almost fine. I can control gently the rotation of the flipper and the collision with the ball happens ok most of the times. But when press the trigger quickly the collision doesn’t happen and the flipper passes over the ball. Just some times it works as expected.

What can I do about it?

There is another detail, I am using a custom shape for the collision shape of the kinematic body, there is an error message saying the collision shape is not supported althought it works just fine, considering what I said before. I’ve tried a simple box shape collision But I got the same problem.

:bust_in_silhouette: Reply From: johnygames

Here are a few tips you can try:

  1. Go to your KinematicBody’s properties and tweak the ‘Safe Margin’ setting. That might work.

  2. Increase the width and height of your collision shapes

  3. Go to your RigidBodies’ property tab and check the ‘Continuous Cd’ box. That will make collision detection a bit more precise.

Btw, did you manage to make the ball not fly away like crazy when slightly touched by the flippers?

Thanks for the tips, Johny.

I’ve tried it all with no luck. I can’t really tell if they made any difference.

For some tests at the beggining the ball was flying like crazy indeed. But I believe it was due to me using a gravity scale of 3 on the rigid body of the ball. Also, I made the table much larger.

I am making the table with a grid and a simple mesh library, which has been a good experience. I kept the gravity at 9.8 but the gravity vector is y = -1 and z = 0.6, so I have the effect of the ball falling as if the table was tilted. Above the table I put a simple box collision, because most of the time the ball was flying off from the top.

Sometimes the ball goes throught the walls. That is rare and the continuous cd seems to have fixed it.

Also the flippers works fine most of the time. But when I wait the ball to be right above it and I hard press the trigger, the flipper kind of teleports to the total rotation and the balls stays there at the same place, before starting to fall.

It makes sense since I’m setting the rotation values. I’m actually impressed how it works just fine when I press the triggers more gently.

I believe the solution would be changing the rotation in increment steps, interpolating the values using the delta somehow. But I can’t figure that out. Something like a linear interpolation could do it. But I struggle still with some math. For vectors I use the methods godot provides, but what can I use for floats?

I’ve seen in the docs the formula:
interpolation = A * (1 - t) + B * t
which is the same as:
interpolation = A + (B - A) * t

In my case A = 0 , B = 60, t = Input.get_action_strength(“L2”).
So:
0 + (60 - 0) * Input.get_action_strength(“L2”)
or simply
60 * Input.get_action_strength(“L2”)
which is quite obvious

That doesn’t help as in practice I am still setting the values directly, not interpolating them inside the engine.

Fabiofrosaa | 2019-07-18 11:14

Have you tried the built-in method ‘lerp’?

It works by taking your current value as the first parameter, the value that you want to end up with as the second, and a weight factor which controls how fast you interpolate from current value to end value. Check the docs for ‘lerp’ @GDScript — Godot Engine (3.1) documentation in English

So you would use it like this:
rotation.y = lerp(rotation.y, rotation.y+5, 0.1)

where rotation can be any value you want.

johnygames | 2019-07-18 15:24

Thanks, Johny!

That really helped. Somehow I thought the lerp method was only for vectors.
Using it the movement is smooth. I works almost perfectly. This is the line of code:

rotation_degrees.y = lerp(rotation_degrees.y, Input.get_action_strength(“L2”) * 60, Input.get_action_strength(“L2”) * 0.15 + 0.15 )

This way the lerp weight is higher the more you press the triiger, with a minimum weight of 0.15 and a maximum of 0.3
I really like the result, but the major issue is still present, even if less frequent:

When the ball is touching the tip of the flipper and I hard press the trigger, the flipper changes rotation so quikly that it teleports over the ball. If I reduce the lerp weight enough the problem goes away, but then the flipper gets so slow that it loses the purpose of being a pinball flipper. The values I mentioned are the ones I found out to be the best, but the problem continues. It is rare but it is game breaking.

Fabiofrosaa | 2019-07-19 16:39

It might seem like a noobish way to do this, but what if you increased the size of the collision shapes of the flippers? Not the meshes, just the collsion shapes. Then you could place them under the flippers and your system would finally detect collisions properly.

Here’s how it works: The game tests to see if there are any collisions in every frame. Now, if your object happens to be bigger than the collider or moves too fast (as is in your case), then the object in question passes through the collider because it skips this collision check. In other words, your object travels in a frame a distance greater than the width and height of the collider it is supposed to collide with.

I just tested this hypothesis with a system I made, where the player (who is holding a very long stick) faces towards the cursor. My character’s stick passes through walls if they are too thin. BUT, if I make the collision shape of the wall much MUCH bigger in all directions, the stick no longer passes through no matter how fast it rotates or moves. I checked this visually and I also implemented the get_slide_collision(0) method, which returns various info about the collider. You can use the get_slide_collision(0) method to obtain the collider_id etc.

Scaling up your collsion boxes shouldn’t be a problem, since your flippers are (presumably) the last object at the bottom of your scene. So you wouldn’t have a problem with anything coming from below them and hitting an invisible wall. Scale their collision objects up on all axes so that their width and height is much bigger than the ball. Then place those collsion shapes just under your flippers. Try it and tell me if it worked. Remember: make them thick.

If this fails for some reason, another solution would be to use raycasting so as to spot objects that lie ahead of the ball. A ray would be projected in front of the ball towards the direction it is moving and it would report any obstacles ahead of time. Then using this info you could code the desired behaviour. You could even create more than one rays facing slightly different directions just for good measure.

johnygames | 2019-07-19 22:18

Man, that makes perfect sense.

I’ll try and do it.
I’m just short on time right now. When I test it I’ll report back.

Thanks for all the help.

Fabiofrosaa | 2019-07-20 13:44