How do i bounce an object at a constant speed?

I got the project working with just the math2d plugin! 

Here’s the code! 

local math2d = require "plugin.math2d" local physics = require "physics" physics.start() physics.setGravity(0,0) local ballSpeed = 500 local lw = display.newRect( display.contentCenterX - 450, display.contentCenterY, 40, 600 ) physics.addBody( lw, "static", { bounce = 1 }) local rw = display.newRect( display.contentCenterX + 450, display.contentCenterY, 40, 600 ) physics.addBody( rw, "static", { bounce = 1 }) local tw = display.newRect( display.contentCenterX, display.contentCenterY - 300, 900, 40 ) physics.addBody( tw, "static", { bounce = 1 }) local bw = display.newRect( display.contentCenterX, display.contentCenterY + 300, 900, 40 ) physics.addBody( bw, "static", { bounce = 1 }) local enterFrame = function( self ) local vx,vy = self:getLinearVelocity() vx,vy = math2d.normVec( vx, vy ) vx,vy = math2d.scaleVec( vx, vy, ballSpeed ) self:setLinearVelocity( vx, vy ) end local function newBall( x, y, angle ) local ball = display.newCircle( x, y, 25 ) physics.addBody( ball, { radius = 25, bounce = 1 }) ball.isFixedRotation = true local vec = math2d.angle2Vector( angle, true ) vec = math2d.scale( vec, ballSpeed ) ball:setLinearVelocity( vec.x, vec.y ) ball.enterFrame = enterFrame ball:addEventListener( "enterFrame", ball ) end local timer = timer.performWithDelay( 1, function() newBall( display.contentCenterX, display.contentCenterY, math.random(0,359) ) end, 1 )

So now the question stands – Can this be made with just LUA and Corona?

–SonicX278

  1. Congrats.

  2. I’m afraid I’ve been working…but had I been checking regularly I’d have answered.

A. Yes, use math2d.

B. The only important part of my example was this:

local enterFrame = function( self ) local vx,vy = self:getLinearVelocity() vx,vy = normVec( vx, vy ) vx,vy = scaleVec( vx, vy, ballSpeed ) self:setLinearVelocity( vx, vy ) end

and the physics settings: friction = 0, bounce = 1, and isFixedRotation = true (for balls).

You pretty much got all that to, so again, “Good job.”  

I do think you may want to go with 0 friction though.

Well this part 

local enterFrame = function( self ) local vx,vy = self:getLinearVelocity() vx,vy = normVec( vx, vy ) vx,vy = scaleVec( vx, vy, ballSpeed ) self:setLinearVelocity( vx, vy ) end

Isn’t math2d…

But this part 

local vec = math2d.angle2Vector( angle, true ) vec = math2d.scale( vec, ballSpeed ) enemyTop:setLinearVelocity( vec.x, vec.y ) enemyTop.enterFrame = enterFrame enemyTop:addEventListener( "enterFrame", enemyTop )

is…

So how would you make this part pure Corona Lua? Is it possible? Or is it jut to much?

–SonicX278

It is possible, but I’m not doing long hand vector math to answer questions in the forums.

Also, the vector math is Lua code , not Corona code.

If you want to learn long hand vector math you can find many sources on the web, and/or try to dig though the math2d source (AKA RGMath2D.lua):  http://github.com/roaminggamer/SSKCorona/blob/master/ssk/RGMath2D.lua

Read the *Fast functions for a straightforward tour of vector math.

i.e. If you want to learn to do 2D addition, look at the file and find 

function math2do.add( ... )

Then look below it and find:

math2do.addFast( x1, y1, x2, y2 ) 

The prior is the flexible version (takes various arguments depending on need).  The *Fast version only takes vector components as scalar values.

I really think your friend would be better served understanding the principles of what the code is doing while keeping the vector operations separated into functions.  Writing the code out long hand will just make it hard to understand and not useful for future examples. 

If you keep the key operations separated as functions, you and your friend can re-use and not worry about re-implementing each time.

