Rebounding of any angle

BlitzMax Forums/BlitzMax Programming/Rebounding of any angle

I am sure code exists somewhere for this. Especially for pinball type games.
However, what method would you use to enable a ball to rebound off any angle such as demonstrated here:



I'd create an array of vectors that represent the 'walls'* and then check for collisions with a lines_intersect function (see code archives), i.e. check each wall vector for a collision with the ball trajectory/movement vector.

Once you've found a collision, you can calc the normal of the wall vector (or have them pre-calced) and calculate your reflection vector from there.

Can't give any code as I don't have bmax. :P

* you could knock up a quick editor to make setting these vectors easy.

Thanks. As it happens I have been playing with that code. It's very nice too.
For defining circular areas though the process would be fairly long-winded and prone to error. Still, if line/ball intersection is the way to go then the effort is worth it

Secondly, what about slopes in the ground?
By now you can guess I am thinking of a 2D golf game type thing here
Any ideas how you would tackle slopes like this? ..




Any ideas how you would tackle slopes like this?


Store the information/pixmap based on colour (or another map where slopes are defined = heightmap) on an array, and then read from this one the 'color-height' and move accordingly to the moving vector (you can rise or go down a slope...)

Good idea

I have hit a snag with Line intersect <> ball code from the archives though. When a new line is added for the collision test, the ball will deflect regardless of where the line ends
It's as if an imaginary line goes all the way across the screen
The RED line has been added in this screenshot. The ball bounces as shown and cannot 'pass by' where the line ends:



The line only works one way too. Depending which way the line is draw it is possible for the ball to pass straight through. After which it rebounds back as expected
Any other ways to resolve this? Help appreciated
What I have so far ..

MAIN SOURCE
SuperStrict

Framework BRL.GLMax2D
Import BRL.Blitz

Import "LineBallColl.bmx"

' Create gameMyObject
' CreateMyObject
' point p0 is its starting point in the coordinates x/y
game.Create(640,480,80,0.0)
game.myOb.airf = 0.982
game.myOb.b = 0.5
game.myOb.f = 0.995
game.myOb.r = 10
game.MyOb.lastTime = MilliSecs()
game.myOb.p0 = New point
game.myOb.p0.x = 350
game.myOb.p0.y = 120
' vectors x/y components
game.myOb.vx = 0.0;
game.myOb.vy = 0.0;
Local v:vector2d = New vector2d
game.createWall(600,30,50,50,1,1)
game.createWall(250,450,600,30,1,1)
game.createWall(50,380,250,450,1,1)
game.createWall(50,50,50,380,1,1)



Global starttime% , timeout%
Global mx# , my#

Type edit
	Global mode%,line%,x%,y%
End Type

Graphics game.stageW,game.stageH

' =======================================================
Repeat
	Cls
	If MouseHit(2)
		edit.mode=~edit.mode ; FlushMouse
		If edit.mode=False
			edit.line=0 ; game.myob.vx=0 ; game.myob.vy=0
		EndIf
	EndIf
	If edit.mode
		Editor
	Else
		GameUpdate
	EndIf	
	Flip
Until KeyDown(key_escape)
' =======================================================

Function GameUpdate()
	game.animate(game.myob)
	game.drawall(game.myob)
	If game.myob.moving=0
		mx=MouseX() ; my=MouseY()
		SetColor $77,$77,$77
		DrawLine game.myob.p0.x,game.myob.p0.y , mx,my
		If MouseHit(1)
			Local d#=Dist(mx,my,game.myob.p0.x,game.myob.p0.y)
			Local a#=Ang(mx,my,game.myob.p0.x,game.myob.p0.y)
			game.myob.vx=Sin(a)*(d/3.5)
			game.myob.vy=-Cos(a)*(d/3.5)
		EndIf
	EndIf
	Msg "LMB = Shoot      RMB for editor"
End Function

Function Editor()
	Cls
	Msg "LMB = add line   RMB= return to game"
	SetColor $aa,$44,$16
	For Local v:vector2d = EachIn game.Walllist
		DrawLine v.p0.x,v.p0.y,v.p1.x,v.p1.y
	Next
	If edit.line=1 DrawLine edit.x,edit.y,MouseX(),MouseY()
	If MouseHit(1)
		Select edit.line
			Case 0
			edit.x=MouseX() ; edit.y=MouseY()
			edit.line=1
			Case 1
			game.createWall(edit.x,edit.y,MouseX(),MouseY(),1,1)
			edit.line=0
		End Select
	EndIf
