Pinball Problems :)

My problem:

When the ball moves pretty fast (going downwards), and the flipper is moving, the ball sometimes passes through the flipper altogether.

I’ve implemented the movement of the flipper by having a joint with a motor, and in all other ways this seems to work well, but I can’t seem to get around this problem.

  • If I slow the motor speed down, the flipper doesn’t push the ball enough.

  • I have set 60 FPS, and that seemed to make the problem less common, but it still occurs pretty often.

  • I’ve fiddled with PositionIterations and VelocityIterations, but those have no noticable effect

-Not sure if there’s a solution to this, but I thought I’d ask - thanks for making a great tool!

  

You can try adding “isBullet” value to the ball (and then the flippers if just the ball doesn’t work).

https://docs.coronalabs.com/api/type/Body/isBullet.html

Box2D stops monitoring for collisions when an object hasn’t been active in a certain period of time and this may result in fast moving objects to pass through idle/sleeping objects because it takes them some time to wake up. Adding “isBullet” to the bodies makes them stay active all the time.

Thank you for your reply - I forgot to mention that I already have isBullet set, both on the flippers and the balls.

I’ve been trying to come up with some way to get around this problem, but right now I’m stumped.

I’d really like to make a decent 2D pinball game for mobile, in the vein of Pinball Dreams

Cheers!

Can you make a small sample project that demonstrates your problem?

I can give you a condensed version of the project - it’s a good deal of ugly code, but it’s probably needed to replicate the problem, if you have the patience.

It occurs sometimes when the bat is moving, and it’s supposed to hit a ball travelling at high speed

Cheers! :slight_smile:

main.lua

[lua]

system.activate( “multitouch” )

local physics = require “physics”

physics.setDrawMode( “hybrid” )

physics.start()

physics.setTimeStep( 0 )

physics.setContinuous(enabled) 

physics.setGravity( 0, 44)

physics.setPositionIterations( 48 )

physics.setVelocityIterations( 64 )

local flipSpeed = 1000

local frameGroup = display.newGroup()

frameGroup.anchorX = 0.5

– walls and stuff

physics.addBody(display.newRect(frameGroup,-5,-1000,2200,20),“static”,{ density = 1.0, friction = 0.5, bounce = 0.7})

physics.addBody(display.newRect(frameGroup,-5,-5,20,2000),“static”,{ density = 1.0, friction = 0.5, bounce = 0.7})

physics.addBody(display.newRect(frameGroup,1024,-5,20,2024),“static”,{ density = 1.0, friction = 0.5, bounce = 0.7})

physics.addBody(display.newRect(frameGroup,-5,1000,2209,20),“static”,{ density = 1.0, friction = 0.5, bounce = 0.7})

local leftSlide = display.newRect(frameGroup,207,487,270,20)

leftSlide.rotation =20

physics.addBody(leftSlide,“static”,wallMaterial)

local ldown = display.newRect(frameGroup,80,325,10,250)

physics.addBody(ldown,“static”,wallMaterial)

local rleftSlide = display.newRect(frameGroup,808,490,270,20)

rleftSlide.rotation =-20

physics.addBody(rleftSlide,“static”,wallMaterial)

local rldown = display.newRect(frameGroup,945,325,10,250)

physics.addBody(rldown,“static”,wallMaterial)

local topSlide = display.newRect(frameGroup,800,-480,500,-10)

topSlide.rotation =20

topSlide.x = 500

physics.addBody(topSlide,“static”,{ density = 1.0, friction = 0.5, bounce = 0.7})

topSlide.level = 2

local topSlide2 = display.newRect(frameGroup,800,-650,400,-10)

topSlide2.rotation =-20

topSlide2.x = 700

physics.addBody(topSlide2,“static”,{ density = 1.0, friction = 0.5, bounce = 0.7})

  

— flippers

local lx = 330

local ly = 532

local lfShape = {

-65,-20, 

60,-10, 

60,10, 

-65,20

}

local leftFlipper = display.newPolygon( frameGroup, lx, ly, lfShape )

leftFlipper.x = lx+83

leftFlipper.y = ly+15

local leftPivot = display.newCircle(frameGroup, lx+15, ly+15, 20 )

physics.addBody( leftFlipper, “dynamic”, { density=10.0, friction=0.8, bounce=0.2, shape=lfShape } )

physics.addBody( leftPivot, “static”, { density=2, radius=5, friction=2.5, bounce=0.5} )

leftFlipper.isBullet = true

local leftJoint = physics.newJoint( “pivot”, leftPivot, leftFlipper, leftPivot.x, leftPivot.y )

leftJoint.isLimitEnabled = true

leftJoint:setRotationLimits( -20, 15)

leftJoint.isMotorEnabled = true

leftJoint.maxMotorTorque = 100000

leftJoint.motorTorque = 70000

leftFlipper.isBullet = true

local rx = 690

local ry = 532

local rfShape = { 60,-20, -60,-10, -60,10, 60,20}

local rightFlipper = display.newPolygon( frameGroup, rx, ry, rfShape )