This is the purpose and value of separating common functionality into functions, modules, and libraries of code.

Also, if one doesn’t already understand vector math, a long hand example won’t be very useful.  A good lesson in vector math would.

Note: I learned vector math in school and extended my knowledge via game development and reading.

This book (PDF) has a great appendix on Trigonometry (including vectors) starting on page 911:

http://portal.aauj.edu/portal_resources/downloads/programming/windows_game_programming_guru.pdf

I still refer to this today on occasion.

One more note for clarity sake.  Both of those are math2d and both are SSK.  math2d is from SSK.

The first one is written using localized calls to functions in SSK’s RGMath2D.lua module.  (You need to look at the whole source file that I supplied as a download to see that.)

http://github.com/roaminggamer/RG_FreeStuff/blob/master/AskEd/2015/11/bouncy/main.lua

While, the second one uses the math2d plugin.

The thing is, math2d is the same as RGMath2D.lua, except I modified it slightly to make it a standalone plugin.

This way folks can get 2D math functions w/o needing to get all of SSK too.

You’l have to reset the velocities after collisions.

Basic code is :

local math2d = require "plugin.math2d" local fixedRate = 100 -- 100 pixels per second function obj.postCollision( self )    local vx,vy = self:getLinearVelocity()    vx,vy = math2d.normalize( vx, vy )    vx,vy = math2d.scale( vx, vy, fixedRate )    self:setLinearVelocity( vx, vy ) end obj:listen("postCollision")

Use the math2d library to do the normalizing and scaling work: https://store.coronalabs.com/plugin/math2d

Wow, I just noticed my docs do not show how to use the scale function. 

This is how:

local vx,vy = 1, 1.5 vx,vy = math2d.scale( vx, vy, 2 ) print(vx,vy) -- prints 2 3

This works but it doesn’t bounce normally… when it collides it goes up and down and side to side but it doesn’t move slanted…

Check out the attachment below.

Thanks for the help!

  1. Did you get and set the x,y components of the vector correctly.  Better double check you didn’t swap and x for a y or vice versa.

  2. Consider adding a slight delay.

    local math2d = require “plugin.math2d” local fixedRate = 100 – 100 pixels per second function obj.postCollision( self ) timer.performWithDelay( 100, function() local vx,vy = self:getLinearVelocity() vx,vy = math2d.normalize( vx, vy ) vx,vy = math2d.scale( vx, vy, fixedRate ) self:setLinearVelocity( vx, vy ) end ) end obj:listen(“postCollision”)

Note: You use the word normal, but you’re trying to change the normal behavior of bouncing.  What you described can’t physically happen.  After a series of collisions, momentum will be lost if bounce is not 100% and if there is damping.  Also, objects will ‘exchange’ momentum.   

You are overriding this ‘normal’ behavior of physics, so this is going to take some tweaking to get it to behave they way you want.

Alternately, you could do this without physics, but then you have to know the math for physics like movement, and collision detection between various shaped bodies.

I’m copying and pasting your code and still its only moving up and down and side to side…

–SonicX278

I’ve got a better solution… posting in a minute.

Note: My solution above was a hint to get you moving in the right direction.  Not and end all solution.  You must experiment.

As you will see in a moment, I did and came up with a working solution, both similar and different from my suggestion.

Thanks!!!

–SonicX278

Downloadable example (written w/ SSK but you can convert to regular Corona): http://github.com/roaminggamer/RG_FreeStuff/raw/master/AskEd/2015/11/bouncy.zip

Solution in action:

https://www.youtube.com/watch?v=H2WXQUP6pL4&feature=youtu.be

Example coded with SSK (pay attention to the physics settings I used (second table in makers)):

