Air Hockey Physics Issues

    I am currently attempting to program a mobile air hockey game, but I’ve been having some difficulty with a seemingly basic part of the physics portion. So far, I have a static-configured striker object, which is moved by a touch/drag method, and a kinetic-configured puck object. Initially, the puck would hardly move at all after the striker hit it, and then would stop registering the collision altogether. That, at least, I was able to handle by opening the physics debugger and determining that the puck was simply sleeping and for whatever reason would not wake up during collisions, which I (possibly unwisely?) fixed by disabling sleeping for the puck.

    Now, however, I’m struggling with a different problem: the striker isn’t delivering any force to the puck. No matter how hard I hit it, or how high I set the bounce settings, the puck is simply pushed along by the striker, maintaining contact with it, and as soon as I stop moving the striker (or move away from the puck), the puck stops moving, and stays where it was last touched.

    I am fairly certain this is a result of the drag method failing to account for/ provide a change in velocity, so while the striker is “moving,” all that is really changing is the position of the striker, and thus any collision between the striker and puck doesn’t actually have any force in it. However, I am unsure how to remedy this, although I’m sure it’s probably something simple I’m just overlooking. Any suggestions/ examples? I can include the relevant code if necessary, but I’d need to go copy it from my other computer.

*Edited for clarity 

Formatting, please! :smiley:

Use paragraphs to make your post easier to read.

Someone may help you out, but if not, I’ll take another look at your post once you’ve formatted it.

Also try to figure out which area of your code might be causing the problem and show us that.  Remember to format your code with the <> button in the control panel.

I made the formatting somewhat better, and I will copy over the code as soon as I can.

re: striker (do you mean paddle?)  You can’t make it static and then move it.  Static objects are just that.  Static.

You’d be better off making the body kinematic.  However, you can’t just change the <x,y> position and hope that will work.

You must move physics objects using physics methods to get the response you hope for.

When you update the <x,y> position of a physics object directly, what you’re really doing is changing the position of the display object. 

The physics body may notice the change later (depending on  various factors), but it will be as if the body simply teleported from one position to another.  This will not give you the result you want.

Instead, you’ll need to combine physics movement with non-physics limits.

Use setLinearVelocity to move the paddle, but if it moves beyond an x- or y- position you deem out of bounds, move it back by forcing the x- and or y- value.  

I have some pong examples here you may find useful as a starting point:

https://github.com/roaminggamer/RG_FreeStuff/tree/master/SSK2/SSK2_Pong

Uses SSK2 (free):

https://github.com/roaminggamer/SSK2

SSK2 docs:

https://roaminggamer.github.io/RGDocs/pages/SSK2/

    Okay, thank you. The first part was pretty much what I was thinking, but didn’t quite know how to put into words. Adding physics movements in makes sense, but how does one determine the direction of the setLinearVelocity change based on touch? I don’t have much familiarity with touch events beyond dragging objects in the previous, flawed manner.

    I’ll look into the pong example, although I found a similar project on GitHub that uses joints to create the paddle motion? Also I found an example from a very old third-party Corona tutorial that simply does it like this: (sorry if the formatting is terrible, I don’t have much experience posting on this forum)

   

< function dragBody(event)

gameUI.dragBody( event, { maxForce=20000, frequency=10, dampingRatio=0.2, center=true } )

end >

Here is the link for that tutorial: https://code.tutsplus.com/tutorials/build-an-air-hockey-game-adding-interaction–pre-45273

And here is the GitHub project: https://github.com/cameronluck/Air-Hockey

I noticed that cameronluck’s project uses the exact same dragBody function, yet requires a separate gameUI.lua file for this, while the tutorial does not seem to, and I’m unsure how that is possible.

re: Touch

You might want to take a timeout from the game and focus entirely on learning how touch event listeners are written.  There are many examples included with Corona (samples).

I will say, there is no single ‘best’ way to do this.  Also, the touch joint idea is actually pretty good.  I think that, combined with an enterFrame listener on the paddle that restricts x and y position ranges would be a real winner.

I tell you what.  I’m at work now, but later I’ll post back a mini-example with a draggable paddle, a puck, and a playboard with four sides.  Be warned I will use SSK2, because I NEVER code long-hand anymore.   Still it should be pretty easily understood.  So check back in a few hours.