rightFlipper.x = rx-82

rightFlipper.y = ry+15

local rightPivot = display.newCircle(frameGroup, rx-15, ry+15, 20 )

physics.addBody( rightFlipper, “dynamic”, { density=10.0, friction=0.8, bounce=0.1, shape=rfShape } )

physics.addBody( rightPivot, “static”, { density=2, radius=5, friction=2.5, bounce=0.5} )

local rightJoint = physics.newJoint( “pivot”, rightPivot, rightFlipper, rightPivot.x, leftPivot.y )

rightJoint.isLimitEnabled = true

rightJoint:setRotationLimits( -15, 20)

rightJoint.isMotorEnabled = true

rightJoint.maxMotorTorque = 100000

rightJoint.motorTorque = 70000

rightFlipper.isBullet = true

local ball = display.newCircle(frameGroup, 0, 0, 20);

ball.x, ball.y = 90, 222;

physics.addBody(ball, “dynamic”, {friction = 0.5, density = 2.5, bounce = 0, radius = 20});

ball.isBullet = true;

local function flipLeft()

if(leftFlipper.active == false) then

leftFlipper.active = true

end

end

local function flipRight()

if(rightFlipper.active == false) then

rightFlipper.active = true

end

end

local function dropLeft()

leftFlipper.active = false

leftJoint.motorSpeed = flipSpeed

end

local function dropRight()

rightFlipper.active = false

rightJoint.motorSpeed = -flipSpeed

end

function leftFlipper:enterFrame()

  if self.active then

leftJoint.motorSpeed = -flipSpeed

  end

end

Runtime:addEventListener(“enterFrame”, leftFlipper);

function ball:enterFrame()

  if (ball.y > 900) then

ball.x = 130

ball.y = 0

  end

  frameGroup.y = -ball.y + 400

end

Runtime:addEventListener(“enterFrame”, ball);

function rightFlipper:enterFrame()

  if self.active then

rightJoint.motorSpeed = flipSpeed

  end

end

Runtime:addEventListener(“enterFrame”, rightFlipper);

local function listener(event) 

if(event.phase == “began” or event.phase == “moved”) then

if(event.x > 600) then

flipRight()

else

flipLeft()

end

elseif(event.phase == “ended” or event.phase == “cancelled”) then

if(event.x > 600) then

dropRight()

else

dropLeft()

end

end

  

return true 

end 

local function onKeyEvent( event )

    local phase = event.phase

    local keyName = event.keyName

    local returnValue = false

print(phase)

if(phase == “down”) then

if(keyName == “right”) then

flipRight(event)

end

if(keyName == “left”) then

flipLeft(event)

end

elseif(event.phase == “up”) then

if(keyName == “right”) then

dropRight()

end

if(keyName == “left”) then

dropLeft()

end

end

end

– Add the key callback

Runtime:addEventListener( “key”, onKeyEvent )

Runtime:addEventListener( “touch”, listener )

[/lua]

build.settings:

[lua]

settings =

{

orientation =

    {

        default = “landscapeRight”,

supported =

        {

            “landscapeRight”

        }

    }

}

[/lua]

config.lua

[lua]

application =

{

launchPad = false,

    content =

    {

fps = 60,

width = 600,

        height = 1024,

scale = “letterbox”,

        antialias = true,

    },

}

[/lua]

So, this is again one of those cases that would have been nearly impossible to diagnose without seeing your code. Also, it is much easier for others if you just zip the necessary code files and upload them here to the forums. You can do so in the “More Reply Options”.

Your entire issue has to do with improperly created display objects , for instance,

local topSlide2 = display.newRect(frameGroup,800,-650,400,-10)

You’ve set the height of the rectangle to minus 10. A rectangle’s height cannot be negative! For some reason Corona allows you to draw this abomination, but the physics engine is understandably confused when you give it an object that cannot realistically exist. Take away the minus and those platforms will work.

As for your flipper, Corona requires polygon physics bodies to have their vertices defined in clockwise order, but this is counterclockwise:

local rfShape = { 60,-20, -60,-10, -60,10, 60,20}

Fix those issues and your issues will be fixed.

I’ll take my upvotes and eternal fame now… well, right… this isn’t reddit. Still, it is better to always post your code from the beginning as the issue may be clear there. You’ll save yourself some days of waiting next time by doing so.

Thanks a big bunch - hope you’re right!

I have only my own, unofficial  upvote to give, but you have it :slight_smile:

Dang it - the flippers still happily let the ball through, every once in a while :frowning:

The only thing that seems to help is if I reduce the speed of the flippers down to 800 (from 1000), but if I do that, the ball will not get enough speed to get to the top of the table.

And if I reduce the gravity to make it easier for the ball to get to the top, the game will feel a bit slow 

-Thanks for all your help(!), but it seems I still can’t get rid of this problem. Maybe Box2D can’t handle the speeds required… 

Have a nice weekend!

/TJ

Can you post the code, again, for how you create the flippers? Their shapes, specifically.

