Try this one http://martinapps.com/files/pinball%20physics.zip
Ah thanks Jon, thought the link was just broken - didn’t even in a wild guess think someone would have a space in a web file name.
Pretty old topic but I’ve always liked pinball, and never tried to seriously recreate one d:
So decided to give it a spin, maybe someone might have a use for it. It’s just a very boring prototype, two flippers, one ball and 3 walls, but it fully uses physics, no transitions, timer, or forced sets through enterFrames, so it should be pretty close and without ball passing through issues (;
display.setStatusBar(display.HiddenStatusBar); local physics = require "physics"; physics.start(); local group = display.newGroup(); local leftFlipper = display.newRect(group, 0, 0, 80, 30); leftFlipper.x, leftFlipper.y = 95, display.contentHeight-display.screenOriginY-80; physics.addBody(leftFlipper, "dynamic", {friction = 1, density = 3, bounce = 0}); local leftPivot = display.newCircle(group, 0, 0, 15); leftPivot.x, leftPivot.y = leftFlipper.contentBounds.xMin, leftFlipper.y; physics.addBody(leftPivot, "static", {radius = 7.5, isSensor = true}); local leftJoint = physics.newJoint( "pivot", leftFlipper, leftPivot, leftPivot.x, leftPivot.y ); leftJoint.isLimitEnabled = true; leftJoint:setRotationLimits( -30, 20) local rightFlipper = display.newRect(group, 0, 0, 80, 30); rightFlipper.x, rightFlipper.y = display.contentWidth-display.screenOriginX-95, leftFlipper.y; physics.addBody(rightFlipper, "dynamic", {friction = 1, density = 3, bounce = 0}); local rightPivot = display.newCircle(group, 0, 0, 15); rightPivot.x, rightPivot.y = rightFlipper.contentBounds.xMax, rightFlipper.y; physics.addBody(rightPivot, "static", {radius = 7.5, isSensor = true}); local rightJoint = physics.newJoint( "pivot", rightFlipper, rightPivot, rightPivot.x, rightPivot.y ); rightJoint.isLimitEnabled = true; rightJoint:setRotationLimits( -20, 30) function leftFlipper:enterFrame() if self.active then self:applyTorque(-500); end end Runtime:addEventListener("enterFrame", leftFlipper); function rightFlipper:enterFrame() if self.active then self:applyTorque(500); end end Runtime:addEventListener("enterFrame", rightFlipper); local function flipperTouch(event) if event.phase == "began" then if event.x \< display.contentCenterX then leftFlipper.active = true; leftFlipper:applyTorque(-15000); else rightFlipper.active = true; rightFlipper:applyTorque(15000); end elseif event.phase == "ended" then leftFlipper.active = nil; rightFlipper.active = nil; end end Runtime:addEventListener("touch", flipperTouch); local flipperFieldLeft = display.newRect(group, 0, 0, 30, display.contentHeight-(display.screenOriginY\*2)); flipperFieldLeft.x, flipperFieldLeft.y = 22, leftPivot.contentBounds.yMin-flipperFieldLeft.contentHeight\*.5; flipperFieldLeft.rotation = -5; physics.addBody(flipperFieldLeft, "static", {friction = 0, density = 2, bounce = 0}); local flipperFieldRight = display.newRect(group, 0, 0, 30, display.contentHeight-(display.screenOriginY\*2)); flipperFieldRight.x, flipperFieldRight.y = display.contentWidth-22, rightPivot.contentBounds.yMin-flipperFieldRight.contentHeight\*.5; flipperFieldRight.rotation = 5; physics.addBody(flipperFieldRight, "static", {friction = 0, density = 2, bounce = 0}); local flipperFieldTop = display.newRect(group, 0, 0, display.contentWidth-(display.screenOriginX\*2), 30); flipperFieldTop.x, flipperFieldTop.y = display.contentCenterX, display.screenOriginY; physics.addBody(flipperFieldTop, "static", {friction = 0, density = 2, bounce = 0}); local ball = display.newCircle(group, 0, 0, 14); ball.x, ball.y = 80, 50; ball.isBullet = true; physics.addBody(ball, "dynamic", {friction = 0, density = 2, bounce = 0, radius = 14}); function ball:enterFrame() if self.y \> display.contentHeight-display.screenOriginY then self.x, self.y = 80, 50; self:setLinearVelocity(0, 0); end end Runtime:addEventListener("enterFrame", ball);
Just copy/paste in main.lua
Thanks hachisoft, always good to have multiple examples.
But this does have the passing through issue. When I hit the ball close to the hinge the ball and the paddle overlap - that’s what I was thinking of as passing through. (In simulator anyway, haven’t had a chance to try it on device yet).
Change the color of the ball and its easier to see this:
ball:setFillColor(0.5);
I’ve made a little video (;
https://www.youtube.com/watch?v=g1H4zCkLLV4
I think I got what you mean, but that’s actually normal behaviour of box2D (: With high speed moving object it recognizes the collision when the object has already overlapped, and so it pushes it back, but for a fraction of time it looks like they overlap.
You can definitely improve that though, by modifying setVelocityIterations and setPositionIterations. That way Box2D will be more “aware” of what’s happening, and notice the upcoming collision sooner.
The difference between using a transition or .rotation approach is that in those cases, you’re not leaving everything in the hands of the physics engine, thus changing setVelocityIterations and setPositionIterations helps only to a certain extent, as box2D updates gets partially overridden by hardcoded settings, forcing it to recalculate stuff while the game goes on, thus showing a few seconds of overlapping until it sorts everything.
Makes sense. I didn’t realize Box2D was that wishy-washy. Its not that bad with the same color objects but it looks real unprofessional with real graphic objects (ball and paddle) when they overlap.
Could you also mitigate it a bit by making the collision surface project past the actual paddle object so it “thinks” it hit before it did?
Or would that make slower hits actually look like they never actually hit the paddle at all?
That might be a solution, but yes, it would show like the ball doesn’t hit the paddle on slower hits.
To solve it, you might have 2 bodies on the paddle, and connect with one or the other depending on the collision force, or the speed of the ball.
Another more cleaner (but I’m not 100% sure about performance) way might be to have the ball cast a ray down. If the Y of the hit of the ray to the paddle minus the position of the ball is smaller than the amount of pixel per frame that the ball is traveling at, then change the speed of the ball so that at the next frame it will move of an amount of pixel exactly equal to the Y of the hit of ray to the paddle minus the ball Y position-ball.contentHeight*.5.
Not sure if it’s easy to understand, but basically calculate the upcoming collision yourself based on the velocity of the ball and the distance between the ball and the collision point, and right before the collision, slow down the ball so that it will not go over the paddle (:
Box2D seems to do just that when it comes to collisions against static objects, but not for dynamic objects. A solution might be to use paddles made of static bodies, but then when you rotate them, you’ll still get a bit of overlapping during the rotation (because it overrides box2D calculations and you might end up rotate the paddle that few pixel more than needed).
“then change the speed of the ball so that at the next frame it will move of an amount of pixel exactly equal to the Y”
I know exactly what you mean. That’s the way I’m used to (from the GarageGames engines) didn’t realize box2d didn’t do that.
Hey Bruce, is this code still available? The link is broken.
Ah thanks Jon, thought the link was just broken - didn’t even in a wild guess think someone would have a space in a web file name.
Pretty old topic but I’ve always liked pinball, and never tried to seriously recreate one d:
So decided to give it a spin, maybe someone might have a use for it. It’s just a very boring prototype, two flippers, one ball and 3 walls, but it fully uses physics, no transitions, timer, or forced sets through enterFrames, so it should be pretty close and without ball passing through issues (;
display.setStatusBar(display.HiddenStatusBar); local physics = require "physics"; physics.start(); local group = display.newGroup(); local leftFlipper = display.newRect(group, 0, 0, 80, 30); leftFlipper.x, leftFlipper.y = 95, display.contentHeight-display.screenOriginY-80; physics.addBody(leftFlipper, "dynamic", {friction = 1, density = 3, bounce = 0}); local leftPivot = display.newCircle(group, 0, 0, 15); leftPivot.x, leftPivot.y = leftFlipper.contentBounds.xMin, leftFlipper.y; physics.addBody(leftPivot, "static", {radius = 7.5, isSensor = true}); local leftJoint = physics.newJoint( "pivot", leftFlipper, leftPivot, leftPivot.x, leftPivot.y ); leftJoint.isLimitEnabled = true; leftJoint:setRotationLimits( -30, 20) local rightFlipper = display.newRect(group, 0, 0, 80, 30); rightFlipper.x, rightFlipper.y = display.contentWidth-display.screenOriginX-95, leftFlipper.y; physics.addBody(rightFlipper, "dynamic", {friction = 1, density = 3, bounce = 0}); local rightPivot = display.newCircle(group, 0, 0, 15); rightPivot.x, rightPivot.y = rightFlipper.contentBounds.xMax, rightFlipper.y; physics.addBody(rightPivot, "static", {radius = 7.5, isSensor = true}); local rightJoint = physics.newJoint( "pivot", rightFlipper, rightPivot, rightPivot.x, rightPivot.y ); rightJoint.isLimitEnabled = true; rightJoint:setRotationLimits( -20, 30) function leftFlipper:enterFrame() if self.active then self:applyTorque(-500); end end Runtime:addEventListener("enterFrame", leftFlipper); function rightFlipper:enterFrame() if self.active then self:applyTorque(500); end end Runtime:addEventListener("enterFrame", rightFlipper); local function flipperTouch(event) if event.phase == "began" then if event.x \< display.contentCenterX then leftFlipper.active = true; leftFlipper:applyTorque(-15000); else rightFlipper.active = true; rightFlipper:applyTorque(15000); end elseif event.phase == "ended" then leftFlipper.active = nil; rightFlipper.active = nil; end end Runtime:addEventListener("touch", flipperTouch); local flipperFieldLeft = display.newRect(group, 0, 0, 30, display.contentHeight-(display.screenOriginY\*2)); flipperFieldLeft.x, flipperFieldLeft.y = 22, leftPivot.contentBounds.yMin-flipperFieldLeft.contentHeight\*.5; flipperFieldLeft.rotation = -5; physics.addBody(flipperFieldLeft, "static", {friction = 0, density = 2, bounce = 0}); local flipperFieldRight = display.newRect(group, 0, 0, 30, display.contentHeight-(display.screenOriginY\*2)); flipperFieldRight.x, flipperFieldRight.y = display.contentWidth-22, rightPivot.contentBounds.yMin-flipperFieldRight.contentHeight\*.5; flipperFieldRight.rotation = 5; physics.addBody(flipperFieldRight, "static", {friction = 0, density = 2, bounce = 0}); local flipperFieldTop = display.newRect(group, 0, 0, display.contentWidth-(display.screenOriginX\*2), 30); flipperFieldTop.x, flipperFieldTop.y = display.contentCenterX, display.screenOriginY; physics.addBody(flipperFieldTop, "static", {friction = 0, density = 2, bounce = 0}); local ball = display.newCircle(group, 0, 0, 14); ball.x, ball.y = 80, 50; ball.isBullet = true; physics.addBody(ball, "dynamic", {friction = 0, density = 2, bounce = 0, radius = 14}); function ball:enterFrame() if self.y \> display.contentHeight-display.screenOriginY then self.x, self.y = 80, 50; self:setLinearVelocity(0, 0); end end Runtime:addEventListener("enterFrame", ball);
Just copy/paste in main.lua
Thanks hachisoft, always good to have multiple examples.
But this does have the passing through issue. When I hit the ball close to the hinge the ball and the paddle overlap - that’s what I was thinking of as passing through. (In simulator anyway, haven’t had a chance to try it on device yet).
Change the color of the ball and its easier to see this:
ball:setFillColor(0.5);
I’ve made a little video (;
https://www.youtube.com/watch?v=g1H4zCkLLV4
I think I got what you mean, but that’s actually normal behaviour of box2D (: With high speed moving object it recognizes the collision when the object has already overlapped, and so it pushes it back, but for a fraction of time it looks like they overlap.
You can definitely improve that though, by modifying setVelocityIterations and setPositionIterations. That way Box2D will be more “aware” of what’s happening, and notice the upcoming collision sooner.
The difference between using a transition or .rotation approach is that in those cases, you’re not leaving everything in the hands of the physics engine, thus changing setVelocityIterations and setPositionIterations helps only to a certain extent, as box2D updates gets partially overridden by hardcoded settings, forcing it to recalculate stuff while the game goes on, thus showing a few seconds of overlapping until it sorts everything.
Makes sense. I didn’t realize Box2D was that wishy-washy. Its not that bad with the same color objects but it looks real unprofessional with real graphic objects (ball and paddle) when they overlap.
Could you also mitigate it a bit by making the collision surface project past the actual paddle object so it “thinks” it hit before it did?
Or would that make slower hits actually look like they never actually hit the paddle at all?
That might be a solution, but yes, it would show like the ball doesn’t hit the paddle on slower hits.
To solve it, you might have 2 bodies on the paddle, and connect with one or the other depending on the collision force, or the speed of the ball.
Another more cleaner (but I’m not 100% sure about performance) way might be to have the ball cast a ray down. If the Y of the hit of the ray to the paddle minus the position of the ball is smaller than the amount of pixel per frame that the ball is traveling at, then change the speed of the ball so that at the next frame it will move of an amount of pixel exactly equal to the Y of the hit of ray to the paddle minus the ball Y position-ball.contentHeight*.5.
Not sure if it’s easy to understand, but basically calculate the upcoming collision yourself based on the velocity of the ball and the distance between the ball and the collision point, and right before the collision, slow down the ball so that it will not go over the paddle (:
Box2D seems to do just that when it comes to collisions against static objects, but not for dynamic objects. A solution might be to use paddles made of static bodies, but then when you rotate them, you’ll still get a bit of overlapping during the rotation (because it overrides box2D calculations and you might end up rotate the paddle that few pixel more than needed).
“then change the speed of the ball so that at the next frame it will move of an amount of pixel exactly equal to the Y”
I know exactly what you mean. That’s the way I’m used to (from the GarageGames engines) didn’t realize box2d didn’t do that.