re: Links

I think part of this discussion is straying into coding practices (modules, project layout, etc.) which I’d like to avoid for now, except…

The ‘cameronluck’ example is very old and uses an old-style module. 

This line in gameUI.lua is the giveaway ==> 

module(..., package.seeall)

It might contain some useful code, but definitely, do not use it as an example of modules.

As for the ‘tutsplus’ tutorial… even older.   So, neither of those is going to be a great example of project layout and module design.  Still, both might contain some applicable code.

    Thank you! I actually managed to find a simple method buried within some tutorial about water physics that completely solved my problem, at least in terms of movement.

    However, I still don’t quite understand how to restrict movement within that, so thank you, that could probably help a lot. I was planning on using sensor objects for the frame that would set the paddle’s velocity to zero upon collision, but wasn’t sure if that would throw an error or not, or just simply not work.

Downloadable mechanics demo:https://github.com/roaminggamer/RG_FreeStuff/raw/master/AskEd/2020/03/airHockeyPaddlePuckConcept.zip

airHockeyPaddlePuckConcept.jpg

Code that is important (in main.lua; WARNING SSK CODE NOT SAME AS VANILLA CORONA ):

-- ===================================================== -- DEMO CODE BEGINS HERE -- ===================================================== local physics = require "physics" physics.start() physics.setGravity(0,0) physics.setDrawMode("hybrid") local back = newImageRect( nil, centerX, centerY, "protoBackX.png", { w = 720, h = 1386, rotation = fullw\>fullh and 90 } ) -- Sides of Play Area newImageRect( nil, left, centerY, "fillW.png", { w = 40, h = fullh, anchorX = 0, fill = \_R\_ }, { bodyType = "static", bounce = 0.5 } ) -- left newImageRect( nil, right, centerY, "fillW.png", { w = 40, h = fullh, anchorX = 1, fill = \_G\_ }, { bodyType = "static", bounce = 0.5 } ) -- right newImageRect( nil, centerX, top, "fillW.png", { w = fullw-80, h = 40, anchorY = 0, fill = \_B\_ }, { bodyType = "static", bounce = 0.5 } ) -- top newImageRect( nil, centerX, bottom, "fillW.png", { w = fullw-80, h = 40, anchorY = 1, fill = \_PURPLE\_ }, { bodyType = "static", bounce = 0.5 } ) -- bottom -- Rectangle To Show Limits of Paddle Movement local boundsWidth = fullw \* 0.5 -- we'll use this value later for the limit code local boundsHeight = fullh \* 0.5 -- we'll use this value later for the limit code local boundsRect = newRect( nil, centerX, centerY, { w = boundsWidth, h = boundsHeight, fill = \_T\_, stroke = \_Y\_, strokeWidth = 4 } ) -- Caculate x,y edges of bounds local minX = centerX - boundsWidth/2 local maxX = centerX + boundsWidth/2 local minY = centerY - boundsHeight/2 local maxY = centerY + boundsHeight/2 local paddleRadius = 40 -- Make Paddle and add phyics dragger to it. local paddle = newCircle( nil, centerX, centerY, { radius = paddleRadius, fill = \_O\_, alpha = 0.5 }, { bodyType = "dynamic" } ) -- We could use the SSK2 drag helper to add a touch joint: -- ssk.misc.addPhysicsDrag( paddle, { force = 1e6 } ) -- But that isn't quite right so we do this instead: paddle.touch = function( self, event ) local phase = event.phase local id = event.id if( phase == "began" ) then self.isFocus = true self.tempJoint = physics.newJoint( "touch", self, self.x, self.y ) self.tempJoint.maxForce = 1e6 self.tempJoint.dampingRatio = 0 self.tempJoint.frequency = 2000 display.currentStage:setFocus( self, id ) elseif( self.isFocus ) then self.tempJoint:setTarget( event.x, event.y ) if( phase == "ended" or phase == "cancelled" ) then self.isFocus = false display.currentStage:setFocus( self, nil ) display.remove( self.tempJoint ) end end return false; end; paddle:addEventListener("touch") -- Add and enterFrame Listener to help limit movement function paddle.enterFrame( self ) if (self.x \< minX + paddleRadius) then self.x = minX end if (self.x \> maxX - paddleRadius) then self.x = maxX end if (self.y \< minY) then self.y = minY end if (self.y \> maxY) then self.y = maxY end end; listen( "enterFrame", paddle ) -- Make Puck local puck = newCircle( nil, centerX, minY, { radius = paddleRadius/2, fill = \_C\_, alpha = 1 }, { bodyType = "dynamic", density = 0.1 } )