Here it is - I think the polygon of the right flipper is correct (clockwise) now

[lua]

local lfShape = {

-65,-20, 

60,-10, 

60,10, 

-65,20

}

local leftFlipper = display.newPolygon( frameGroup, lx, ly, lfShape )

leftFlipper.x = lx+83

leftFlipper.y = ly+15

local leftPivot = display.newCircle(frameGroup, lx+15, ly+15, 20 )

leftFlipper.active = false

physics.addBody( leftFlipper, “dynamic”, { density=10.0, friction=0.8, bounce=0.2, shape=lfShape } )

physics.addBody( leftPivot, “static”, { density=2, radius=5, friction=2.5, bounce=0.5} )

leftFlipper.isBullet = true

local leftJoint = physics.newJoint( “pivot”, leftPivot, leftFlipper, leftPivot.x, leftPivot.y )

leftJoint.isLimitEnabled = true

leftJoint:setRotationLimits( -20, 15)

leftJoint.isMotorEnabled = true

leftJoint.maxMotorTorque = 100000

leftJoint.motorTorque = 70000

leftFlipper.isBullet = true

local rx = 690

local ry = 532

local rfShape = { 65,20, -60,10, -60,-10,65,-20}

local rightFlipper = display.newPolygon( frameGroup, rx, ry, rfShape )

rightFlipper.x = rx-82

rightFlipper.y = ry+15

local rightPivot = display.newCircle(frameGroup, rx-15, ry+15, 20 )

rightFlipper.active = false

physics.addBody( rightFlipper, “dynamic”, { density=10.0, friction=0.8, bounce=0.1, shape=rfShape } )

physics.addBody( rightPivot, “static”, { density=2, radius=5, friction=2.5, bounce=0.5} )

local rightJoint = physics.newJoint( “pivot”, rightPivot, rightFlipper, rightPivot.x, leftPivot.y )

rightJoint.isLimitEnabled = true

rightJoint:setRotationLimits( -15, 20)

rightJoint.isMotorEnabled = true

rightJoint.maxMotorTorque = 100000

rightJoint.motorTorque = 70000

rightFlipper.isBullet = true

[/lua]

You could try making the physics object of the flipper ‘thicker’ than it appears on screen, making the ball less likely to slip past it in between physics ticks. Unfortunately this is a thing that you might have to do some ugly hacks to protect against, such as checking if the ball reaches a certain ‘impossible’ position in relation to the flipper while travelling between certain angles, and artificially propel the ball if so.

Or perhaps reduce the flipper speed as you have done and multiply the velocity of the ball by some factor once it leaves the flipper.

The twitch streamer Quill18 built a pinball game in Unity for Ludum Dare and had the same issue, even though in Unity you have the ability to increase the number of physics simulations per second and alter various other settings objects related to collisions. He increased it to 100 but it still happened occasionally. 

for “joint power”, often the issue is torque, not speed.  play with the values, see what happens - if you can achieve sufficient power via torque, then perhaps you can then decrease speed to improve stability.

Thanks for replying, guys! 

Without an exact and fair flipper, there is no point making a flipper game, so this is kind of my big problem with this project

“Manually” adjusting the interaction between the ball and the flipper feels like it could be a can of worms. I wonder how they did it in Pinball Dreams, with only a modest Amiga processor :slight_smile:

I’ve fiddled around a good bit with torque/speed, but haven’t achieved a good result yet. 

I’ve also fiddled with position and velocity-iterations, but when I put them really high, sometimes gameplay feels twitchy… it seems to reduce the main problem, though.

I think my next thing to test will be to measure at what speeds the problem occurs, and then maybe limiting the max speed of the ball. I have  a hunch it will fell a bit strange when playing, though.

This being my first physics-based project, I kindof had the idea that Box2D would work a bit differently :slight_smile: - And I’m surprised that the same problem seems to occur in Unity; I’d actually pondered making the game in Unity instead, in order to not have to deal with this problem.

I’ve had a cursory look at the Defold engine (The one used by King - seems a bit similar to Corona), and they seem to have a thng where you can project “rays” along the path of an object, which I guess is in order to avoid things like this… maybe that is something I could try in corona, as well

In case this is of interest to you or anybody else: Pinball Dreams CPC

This is on quite underwhelming hardware by any modern standard, so if you still “wonder how they did it in Pinball Dreams, with only a modest … processor”, you could always ask them.  :) 

It does have a better feel at flipSpeed = 1000 but it’s really the force on the ball that makes 800 seem sluggish, right?  What if you gave the ball a slight linear impulse boost when it hits the flipper?  Of course we might have to figure out a solution for the static body walls post boost but that’s maybe an idea worth playing around with if it allows you to get the feel right.

I also wonder if you might fair better by giving the flippers an angular impulse instead of moving it with motorSpeed. Perhaps it’s my imagination but when I was dealing with a similar problem it seemed that I had a better “hit rate” with impulses.

Nice idea, sporkfin! - worth a shot! :slight_smile: