Change the rebound angle

Hi

My need is to decrease the rebound angle of a ball.

In the attached photos I show in orange what happens and in green what I would like:

Thanks to the help of @StarCrunch and @roaminggamer, I made the following code that only works in part and I still can not get to the green conclusion:

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.dot( ... ) -- ( objA, objB ) or ( x1, y1, x2, y2 ) local retVal = 0 if( type(arg[1]) == "number" ) then retVal = arg[1] \* arg[3] + arg[2] \* arg[4] else retVal = arg[1].x \* arg[2].x + arg[1].y \* arg[2].y end return retVal end 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 PrevVX, PrevVY function Correct (vx, vy) -- velocity to correct local lx, ly = math2do.normalize(PrevVX, PrevVY) -- "left" (we don't care about the length) local ux, uy = ly, -lx -- "up" local htf\_len vx, vy = math2do.normalize(vx, vy) local dot = math2do.dot(lx, ly, vx, vy) local angle = math.acos(dot) if math.deg(angle) \>= 160 then return ux \* ballSpeed, uy \* ballSpeed -- 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 \* ballSpeed, dir\_y \* ballSpeed else return vx \* ballSpeed, vy \* ballSpeed -- original direction fine end end local enterFrame = function( self ) local vx,vy = self:getLinearVelocity() vx,vy = Correct(vx, vy) PrevVX, PrevVY = vx, vy 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 ) PrevVX, PrevVY = 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, } )

first part of the question here (https://forums.coronalabs.com/topic/73586-constant-speed-ball-after-collision/#entry387526)

caveat: only gave it a “5 second” read, but i think the angle you’re calculating between vectors would be the 20-degree suppliment, not the 160-degree orange (referencing your first illustration)

Hi. It looks like this change will do it, at least with this example:

function Correct (vx, vy) -- velocity to correct local lx, ly = math2do.normalize(PrevVX, PrevVY) -- "left" (we don't care about the length) local ux, uy = ly, -lx -- "up" vx, vy = math2do.normalize(vx, vy) local dot = math2do.dot(lx, ly, vx, vy) local angle = math.acos(dot) if math.deg(angle) \> 20 then return ux \* ballSpeed, uy \* ballSpeed -- 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 \* ballSpeed, dir\_y \* ballSpeed else return vx \* ballSpeed, vy \* ballSpeed -- original direction fine end end

Following my earlier comments, you would actually have wanted

local lx, ly = math2do.normalize(-PrevVX, -PrevVY)

However, that will give you a 180 degree angle while falling and just get you stuck.  :slight_smile: It’s possible you’ll still run into this situation, say when hitting a wall at a right angle.

Hello, thank you very much!

I have tested the code in the example and it works well!

Then I did a test with other curved surfaces here and there and the problems persist.

At other times the ball moves strangely.

Given the scale of the problem, I think it’s impossible for me to solve and it would be too much to ask you.

I was therefore wondering, as a last hope, something.

Would it be possible (by removing the “Correct” method) to put a listen on the obstacle that detects these cases?

This would be enough for me. If the obstacle detects that the ball is slamming it at an angle near 180 I remove that obstacle.

Is this possible or equally complicated?

fwiw:  this seems like a strange game mechanic - i can’t imagine a real-world case where that type of reaction would be “expected”.  makes me wonder if you’re trying really hard to implement something that, in the end, won’t even be desirable.

having said that, what i’d instead do is “roughen” your obstacle geometry to achieve a similar effect.  imagine at each existing current vertex along the concave curve you “bump out” a tiny little “saw tooth” - then the physics engine will do all the work for you.

it’d be unlikely to give exactly 90 degrees, but might mimic it close enough, with little effort involved - don’t know your requirements, but maybe that would suffice.

Is this an attempt to fix/prevent wall-sliding?

 

@davebollinger

I know it seems unlikely but it was an example. I could decide if this event was confirmed to push the ball towards an area(rather than eliminating the obstacle). The important thing is that they do not slip.

Among other things, I do not ask for a rebound of exactly 90 °. It was just an example.

Let’s say that if the angle would be between about 180 (impossible) and 150, I would like to slam giving rise to a smaller angle (in the example 90 °) but also a little more or less purses do not slip

@roaminggamer

Exact!

 

I wish the ball did not slip into the wall

perhaps if you posted something that demonstrated the actual problem you’re trying to solve?

(ie, rather than asking for help to “fix your fix”, ask for help on the original problem.  maybe?)

I agree with @davebollinger here.

This is another case of the OP posting a solution that they think will fix their problem, but that they are unable to write.

I tell folks this all the time, but please don’t ask for help on solutions.  Ask for help on problems.

The actual problem here might have been stated as:

“I have balls that bounce around a room.  I have added code so they keep moving at the same rate.  This works for the most part, but sometimes:”

  • One or more balls will start sliding along a wall and never stop bouncing back and forth along that wall.

I need help preventing this behavior.  I want balls to keep bouncing around the room without any prolonged walls sliding.

@Jake,

I know this may come across as mean or critical, but I’m not trying to be.  I simply want to make the point that too many people who ask for help fail to ever state what the actual problem is.  More often than not they are asking for help writing a solution that they’ve guessed will help solve the actual problem.

A long time ago I wrote a rather lengthy post on asking questions that might help you and others in the future:  https://forums.coronalabs.com/topic/55780-ask-a-better-question-get-a-better-answer/

PS - Please do make a ‘micro’ (and I really mean tiny and neat ) example showing the problem and I’m sure you’ll get takers on helping with the original problem.  Just zip it up and attach it here.  

PPS - I should also say, the general quality of this and the prior post were good, but the point about not actually addressing the real problem still stands.

i was like:  “how the heck did he read all that out of this thread?!?!” :smiley:

if that is indeed the problem statement:  odds are that the “added code” is directly altering velocity instead of applying a force, and THAT is likely what is causing whatever issue with collision detection you mean by “slip/slide/slam”.

directly setting velocity (linear or angular) should be reserved for kinematic bodies - if at all possible.  because doing so on a dynamic body will “bypass” integration by the physics engine that may be necessary for proper contact detection and collision response.

@All

I have re-read everything.

I think I was wrong to concentrate the question of this tread as if it were the continuation of the previous one. I would have to reformulate the question in a different and more complete way.

@roaminggamer always manages to capture everything but probably for his experience. However, I think people should not spend “hours” just to understand the question so I apologize for this.

The text formulated by roaminggamer was therefore fine to add only 2 things:

  • I’m working with gravity set to 0;
  • the annoyance is not only given by the situations in which it slides up and down but also by the fact that it slips.

I attach as required a simple example in which the ball after having rebounded on an obstacle slips on another curved obstacle.

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( 250, 350, 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 = 299 local vec = math2do.angle2Vector( angleStart, true ) vec = math2do.scale( vec, ballSpeed ) ball:setLinearVelocity( vec.x, vec.y ) ball.enterFrame = enterFrame Runtime:addEventListener( "enterFrame", ball ) --=========-- --obstacles-- --=========-- 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( 185, 292, 10, 10 ) physics.addBody( obstacle, "static", { chain = points, } ) local obstacle2 = display.newCircle( 100, 250, 10 ) physics.addBody( obstacle2, "static", {radius=10})

I hate to say it,but I ran that example and that is exactly what I would expect to happen.  If it did something else I’d think it were broken.

Also, this seems really artificial.  Is this shape interaction really part of your game or something you made to ‘mimic’ the behavior you’re seeing.

I guess what i’m saying is this doesn’t actually feel like the problem still.  

If we saw a concrete example that was actually like your game usage this would make more sense.  Can you describe your game mechanic for us?

Nobody wants to steal it so being protective is pointless.  Also people who steal prefer to steal the finished product.  i.e. They won’t steal a mechanic.  Oh, and there are no new mechanics so…  tell us what you really want to do with the ball(s) in your game so we can finally focus on the problem.

The problem is exactly that.

The game is this: a shuttle moves and must avoid the obstacles. Obstacles must bounce on the walls. I thought of three different modes, one with a background: square, spherical and triangular. I want the obstacles to bounce back to infinity and not that it slides to infinity.

You asked me for a simple example and that’s what I did. I think the problem is this and that further code does not help.

However if you are asking for it I have no problems. I repeat I’m not reinventing the wheel so if you believe it can be useful here is:

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 --=========-- --border-- --=========-- local points = { 185.06664671286 ,51.577059737104, 209.73797743297 ,56.283367774274, 233.62491053694 ,64.04470282235, 256.35073482034 ,74.738663991227, 277.55705045849 ,88.196601125011, 296.90942118574 ,104.20627451572, 314.10264855516 ,122.51520205026, 328.8655851004 ,142.8346410042, 340.9654104932 ,164.84414168699, 350.21130325903 ,188.19660112501, 356.45745014574 ,212.52373708286, 359.60534568565 ,237.44189609414, 359.60534568565 ,262.55810390586, 356.45745014574 ,287.47626291714, 350.21130325903 ,311.80339887499, 340.9654104932 ,335.15585831301, 328.8655851004 ,357.1653589958, 314.10264855516 ,377.48479794974, 296.90942118574 ,395.79372548428, 277.55705045849 ,411.80339887499, 256.35073482034 ,425.26133600877, 233.62491053694 ,435.95529717765, 209.73797743297 ,443.71663222573, 185.06664671286 ,448.4229402629, 160, 450, 134.93335328714 ,448.4229402629, 110.26202256703 ,443.71663222573, 86.375089463064 ,435.95529717765, 63.649265179657 ,425.26133600877, 42.442949541505 ,411.80339887499, 23.090578814262 ,395.79372548428, 5.8973514448423 ,377.48479794974, -8.8655851004031 ,357.1653589958, -20.965410493204 ,335.15585831301, -30.211303259031 ,311.80339887499, -36.457450145738 ,287.47626291714, -39.605345685654 ,262.55810390586, -39.605345685654 ,237.44189609414, -36.457450145738 ,212.52373708285, -30.211303259031 ,188.19660112501, -20.965410493204 ,164.84414168699, -8.865585100403 ,142.8346410042, 5.8973514448422 ,122.51520205026, 23.090578814262 ,104.20627451572, 42.442949541505 ,88.196601125011, 63.649265179657 ,74.738663991227, 86.375089463064 ,64.04470282235, 110.26202256703 ,56.283367774274, 134.93335328714 ,51.577059737104, 160, 50, } local obstacle = display.newRect( 0, 0, 10, 10) physics.addBody( obstacle, "static", { chain = points, connectFirstAndLastChainVertex = true } ) --[[local Effetti = require("libs.Effetti") Effetti.setPathCircle( obstacle, { radius = 200, startA = 0, endA = 360, numPath = 50, })]]-- --=======================-- --ball(obstacle for ship)-- --=======================-- local ballSpeed = 200 local ball = display.newCircle( 250, 350, 15 ) physics.addBody( ball, "dynamic", {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 ) --normalize ==\> normalizeVec vx,vy = math2do.scale( vx, vy, ballSpeed ) --scale ==\> scaleVec 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 )

(start the code and wait about 10 seconds)

have you tried simply setting bounce=1.0 and eliminating all of your after-the-fact-fixup?

seems weird that you’re trying so hard to preserve velocity, yet intentionally drop nearly a third of it with each collision, just to have to fix it up manually afterward.

aside:  setting friction on the ball will have no effect if you don’t also set friction on the obstacle.  HOWEVER, you probably don’t want ANY friction as it will convert some portion of linear velocity to angular velocity.

I did some tests and you’re right. Remove friction and increase rebound helps.Cases in which the ball slips are less frequent.

However, sometimes they appear (I did some tests modifying the departure at random and waiting a few seconds).

There is no way to recognize the phenomenon just before it is present and push the ball (forcibly) to the center?

it might help to post a (yet another, but specific) sample that demonstrates this “slip”, as i for one am not sure what you mean by it.

i’m not trying to just get caught up in word definitions, but if we don’t understand, we can’t help…

“slip” per mechanical engineering would commonly have to do with the incomplete transfer of energy from one body to another…

using car analogies, in gaming (like Hill Climb Racing) or real world, it’d be like when you slam on the gas - the torque of the tires will overcome the friction with the ground, causing the tires to “slip” relative to the ground, as they rotate faster than they can impel the vehicle (aka a “burn out”).  similarly, slamming on the brakes at high velocity can cause a “slip” of the tires relative to the ground (aka a “skid”), as the friction of the tires with the ground is overcome by the forward momentum of the vehicle.

are either of those two types of effect occurring?  you appear to not be using friction, so no rotation of the ball, so it isn’t clear what “slipping” would mean.

thought:  are you talking about the inelastic collisions that box2d resorts to on very glancing collisions, such that a colliding object FAILS to bounce during collision – is THAT your “slip”?

if so, there are things you can tweak A BIT (fe a fixed time step, reduction of physics scale) but can’t fully fix without changing/recompiling box2d’s source code velocity threshold.

First of all, thank you again for your time. It’s really important for me to solve this problem.

I think my case is the second one. The problem is that the ball “flows” along the circumference:

This usually happens after a series of rebounds that he portrayed with the simple drawing (simplified steps):

At this point I think the best idea is to intervene in step 3 or 4 and avoid getting to 5.

I do not have enough mathematical knowledge to do that but I think we can calculate the angle of the ball with respect to the circle every time the ball slams on it. Therefore, if the angle is too wide, force the ball to a narrower angle.

If there are other solutions I’m happy. I’m interested in any solution that always works. I hope I explained myself well this time. Not being a native speaker does not help me …

try the following as experiments:

physics.setTimeStep(1/display.fps) physics.setScale(10)

one or both of those might help, though may cause other “side effects” that you then need to deal with

in theory the following would solve your problem:

physics.setMKS("velocityThreshold", 0)

though it has never actually worked (that i’m aware of)

refs: docs or google it