I noticed a small error and just fixed it.

If you got it, please download again.

Note: Body of paddle ended up needing to be dynamic for touch joint to work.

    I just came across another issue today. I used a somewhat similar function to restrict the movement of the paddle (referred to as “striker” in my code), and it does exactly what I need it to, but throws the error message “attempt to compare number with nil stack traceback”  when I attempt to go back to the app’s main menu (requires a scene changed that worked previously).

  I think the issue is that the event listener keeps continues to check while the scene is closing, but I’m not sure. Here is the code in question, which is within the scene:create function :

< local function limitStriker()
     local x = striker.x
     local y = striker.y

     if(x > 230) then
          striker.x = 230
     elseif(x < 90) then
          striker.x = 90
     end

     if(y > 360) then
          striker.y = 360
     elseif(y < 280) then
          striker.y = 280
     end

  end 

  Runtime:addEventListener( “enterFrame”, limitStriker ) >

Your ‘striker’ object probably isn’t valid (yet) when the code executes.

Add this line at the top of the function.

if( striker == nil or striker.getLinearVelocity == nil ) then return end

I finally have everything more or less working! (at least for one side of the playing field, I still need to duplicate everything for the opponent’s side)

However, I’ve been having trouble on reset. Everything works fine initially, but if I go back to the home scene from the game and then attempt to start a new game, for example, my puck reset function stops working. During the reset, I attempt to stop the puck before returning it to the center by using <puck:setLinearVelocity( 0, 0 )> , and this works fine initially, but results in a nill stack traceback error when attempted after restarting.

I’m assuming this is because I was sloppy during scene cleanup and missed something important, but I am unsure how to proceed. Any advice?

If more code/ a better explanation is needed please let me know, and if this is getting too off-topic I will start a new thread if necessary. 

Formatting, please! :smiley:

Use paragraphs to make your post easier to read.

Someone may help you out, but if not, I’ll take another look at your post once you’ve formatted it.

Also try to figure out which area of your code might be causing the problem and show us that.  Remember to format your code with the <> button in the control panel.

I made the formatting somewhat better, and I will copy over the code as soon as I can.

re: striker (do you mean paddle?)  You can’t make it static and then move it.  Static objects are just that.  Static.

You’d be better off making the body kinematic.  However, you can’t just change the <x,y> position and hope that will work.

You must move physics objects using physics methods to get the response you hope for.

When you update the <x,y> position of a physics object directly, what you’re really doing is changing the position of the display object. 

The physics body may notice the change later (depending on  various factors), but it will be as if the body simply teleported from one position to another.  This will not give you the result you want.

Instead, you’ll need to combine physics movement with non-physics limits.

Use setLinearVelocity to move the paddle, but if it moves beyond an x- or y- position you deem out of bounds, move it back by forcing the x- and or y- value.  

I have some pong examples here you may find useful as a starting point:

https://github.com/roaminggamer/RG_FreeStuff/tree/master/SSK2/SSK2_Pong

Uses SSK2 (free):

https://github.com/roaminggamer/SSK2

SSK2 docs:

https://roaminggamer.github.io/RGDocs/pages/SSK2/

    Okay, thank you. The first part was pretty much what I was thinking, but didn’t quite know how to put into words. Adding physics movements in makes sense, but how does one determine the direction of the setLinearVelocity change based on touch? I don’t have much familiarity with touch events beyond dragging objects in the previous, flawed manner.

    I’ll look into the pong example, although I found a similar project on GitHub that uses joints to create the paddle motion? Also I found an example from a very old third-party Corona tutorial that simply does it like this: (sorry if the formatting is terrible, I don’t have much experience posting on this forum)

   

< function dragBody(event)

gameUI.dragBody( event, { maxForce=20000, frequency=10, dampingRatio=0.2, center=true } )

end >