Author Topic: 2d Ball physics+source[bmax]  (Read 417 times)

0 Members and 1 Guest are viewing this topic.

Offline JumpMan

  • C= 64
  • **
  • Posts: 45
  • Karma: 11
    • View Profile
2d Ball physics+source[bmax]
« on: January 18, 2008 »


this past couple of days I had some free time and I decided to take my time and try to look at vectors. I googled and got a load of sites to search. I ran in to this site:
http://tonypa.pri.ee/vectors/start.html
it is a vector tutorial website for flash. I noticed that most of the language is similar to blitzmax. So I started looking deep in to it and discovered it is a good site for vectors. I have learned a bit from it so far. I went through all of the tutorials and managed to convert most of them to blitzmax. and also managed to make a demo. And Here it is:
demo:
Code: [Select]
SuperStrict
Framework BRL.GLMax2D
Import BRL.Random
Import BRL.DXGraphics
SetGraphicsDriver GLMax2DDriver()
Include "lib.bmx"
Type point
Field x#
Field y#
End Type
Type tgame
Field stageW%
Field stageH%
Field maxV#
Field gravity#
Field bounce:vector2d
Field t#
Field LastTime#
Global walllist:TList
Global ballList:TList
Global objlist:TList
Method Create(W%,H%,V#,G#)
    walllist = CreateList()
balllist = CreateList()
objlist = CreateList()
stageW = W
    stageH = H
    maxV = V
    gravity = G
'myOb = New vector2d
End Method
Method createObjects(n%)
For Local i% = 1 To n
Local bn:vector2d = New vector2d
bn.p0 = New point
bn.p1 = New point
objlist.addlast(bn)
bn.r=5+Rand(4)*5
bn.m=1
'attach mc
Local x#=i*65;
Local y#=80;
bn.p0.x=x
bn.p0.y=y
x:+i*20
y:+Rand(6)-1
bn.p1.x=x
bn.p1.y=y
bn.updateVector(True);
bn.airf = 1
bn.b = 1
bn.f = 1
bn.r = 20
bn.lastTime = MilliSecs()
Next
End Method
Method createWall(x1#,y1#,x2#,y2#,b#,f#)
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(True)
End Method
Method CreateBall(x1#,y1#,R#)
Local B:vector2d = New vector2d
balllist.addlast(B)
b.p0 = New point
b.p0.x = x1
b.p0.y = y1
b.r = R
b.updatevector(True)
End Method

Method speedlimit(ob:vector2d)
         If (ob.vx>game.maxV)
            ob.vx = game.maxV;
         Else If (ob.vx<-game.maxV)
            ob.vx = -game.maxV;
         EndIf
         If (ob.vy>game.maxV)
            ob.vy = game.maxV
         ElseIf (ob.vy<-game.maxV)
            ob.vy = -game.maxV;
         EndIf
End Method
Method collitionball2wall(ob:vector2d)
         For Local w:vector2d = EachIn game.BallList
            Local v:vector2d = ob.ball2ball(w);
            Local pen# = ob.r+w.r-v.Length;
            ' If we have hit the ball
            If pen>=0
               ' move Object away from the ball
               ob.p1.x :+ v.dx*pen;
               ob.p1.y :+ v.dy*pen;
               ' change movement, bounce off from the normal of v
               Local vbounce:vector2d = New vector2d
   vbounce.dx = v.dy
   vbounce.dy =-v.dx
   vbounce.lx = v.dx
   vbounce.ly = v.dy
               Local vb:vector2d = ob.bounced(vbounce)
               ob.vx = vb.vx
               ob.vy = vb.vy
            EndIf
         Next
End Method
Method collitionball2ball(ob:vector2d)
End Method
Method collitionwall(ob:vector2d)
         For Local w:vector2d = EachIn game.walllist
            Local v:vector2d = Ob.findIntersection(w);
 
v = v.updateVector(False);
           
Local pen# = Ob.r-v.Length;
            ' If we have hit the wall
            If (pen>=0)
               ' move Object away from the wall
               Ob.p1.x :+ v.dx*pen;
               Ob.p1.y :+ v.dy*pen;
               ' change movement, bounce off from the normal of v
               Local vbounce:vector2d = New vector2d
   vbounce.dx = v.lx
   vbounce.dy = v.ly
   vbounce.lx = v.dx
   vbounce.ly = v.dy
   vbounce.b = 1
   vbounce.f = 1
               Local vb:vector2d = Ob.bounced(vbounce);
               Ob.vx = vb.vx;
               Ob.vy = vb.vy;
            EndIf
         Next
End Method
'wrap balls to opoisite side of screen
Method wrap(ob:vector2d)
         If (ob.p1.x>game.stageW+ob.r)
            ob.p1.x = -ob.r
          Else If (ob.p1.x<-ob.r)
            ob.p1.x = game.stageW+ob.r
         EndIf
         If (ob.p1.y>game.stageH+ob.r)
            ob.p1.y = -ob.r;
         Else If (ob.p1.y<-ob.r)
            ob.p1.y = game.stageH+ob.r
         EndIf
End Method
Method ball2wall (ob:vector2d)
    ' start To calculate movement
        ' dont let it go over Max speed
        ' update the vector parameters
        ob.vx:*ob.airf
ob.vy:*ob.airf
speedlimit(ob)
ob.updateObject();
' check the balls For collisions
        collitionball2wall(ob)
collitionwall(ob)
' reset Object To other side If gone out of stage
wrap(ob)
' make End point equal To starting point For Next cycle
ob.p0 = ob.p1
' save the movement without time
ob.vx :/ ob.timeFrame
ob.vy :/ ob.timeFrame
End Method
Method drawall()
SetColor 255,0,0
For Local v:vector2d = EachIn game.BallList
Drawcircle v.p0.x,v.p0.y,v.r
Next

SetColor 0,255,0
For Local v:vector2d = EachIn game.walllist
DrawLineaa v.p0.x,v.p0.y,v.p1.x,v.p1.y
Next
EndMethod      ' main Function
Method ball2ball()
Local vc:vector2d = New vector2d
Local vn:vector2d = New vector2d
Local v3:vector2d = New vector2d
Local v4:vector2d = New vector2d
Local p2:point = New point
Local p3:point = New point
Local totalradius#
Local remaining:TList = objlist.copy()
For Local ob:vector2d = EachIn objlist
Local newv:vector2d = New vector2d
remaining.removefirst()
For Local ob2:vector2d = EachIn remaining
'vector between center points of ball
vc.p0 = ob.p0
vc.p1 = ob2.p0
vc.updateVector(True)
'sum of radius
totalRadius=ob.r+ob2.r
Local pen#=totalRadius-vc.length
'check If balls collide at start
If(pen>=0)
'move Object away from the ball
ob.p1.x:-vc.dx*pen
ob.p1.y:-vc.dy*pen
'change movement, bounce off from the normal of v
newv=ob.bounceBalls(ob2, vc)
ob.vx=newv.vx1
ob.vy=newv.vy1
ob2.vx=newv.vx2
ob2.vy=newv.vy2
Else
'reduce movement vector from ball2 from movement vector of ball1
v3.p0 = ob.p0
v3.vx=ob.vx-ob2.vx
v3.vy=ob.vy-ob2.vy
v3.updateVector()
'use v3 as New movement vector For collision calculation
'projection of vc on v3
Local vp:vector2d=vc.projectVector(v3.dx, v3.dy)
'vector To center of ball2 in direction of movement vectors normal

p2.x=ob.p0.x+vp.vx
p2.y=ob.p0.y+vp.vy
vn.p0 = p2
vn.p1 = ob2.p0
vn.updateVector(True)
'check If vn is shorter Then combined radiuses
Local diff#=totalRadius-vn.length;
Local collision%=False
If(diff>0)
'collision
'amount To move back moving ball
Local moveBack#=Sqr(totalRadius*totalRadius-vn.length*vn.length);
p3.x=vn.p0.x-moveBack*v3.dx
p3.y=vn.p0.y-moveBack*v3.dy
'vector from ball1 starting point To its coordinates when collision happens
v4.p0 = ob.p0
v4.p1 = ob.p1
v4.updateVector(True)
'check If p3 is on the movement vector
If(v4.length<=v3.length And v4.dotP(ob)>0)
'collision
Local t#=v4.length/v3.length
collision=True
ob.p1.x=ob.p0.x+t*ob.vx
ob.p1.y=ob.p0.y+t*ob.vy
ob2.p1.x=ob2.p0.x+t*ob2.vx
ob2.p1.y=ob2.p0.y+t*ob2.vy
'vector between centers of ball in the moment of collision
vc.p0 = ob.p1
vc.p1 = ob2.p1
vc.updateVector(True)
newv:vector2d=ob.bounceBalls(ob2, vc)
ob.vx=newv.vx1
ob.vy=newv.vy1
ob2.vx=newv.vx2
ob2.vy=newv.vy2
ob2.makeVector()
ob.makeVector()
EndIf
EndIf
EndIf
Next
Next
End Method
End Type
Type vector2d
Field p0:point
Field p1:point
Field vx#,vy#
Field dx#,dy#
Field rx#,ry#
Field lx#,ly#
Field r#,m#
Field vx1#,vy1#
Field vx2#,vy2#
Field Length#
Field timeFrame#
Field lastTime#
Global airf#
Global b#
Global f#
      Method updateObject ()
         ' find time passed from last update
         Local thisTime# = MilliSecs()
         Local time# = (thisTime - lastTime)/60
         ' we use time, Not frames To move so multiply movement vector with time passed
         vx :* time
         vy :* time
         ' 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;
      EndMethod
    Method updateVector:vector2d (frompoints:Int = False)
         ' x And y components
         If frompoints
            vx = p1.x-p0.x;
            vy = p1.y-p0.y;
         Else
p0 = New point
p1 = New point
            p1.x = p0.x+vx;
            p1.y = p0.y+vy;
         EndIf
         makeVector()
holdvector()
Return Self
      End Method
Method makeVector()
'lengthgth 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;
End Method
'Function To hold balls inside stage
Method holdVector()
'reset Object To other side If gone out of stage
If(p1.x>game.stageW-r)
p1.x=game.stageW-r;
vx=-Abs(vx);
ElseIf(p1.x<r)
p1.x=r;
vx=Abs(vx);
EndIf
If(p1.y>game.stageH-r)
p1.y=game.stageH-r;
vy=-Abs(vy);
ElseIf(p1.y<r)
p1.y=r;
vy=Abs(vy);
EndIf
End Method
'calculate dot product of 2 vectors
Method dotP#(v2:vector2d)
Local dp# = vx*v2.vx + vy*v2.vy;
Return dp
End Method
    Method findIntersection:vector2d (v2:vector2d)
    ' vector between center of ball And starting point of wall
    Local v3:vector2d = New vector2d
    Local v:vector2d
v3.vx = p1.x-v2.p0.x;
    v3.vy = p1.y-v2.p0.y;
    ' check If we have hit starting point
    Local dp# = v3.dotP(v2)
    If (dp<0)
        ' hits starting point
            v = v3;
Else
Local v4:vector2d = New vector2d
v4.vx = p1.x-v2.p1.x;
v4.vy = p1.y-v2.p1.y;
' check If we have hit side Or endpoint
dp = v4.vx*v2.dx+v4.vy*v2.dy;
If (dp>0)
' hits ending point
v = v4;
Else
' it hits the wall
' project this vector on the normal of the wall
v = v3.projectVector(v2.lx, v2.ly);
EndIf
EndIf
Return v;
EndMethod
' find collision of 2 balls
Method ball2ball:vector2d (b2:vector2d)
' vector between centers of balls
Local v3:vector2d = New vector2d
v3.vx = p1.x-b2.p0.x;
v3.vy = p1.y-b2.p0.y;
        v3.Length = Sqr(v3.vx*v3.vx+v3.vy*v3.vy);
v3.dx = v3.vx/v3.Length;
v3.dy = v3.vy/v3.Length;
Return v3;
EndMethod
' find New vector bouncing from v2
Method bounced:vector2d (v2:vector2d)
         ' projection of v1 on v2
         Local proj1:vector2d = projectVector(v2.dx, v2.dy);
         ' projection of v1 on v2 normal
         Local proj2:vector2d = projectVector(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 = proj1.vx+proj2.vx;
         proj.vy = proj1.vy+proj2.vy;
         Return proj;
EndMethod
Method bounceBalls:vector2d(v2:vector2d, v:vector2d)
Local proj11:vector2d=projectVector(v.dx, v.dy) 'projection of v1 on v
Local proj12:vector2d=projectVector(v.lx, v.ly) 'projection of v1 on v normal
Local proj21:vector2d=v2.projectVector(v.dx, v.dy) 'projection of v2 on v
Local proj22:vector2d=v2.projectVector(v.lx, v.ly) 'projection of v2 on v normal

Local P#= m * proj11.vx + v2.m * proj21.vx;
Local Vn#=proj11.vx-proj21.vx;
Local v2fx#=(P+Vn*m)/(m+v2.m);
Local v1fx#=v2fx-Vn;

P#=m*proj11.vy+v2.m*proj21.vy;
    Vn#=proj11.vy-proj21.vy;
Local v2fy#=(P+Vn*m)/(m+v2.m);
Local v1fy#=v2fy-Vn;

Local proj:vector2d  = New vector2d
'add the projections For v1
proj.vx1=proj12.vx+v1fx;
proj.vy1=proj12.vy+v1fy;
'add the projections For v2
proj.vx2=proj22.vx+v2fx;
proj.vy2=proj22.vy+v2fy;
Return proj
End Method
      ' project vector v1 on unit-sized vector dx/dy
      Method projectVector:vector2d (dx#, dy#)
         ' find dot product
         Local dp# = vx*dx+vy*dy;
         Local proj:vector2d = New vector2d
         ' projection components
         proj.vx = dp*dx;
         proj.vy = dp*dy;
         Return proj;
      EndMethod
End Type

Global game:tgame = New tgame
game.Create(640,640,40,0.4)
game.Createobjects(4)
game.createBall(100,100,30)
game.createBall(400,100,40)
game.createBall(250,250,20)
game.createBall(100,300,15)
game.createball(500,350,25)
game.createwall(0,0,game.stageW-1,0,1,1)
game.createwall(0,game.stageH-1,game.stageW-1,game.stageH-1,1,1)
game.createwall(0,0,0,game.stageH-1,1,1)
game.createwall(game.stageW-1,0,game.stageW-1,game.stageH-1,1,1)

game.createWall(250,110,50,250,1,1)
game.createwall(250,150,250,110,1,1)
game.createWall(50,150,250,150,1,1)
game.createWall(50,250,50,150, 1,1)
game.createWall(250,300,400,280,1,1)
game.createWall(250,300,250,350,1,1)
game.createWall(400,280,400,330,1,1)
game.createWall(250,350,400,330,1,1)
game.createWall(250,620,500,600,1,1)
game.createWall(200,620,200,500,1,1)

Graphics game.stageW,game.stageH
Global mycaps:TG_D3DDEVICEDESC7
If _max2dDriver.ToString() = "DirectX7"
mycaps = New TG_D3DDEVICEDESC7
D3D7GraphicsDriver().Direct3DDevice7().getcaps mycaps
EndIf
Repeat

Cls
game.ball2ball()
game.drawall()
For Local g:vector2d = EachIn game.objlist
game.ball2wall(g)
SetColor 0,0,255
Drawcircle g.p0.x, g.p0.y,g.r
Next
Flip()
Until KeyDown(key_escape)


lib.bmx:
Code: [Select]
Function DRAWLINEAA(from_x:Float,from_y:Float,to_x:Float,to_y:Float)
If _max2dDriver.ToString() = "DirectX7"
If mycaps.dwRasterCaps_LINE & 4096
Local D3DRS_EDGEANTIALIAS:Int=40
D3D7GraphicsDriver().Direct3DDevice7().setRenderState D3DRS_EDGEANTIALIAS,True
DrawLine from_x,from_y,to_x,to_y
D3D7GraphicsDriver().Direct3DDevice7().setRenderState D3DRS_EDGEANTIALIAS,False
Else
    DebugLog "AA not supported"
          DrawLine from_x,from_y,to_x,to_y
EndIf
Else
Local saveblend:Int=GetBlend()
SetBlend alphablend
    glEnable(GL_LINE_SMOOTH)
DrawLine from_x,from_y,to_x,to_y
SetBlend saveblend
    glDisable(GL_LINE_SMOOTH)
EndIf
End Function

Type TG_D3DDeviceDesc7
Field dwDevCaps:Int
Field dwSize_LINE:Int               'Size of structure
    Field dwMiscCaps_LINE:Int           'Miscellaneous capabilities
    Field dwRasterCaps_LINE:Int         'Raster capabilities
    Field dwZCmpCaps_LINE:Int           'Z-comparison capabilities
    Field dwSrcBlendCaps_LINE:Int       'Source-blending capabilities
    Field dwDestBlendCaps_LINE:Int      'Destination-blending capa bilities
    Field dwAlphaCmpCaps_LINE:Int       'Alpha-test-comparison capabilities
    Field dwShadeCaps_LINE:Int          'Shading capabilities
    Field dwTextureCaps_LINE:Int        'Texture capabilities
    Field dwTextureFilterCaps_LINE:Int  'Texture-filtering capabilities
    Field dwTextureBlendCaps_LINE:Int   'Texture-blending capabilities
    Field dwTextureAddressCaps_LINE:Int 'Texture-addressing capabilities
    Field dwStippleWidth_LINE:Int       'Stipple width
    Field dwStippleHeight_LINE:Int      'Stipple height
    Field dwSize_TRI:Int               'Size of structure
    Field dwMiscCaps_TRI:Int           'Miscellaneous capabilities
    Field dwRasterCaps_TRI:Int         'Raster capabilities
    Field dwZCmpCaps_TRI:Int           'Z-comparison capabilities
    Field dwSrcBlendCaps_TRI:Int       'Source-blending capabilities
    Field dwDestBlendCaps_TRI:Int      'Destination-blending capa bilities
    Field dwAlphaCmpCaps_TRI:Int       'Alpha-test-comparison capabilities
    Field dwShadeCaps_TRI:Int          'Shading capabilities
    Field dwTextureCaps_TRI:Int        'Texture capabilities
    Field dwTextureFilterCaps_TRI:Int  'Texture-filtering capabilities
    Field dwTextureBlendCaps_TRI:Int   'Texture-blending capabilities
    Field dwTextureAddressCaps_TRI:Int 'Texture-addressing capabilities
    Field dwStippleWidth_TRI:Int       'Stipple width
    Field dwStippleHeight_TRI:Int      'Stipple height
Field      dwDeviceRenderBitDepth:Int
Field      dwDeviceZBufferBitDepth:Int
Field      dwMinTextureWidth:Int
Field     dwMinTextureHeight:Int
Field      dwMaxTextureWidth:Int
Field    dwMaxTextureHeight:Int
Field      dwMaxTextureRepeat:Int
Field      dwMaxTextureAspectRatio:Int
Field      dwMaxAnisotropy:Float
Field      dvGuardBandLeft:Float
Field      dvGuardBandTop:Float
Field      dvGuardBandRight:Float
Field      dvGuardBandBottom:Float
Field      dvExtentsAdjust:Float
Field      dwStencilCaps:Int
Field      dwFVFCaps:Int
Field      dwTextureOpCaps:Int
Field      wMaxTextureBlendStages:Short
Field      wMaxSimultaneousTextures:Short
Field      dwMaxActiveLights:Int
Field      dvMaxVertexW:Float
Field      GUID_Interface_type:Int
Field      GUID_1:Short
Field      GUID_2:Short
Field      GUID_3:Byte
Field      GUID_4:Byte
Field      GUID_5:Byte
Field      GUID_6:Byte
Field      GUID_7:Byte
Field      GUID_8:Byte
Field      GUID_9:Byte
Field      GUID_10:Byte
Field      wMaxUserClipPlanes:Short
Field      wMaxVertexBlendMatrices:Short
Field      dwVertexProcessingCaps:Int
Field      dwReserved1:Int
Field      dwReserved2:Int
Field      dwReserved3:Int
Field      dwReserved4:Int
End Type

Type Tdrag
   Field x%         'object x
   Field y%         'object y
   Field msx%         'mouse x
   Field msy%         'mouse y
   Field Width%      'object width
   Field Height%      'object height
   Field inuse%      'object selected flag
   Field dragging%      'mouse dragging object flag
   Field Oldmx%      ' old mouse x
   Field Oldmy%      ' old mouse y
   Global selected%  ' object selected
'   create a box(square) object

   Function Create:tdrag(x%,y%,Width%,Height%)
      Local box:Tdrag = New Tdrag
      box.x = x
      box.y = y
      box.Width = width
      box.Height = Height
      box.dragging = False
      box.inuse = False
      Return box
   End Function
'   test to see if mouse is width in the box/squre object   
   Method mouseinbox%()
      If msx < Self.x Return False
      If msx > (Self.x+Self.Width) Return False
      If msy < Self.y Return False
      If msy > (Self.y+Self.Height) Return False
      Return True
   End Method
'   move object to new position
   Method shift()
      Self.x :+(msx-oldmx)
      Self.y :+(msy-oldmy)
   End Method
'   check to see if mouse was moved
   Method mousemove%()
      If oldmx <> msx Return True
      If oldmy <> msy Return True
      Return False
   End Method
'   draw Object
   Method getxy(x% Var,y% Var)
      x = Self.x
      y = Self.y
   End Method
   Method getcenter(x% Var,y% Var)
      x = Self.x+width/2
  y = Self.y+height/2
   End Method
' animate box
   Method animate()
      msx = MouseX() 'assign to variable to avoid continuous mouse function calls
      msy = MouseY() '  ''                          ''
      If MouseDown(1)
         
   If dragging = True
            If mousemove()  shift() ' set new position of object
         Else
            If mouseinbox()
If inuse = False
                If selected = False
dragging = True ' find box and allow dragging'
            selected = True
EndIf
  EndIf
  Else
               inuse = True 'prevent any more selection of object/s
            EndIf
         EndIf
      Else
         dragging = False ' if mouse is not pressed stop moving object
         inuse = False ' allow selection of object/s
          selected = False
End If
      oldmx = msx ' store mouse current position for futere use
      oldmy = msy '      ''               ''      ''

   End Method
End Type


Function drawcircle (xC%,yC%,radius%)
If (xC-radius) > GraphicsWidth()  Return 
If (yC-radius) > GraphicsHeight()  Return
If (xC+radius) < 0 Then Return
If (yC+radius) < 0 Then Return
Local x:Int = 0
Local d:Int = (2*Radius)
Local y:Int=Radius
While x<y
If d < 0 Then
d = d + (4 * x) + 6
Else
d = d + 4 * (x - y) + 10
y = y - 1
End If
Plot(xC + X, yC + Y)
Plot(xC + X, yC - Y)
Plot(xC - X, yC + Y)
Plot(xC - X, yC - Y)
Plot(xC + Y, yC + X)
Plot(xC + Y, yC - X)
Plot(xC - Y, yC + X)
Plot(xC - Y, yC - X)
x=x+1
Wend
so far the only problem I have is that the balls slow down even with out any friction or gravity.
the source coude is mostly a straight copy from the flash files so keep in mind that the code is copyright and is limited to the conditions here:
http://www.tonypa.pri.ee/vectors/index.html
other than that you are free to use as you please with this code.
if anybody is interested in some of the original demos( I managed to convert most of them with limitations of mouse control but other than that they are straight copy),I can post them here.
« Last Edit: January 18, 2008 by JumpMan »