Constant speed ball after collision

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/