Constant speed ball after collision

In my game I use physics. I imposed gravity at (0,0)

I have a physical ball that I would always move at a constant speed

the problem is that if the ball collides with a physical body it loses speed. I would like the ball not to lose speed and remain constant even after the rebound

I tried to use runtime listeners to keep the speed constant but I can not. some idea?

Determine speed  you want to have ball move at ’ rate’, then use enterFrame listener as follows:

  1. Get velocity of ball with getLinearVelocity()
  2. Normalize this vector.
  3. Scale this vector by rate
  4. Set ball velocity with results of 3 setLinearVelocity

This is a common question which I’ve answered before.

I have an example (somewhere) on RG_FreeStuff in the AskEd content that does this.

https://github.com/roaminggamer/RG_FreeStuff/

For the life of me I can’t find the example.  Too many and no previews… gotta reorg.

Nonetheless I know that solution works.

I took about an hour to check all the folders. But it was worth it!

I found the example you were saying.

his name is “bouncy”.

Thank you for sharing this solution and your code!

Glad you found it.  That is a terrible name.  I should have named it better.

https://www.youtube.com/watch?v=H2WXQUP6pL4

Here again.

I’m sorry but my math is not very good and it’s days that I can not solve a problem.

I’m using your code and everything is fine. However, a problem has arisen in some contexts.

If a ball bounces on a block it happens as in the picture:

This is fine but on some surfaces (especially curves) I have different needs.

So I would like to do this: if the rebound angle exceeds 160 ° (approx.) Take it to something like 90 °.

This second image should explain better:

Is this possible? Did I explain myself well?

Assuming you know three points (where the ball is, where it hits the block, and where the front of the orange arrow is), you’d proceed like so (untested):

local function DotProduct (vx, vy, wx, vy) return vx \* wx + vy \* wy end local function Length (vx, vy) return math.sqrt(vx \* vx + vy \* vy) end local function Normalize (vx, vy) local vlen = Length(vx, vy) return vx / vlen, vy / vlen, vlen -- add original length as convenience end local function ScaleTo (vx, vy, scale) -- didn't need it, but handy vx, vy = Normalize(vx, vy) return scale \* vx, scale \* vy end local function Correct (bx, by, hx, hy, fx, fy) -- ball, hit, and final positions local bth\_x, bth\_y = bx - hx, by - hy -- hit-to-ball local htf\_x, htf\_y = fx - hx, fy - hy -- hit-to-final local rx, ry = Normalize(bth\_x, bth\_y) -- "left" (we don't care about the length) local ux, uy = ry, -rx -- "up" local htf\_len htf\_x, htf\_y, htf\_len = Normalize(htf\_x, htf\_y) -- get the reflection direction -- but preserve the length for our -- correction local dot = DotProduct(rx, ry, htf\_x, htf\_y) local angle = math.acos(dot) -- n.b. there are improvements on this, e.g. more stable -- versions or checking 'dot' directly if math.deg(angle) \>= 160 then return hx + ux \* htf\_len, hy + uy \* htf\_len -- for corrections using other reflection angles (in radians), do: -- local ca, sa = math.cos(ref\_angle), math.sin(ref\_angle) -- local dir\_x, dir\_y = ca \* rx + sa \* ux, ca \* ry + sa \* uy -- return hx + dir\_x \* htf\_len, hy + dir\_y \* htf\_len else return fx, fy -- original fine end end

The result is the “corrected” where-the-front-of-the-orange-arrow-is. I can elaborate on the steps if necessary.

Hi!

First of all, thank you very much for your help!

I do not think I have understood correctly how to use this method

I know where the ball is and thanks to the collision event I can know the hit but I do not think I can calculate where the ball will go.

What I do at the moment is to create a ball with this listener (taken from the examples of @roaminggamer):

 local enterFrame = function( self ) local vx,vy = self:getLinearVelocity() vx,vy = math2d.normalize( vx, vy ) vx,vy = math2d.scale( vx, vy, ballSpeed ) self:setLinearVelocity( vx, vy ) end --startwith a random angle local angle = math.random(0,359) local vec = math2d.angle2Vector( angle, true ) vec = math2d.scale( vec, ballSpeed ) ball:setLinearVelocity( vec.x, vec.y ) ball.enterFrame = enterFrame Runtime:addEventListener( "enterFrame", ball )