-- -- Interesting bits start after this... -- local physics = require "physics" physics.start() physics.setGravity(0,0) local ballSpeed = 500 -- 100 pixels per second -- Walls local lw = newRect( nil, left, centerY, { fill = \_B\_, w = 40, h = fullh, anchorX = 0 }, { bodyType = "static", bounce = 1, friction = 0 } ) local rw = newRect( nil, right, centerY, { fill = \_B\_, w = 40, h = fullh, anchorX = 1 }, { bodyType = "static", bounce = 1, friction = 0 } ) local tw = newRect( nil, centerX, top, { fill = \_B\_, w = fullw, h = 40, anchorY = 0 }, { bodyType = "static", bounce = 1, friction = 0 } ) local bw = newRect( nil, centerX, bottom, { fill = \_B\_, w = fullw, h = 40, anchorY = 1 }, { bodyType = "static", bounce = 1, friction = 0 } ) local enterFrame = function( self ) local vx,vy = self:getLinearVelocity() vx,vy = normVec( vx, vy ) vx,vy = scaleVec( vx, vy, ballSpeed ) self:setLinearVelocity( vx, vy ) end local function newBall( x, y, angle ) local ball = newCircle( nil, x, y, { radius = 25, fill = randomColor(), stroke = randomColor(), strokeWidth = 2 }, { bounce = 1, friction = 0, isFixedRotation = true } ) local vec = angle2Vector( angle, true ) vec = scaleVec( vec, ballSpeed ) ball:setLinearVelocity( vec.x, vec.y ) ball.enterFrame = enterFrame listen( "enterFrame", ball ) end for i = 1, 25 do timer.performWithDelay( (i-1) \* 500, function() newBall( centerX, centerY, mRand(0,359) ) end ) end

Thanks so much! Is it hard to covert? I’m trying to keep this as simple as possible so newbies can understand it… And I’m not on my pc right now so I can’t check out the code my self…

–SonicX278

This solution is a bit of overkill.  You can further modify the enterFrame() listener to only change velocity if it below target velocity by a specific factor.  That way it isn’t change velocity every frame like now.

What do you mean trying to keep simple for newbies?  Isn’t this for your personal project?

If you’re asking for help making instructional materials, please mention that next time.  I have a policy of not helping write other’s instructional materials or templates.  That’s just letting me do the heavy lifting.  I’m not very keen on that.

I help here to help individuals, and have that help visible and useful to others (here in the forums) later.  I don’t help here so others can take my code and convert it into new instructional materials posted elsewhere for free or a fee. 

As far as the question, “Is it hard to convert”… you should try before asking, but this is the essence of the conversion you need to do:

This in SSK:

newRect( nil, left, centerY, { fill = \_B\_, w = 40, h = fullh, anchorX = 0 }, { bodyType = "static", bounce = 1, friction = 0 } )

equals this in ‘pure’ Corona:

local centerX = display.contentCenterX local centerY = display.contentCenterY local left = centerX - display.actualContentWidth/2 local right = centerX + display.actualContentWidth/2 local top = centerY - display.actualContentHeight/2 local bottom = centerY - display.actualContentHeight/2 local tmp = display.newRect( left, centerY, 40, display.actualContentHeight ) tmp.anchorX = 0 tmp:setFillColor( 0, 0, 1 ) physics.addBody( tmp, "static", { bounce = 1, friction = 0 } )

SSK newRect

Parameters of SSK newRect( group, x, y [, renderParams [, bodyParams] ] )

  • (required) group to insert object into (defaults to current stage if set to nil as I did above)
  • (required) x, y 
  • (optional) renderParams - Table containing named parameters for render settings.
  • (optional) bodyParams - Table containing physics settings.  If not passed, object will NOT have a body.  Pass a empty table for a ‘default’ body.

Ahh I see … No it’s for me but a friend of mine that’s just starting corona wants this… And I haven’t tried ssk so I can’t really explain what’s happening and how it works… But thanks alot!

–SonicX278

Re: helping a friend.  Ah, that is OK, and good to know.  Thanks for the clarification.

Just be aware.  SSK code is very easy to read as long as you already know how to write the pure equivalent.

Compare my two samples above and you’ll see the ‘common DNA’.

The SSK builders (newRect, newImageRect, etc.) are just parameterized modules that construct objects using pure Corona based the passed parameters.