End Function

Function Msg(t$)
	SetColor $ff,$ff,$ff
	DrawText t$,10,5
End Function

Function Dist#(x1#,y1#,x2#,y2#)
	Local dx#=Abs(x1-x2)
	Local dy#=Abs(y1-y2)
	Return Sqr(dx*dx + dy*dy)
End Function

Function Ang#(x1#,y1#,x2#,y2#)
	Local dx#=x1-x2
	Local dy#=y1-y2
	Return 180.0-ATan2(dx,dy)
End Function



####################################


IMPORT code "BallLineColl.bmx"
' Line/Ball collision and deflection - Jesse

SuperStrict

Import BRL.LinkedList

Type point
	Field x# , y#
End Type

Type tgame
	Field stageW% , stageH%
	Field maxV# , gravity#
	Field bounce:vector2d
	Field MyOb:vector2d
	Global walllist:TList
	'
	Method Create(W%,H%,V#,G#)
    	walllist = CreateList()
		stageW = W ; stageH = H
    	maxV = V ; gravity = G
		myOb = New vector2d
	End Method
	'
	Method CreateWall(x1#,y1#,x2#,y2#,b#,f#)
      ' Create vector
      ' point p0 is its starting point in the coordinates x/y
      ' point p1 is its ending point in the coordinates x/y
		Local v:vector2d = New vector2d
		walllist.addlast(v)
		v.p0 = New point
		v.p0.x = x1 ; v.p0.y = y1
		v.p1 =New point
		v.p1.x = x2 ; v.p1.y = y2
		v.b = b ; v.f = f
		v.updateVector()
	End Method
	'
    Method Animate(MyOb:vector2d) 
         Local i%,w:vector2d,vb:vector2d,pen#
         ' add air resistance
         MyOb.vx :* MyOb.airf;
         MyOb.vy :* MyOb.airf;
         ' dont let it go over Max speed
         If (Myob.vx>game.maxV)
            MyOb.vx = game.maxV;
         Else If (MyOb.vx<-game.maxV)
            MyOb.vx = -game.maxV;
         EndIf
         If (MyOb.vy>game.maxV)
            MyOb.vy = game.maxV;
         Else If (MyOb.vy<-game.maxV)
            MyOb.vy = -game.maxV;
         EndIf
         ' update the vector parameters
         myob.updateObject();
         ' check the walls For collisions
         For w = EachIn game.Walllist
            ' If we have hit the wall
            pen# = MyOb.r-myOb.findIntersection(w);
            If pen>=0 
               ' move MyObject away from the wall
               MyOb.p1.x :+ w.lx*pen
               MyOb.p1.y :+ w.ly*pen
               ' change movement
               vb = myob.bounce(w)
               MyOb.vx = vb.vx
               MyOb.vy = vb.vy
            EndIf
         Next
         ' reset MyObject To other side If gone out of stage
         If (MyOb.p1.x>game.stageW+MyOb.r)
            MyOb.p1.x = -Myob.r;
         ElseIf (Myob.p1.x<-Myob.r)
            MyOb.p1.x = game.stageW+Myob.r;
         EndIf
         If (Myob.p1.y>game.stageH+Myob.r)
            MyOb.p1.y = -Myob.r;
         ElseIf (Myob.p1.y<-Myob.r)
            MyOb.p1.y = game.stageH+Myob.r;
         EndIf
         ' draw it
         drawAll(Myob);
         ' make End point equal To starting point For Next cycle
         MyOb.p0 = MyOb.p1;
         ' save the movement without time
         MyOb.vx :/ MyOb.timeFrame
         MyOb.vy :/ MyOb.timeFrame
      End Method
		'
	  Method DrawAll(v1:vector2d)
		SetColor $77,$ff,$66
		For Local v:vector2d = EachIn game.Walllist
			DrawLine v.p0.x,v.p0.y,v.p1.x,v.p1.y
		Next
		SetColor $ff,$ff,$40
		DrawOval v1.p0.x-v1.r,v1.p0.y-v1.r,..
		         v1.r*2,v1.r*2
	  End Method
	'
End Type

Type vector2d
	Field p0:point
	Field p1:point
	Field vx#,vy# , dx#,dy# , rx#,ry# , lx#,ly#
	Field Length# , timeFrame# , lastTime#
	Field r# , airf# , b# , f# , moving%
    '
	Method updateObject()
         Local ThisTime#,time#
		 ' find time passed from last update
         thisTime = MilliSecs();
         time = (thisTime - lastTime) /100
         ' we use time, Not frames To move so multiply movement vector with time passed
         vx :* time ; vy :* time;
		 moving=True
		If (vx>-0.04 And vx<0.04) Then vx=0
		If (vy>-0.04 And vy<0.04) Then vy=0
		If (vx=0 And vy=0) Then moving=False
         ' add gravity, also based on time
         vy = vy+time*game.gravity;
         p1 = New point;
         ' find End point coordinates
         p1.x = p0.x+vx ; p1.y = p0.y+vy;
         ' length of vector
         Length = Sqr(vx*vx+vy*vy);
         ' normalized unti-sized components
         dx = vx/Length ; dy = vy/Length
         ' Right hand normal
         rx = -vy ; ry = vx;
         ' Left hand normal
         lx = vy ; ly = -vx;
         ' save the current time
         lastTime = thisTime;
         ' save time passed
         timeFrame = time;
      End Method
		'
      Method updateVector:vector2d()
         ' x And y components
         ' End point coordinate - start point coordinate
         vx = p1.x-p0.x ; vy = p1.y-p0.y
         ' length of vector
         Length = Sqr(vx*vx+vy*vy);
         ' normalized unti-sized components
         If (Length>0)
            dx = vx/Length ; dy = vy/Length;
         Else
            dx = 0 ; dy = 0;
         EndIf
         ' Right hand normal
         rx = -dy ; ry = dx;
         ' Left hand normal
         lx = dy ; ly = -dx;
         Return Self
      End Method
		'
      ' find New vector bouncing from v2
      Method bounce:vector2d(v2:vector2d)
         ' projection of v1 on v2
         Local proj1:vector2d = projectVector(Self, v2.dx, v2.dy);
         ' projection of v1 on v2 normal
         Local proj2:vector2d = projectVector(Self, v2.lx, v2.ly);
         Local proj:vector2d = New vector2d
         ' reverse projection on v2 normal
         proj2.Length = Sqr(proj2.vx*proj2.vx+proj2.vy*proj2.vy);
         proj2.vx = v2.lx*proj2.Length
         proj2.vy = v2.ly*proj2.Length
         ' add the projections
         proj.vx = f*v2.f*proj1.vx+b*v2.b*proj2.vx
         proj.vy = f*v2.f*proj1.vy+b*v2.b*proj2.vy
         Return proj
      End Method
	'
      ' project vector v1 on unit-sized vector dx/dy
      Method projectVector:vector2d (v1:vector2d, dx#, dy#)
         ' find dot product
         Local dp# = v1.vx*dx+v1.vy*dy;
         Local proj:vector2d = New vector2d
         ' projection components
         proj.vx = dp*dx ; proj.vy = dp*dy;
         Return proj;
      End Method
	'
      ' find intersection point of 2 vectors
      Method findIntersection# (v2:vector2d)
         ' vector between center of ball And starting point of wall
         Local v3:vector2d = New vector2d
         v3.vx = p1.x-v2.p0.x;
         v3.vy = p1.y-v2.p0.y;
         ' project this vector on the normal of the wall
         Local v:vector2d = projectVector(v3, v2.lx, v2.ly)
         ' find length of projection
         v.Length = Sqr(v.vx*v.vx+v.vy*v.vy)
         Return v.Length
      End Method
	'
End Type

Global game:tgame = New tgame


This has been asked so many times before. I've got a snippet in the code archives, and a nice post with pictures here.

Yeah I made a big post about it a while back. It's actually quite hard to get perfect.

There is a Reflected method that returns the reflected vector in my TVec2
object where you:

-Create a incident vector - like your ball
-Create a wall vector (normalized or not - it doesn't matter)
-Then you call:

Reflected_Vector = Incident_Vector.Reflected(Wall_Normal)

The Vector object is here:

http://blitzbasic.com/codearcs/codearcs.php?code=2320#comments