Then let the ball rebound to infinity here and there. However it creates the problem I was saying when the angle is too big.

Precisely if the surface is curved the ball instead of bouncing there slips. And I do not need this.

Hi.

I was basing it on your picture, so didn’t translate it quite to your situation. Also I guess it would need some minor revisions to math2d.

The “hit-to-ball” stuff would basically be your most recent pre-collision linear velocity, but backwards. You could probably get a decent approximation of this by updating it in a timer or enterFrame (gravity and such are likely to desync it slightly) and probably in the collision event for good measure.

“Hit-to-final” would be your new velocity.

So instead probably something like:

local PrevVX, PrevVY -- previous linear velocity (update this somewhere) local Speed = Constant speed function Correct (vx, vy) -- velocity to correct local bth\_x, bth\_y = bx - hx, by - hy -- hit-to-ball local htf\_x, htf\_y = fx - hx, fy - hy -- hit-to-final local lx, ly = math2d.normalize(PrevVX, PrevVY) -- "left" (we don't care about the length) local ux, uy = ly, -lx -- "up" local htf\_len vx, vy = math2d.normalize(vx, vy) local dot = math2d.dot(lx, ly, vx, vy) local angle = math.acos(dot) if math.deg(angle) \>= 160 then return ux \* Speed, uy \* Speed -- for corrections using other reflection angles (in radians), do: -- local ca, sa = math.cos(ref\_angle), math.sin(ref\_angle) -- local dir\_x, dir\_y = ca \* lx + sa \* ux, ca \* ly + sa \* uy -- return dir\_x \* Speed, dir\_y \* Speed else return vx \* Speed, vy \* Speed -- original direction fine end end local vx, vy = Correct(Ball:getLinearVelocity()) PrevVX, PrevVY = vx, vy Ball:setLinearVelocity(vx, vy)

Before answering I did some tests. But I still can not.

I do not understand how to recover the 3 points required by the “Correct” function.

Do I have to create events: “collision”, “preCollision” and “postCollision” in obstacle object?

I have created a small example that can help me show what is happening now:

local physics = require("physics") physics.setDrawMode( "hybrid" ) --debug hybrid normal physics.start() physics.setGravity( 0, 0 ) --"math2do" taken from the examples of @roaminggamer local math2do = {} local mRad = math.rad local mCos = math.cos local mSin = math.sin local mSqrt = math.sqrt function math2do.scale( ... ) -- ( objA, scale [, altRet] ) or ( x1, y1, scale, [, altRet] ) if( type(arg[1]) == "number" ) then local x,y = arg[1] \* arg[3], arg[2] \* arg[3] if(arg[4]) then return { x=x, y=y } else return x,y end else local x,y = arg[1].x \* arg[2], arg[1].y \* arg[2] if(arg[3]) then return x,y else return { x=x, y=y } end end end function math2do.length( ... ) -- ( objA ) or ( x1, y1 ) local len if( type(arg[1]) == "number" ) then len = mSqrt(arg[1] \* arg[1] + arg[2] \* arg[2]) else len = mSqrt(arg[1].x \* arg[1].x + arg[1].y \* arg[1].y) end return len end function math2do.normalize( ... ) -- ( objA [, altRet] ) or ( x1, y1 [, altRet] ) if( type(arg[1]) == "number" ) then local len = math2do.length( arg[1], arg[2], false ) local x,y = arg[1]/len,arg[2]/len if(arg[3]) then return { x=x, y=y } else return x,y end else local len = math2do.length( arg[1], arg[2], true ) local x,y = arg[1].x/len,arg[1].y/len if(arg[2]) then return x,y else return { x=x, y=y } end end end function math2do.angle2Vector( angle, tableRet ) local screenAngle = mRad(-(angle+90)) local x = mCos(screenAngle) local y = mSin(screenAngle) if(tableRet == true) then return { x=-x, y=y } else return -x,y end end --====-- --ball-- --====-- local ballSpeed = 100 local ball = display.newCircle( 160, 50, 15 ) ball:setFillColor( 1, 0, 0 ) physics.addBody( ball, {density=0.1, radius = 15, bounce = 0.7, friction = 1 } ) ball.isBullet = true local enterFrame = function( self ) local vx,vy = self:getLinearVelocity() vx,vy = math2do.normalize( vx, vy ) vx,vy = math2do.scale( vx, vy, ballSpeed ) self:setLinearVelocity( vx, vy ) end local angleStart = 180 local vec = math2do.angle2Vector( angleStart, true ) vec = math2do.scale( vec, ballSpeed ) ball:setLinearVelocity( vec.x, vec.y ) ball.enterFrame = enterFrame Runtime:addEventListener( "enterFrame", ball ) --========-- --obstacle-- --========-- local points = { -7.8459095727845 ,99.691733373313, -15.643446504023 ,98.768834059514, -23.344536385591 ,97.236992039768, -30.901699437495 ,95.105651629515, -38.268343236509 ,92.387953251129, -45.399049973955 ,89.100652418837, -52.249856471595 ,85.264016435409, -58.778525229247 ,80.901699437495, -64.944804833018 ,76.040596560003, -70.710678118655 ,70.710678118655, -76.040596560003 ,64.944804833018, -80.901699437495 ,58.778525229247, -85.264016435409 ,52.249856471595, -89.100652418837 ,45.399049973955, -92.387953251129 ,38.268343236509, -95.105651629515 ,30.901699437495, -97.236992039768 ,23.344536385591, -98.768834059514 ,15.643446504023, -99.691733373313 ,7.8459095727845, -100, 2.4492935982947e-014, } local obstacle = display.newRect( 240, 300, 10, 10 ) physics.addBody( obstacle, "static", { chain = points, } )

I ask you a little more patience, I understand that this can be trivial for you

Well, as mentioned in the last post, the previous example with points was just based on your picture. You should actually reread what I wrote there and try to draw up another picture to get some idea of what it’s doing (and read up on unfamiliar terms).

Given that revised Correct function, maybe you could try adjusting these parts:

-- stuff before enterFrame... local PrevVX, PrevVY local function Correct (vx, vy) -- as in previous post, but with ballSpeed rather than Speed end local enterFrame = function( self ) local vx,vy = self:getLinearVelocity() -- vx,vy = math2do.normalize( vx, vy ) -- vx,vy = math2do.scale( vx, vy, ballSpeed ) vx,vy = Correct(vx, vy) PrevVX, PrevVY = vx, vy self:setLinearVelocity( vx, vy ) end -- stuff after enterFrame, up to initialization of velocity... ball:setLinearVelocity(vec.x, vec.y) PrevVX, PrevVY = vec.x, vec.y -- initial value -- rest of stuff...

The previous direction is important if you want to keep updating the angle.

I tried as you said but the ball behaves similarly to before.

I may have underestimated the problem I do not know.

I’m also trying other solutions crosses but in vain.

The problem is that this is a crucial point of my app and I can not leave it alone

I keep failing. I have understood the necessary mathematics but I can not impose myself.

If it’s a simple thing for you and it does not take a long time, could I make a small working example?

I do not want to ask too much but I’m really stuck :wacko:

Post some code for your most recent attempt, at least to show what you’re doing.

It might not hurt to make this a new, separate topic as well.

Thanks again

The new topic has been opened here: https://forums.coronalabs.com/topic/73634-change-the-rebound-angle/

Determine speed  you want to have ball move at ’ rate’, then use enterFrame listener as follows:

  1. Get velocity of ball with getLinearVelocity()
  2. Normalize this vector.
  3. Scale this vector by rate
  4. Set ball velocity with results of 3 setLinearVelocity

This is a common question which I’ve answered before.

I have an example (somewhere) on RG_FreeStuff in the AskEd content that does this.

https://github.com/roaminggamer/RG_FreeStuff/

For the life of me I can’t find the example.  Too many and no previews… gotta reorg.

Nonetheless I know that solution works.

I took about an hour to check all the folders. But it was worth it!

I found the example you were saying.

his name is “bouncy”.

Thank you for sharing this solution and your code!

Glad you found it.  That is a terrible name.  I should have named it better.

https://www.youtube.com/watch?v=H2WXQUP6pL4

Here again.

I’m sorry but my math is not very good and it’s days that I can not solve a problem.

I’m using your code and everything is fine. However, a problem has arisen in some contexts.

If a ball bounces on a block it happens as in the picture:

This is fine but on some surfaces (especially curves) I have different needs.

So I would like to do this: if the rebound angle exceeds 160 ° (approx.) Take it to something like 90 °.

This second image should explain better:

Is this possible? Did I explain myself well?