# VB:Tutorials:Building A Simple Physics Engine

This article will show you the basics of making a simple physics engine. This engine will handle circle-circle collision detection. Before we start, it may be useful to read: Pool Hall Lessons[1], which is what this is based on.

## Creating the Engine Core

Open a new ActiveX DLL and rename the class PEngine. This is the main engine which handles everything. Now add a new module named modMain.

A common thing we need is gravity, so make two public variables called sGravityX and sGravityY, both as Single. Also we need a way to make sure our physics loop doesn’t freeze, so add a Double called dMaxTime. Place them inside modMain:

```Public sGravityX As Single
Public sGravityY As Single

Public dMaxTime As Double```

Just for this engine I will provide rudimentary world bounding box collision detection. In a real physics engine, you should use proper line collision detection: [2]. Part Two will implement that.

```Public sWorldLeft As Single
Public sWorldRight As Single
Public sWorldTop As Single
Public sWorldBottom As Single```

So what do we have now? Nothing. We need a way to store circles, so make a class called PCircle. This is where a lot of code will go. We can make two public variables in PCircle named Radius to hold the radius, and Mass to hold Mass. We also need to be able to store position, velocity, and acceleration. Unfortunately, Visual Basic doesn’t let us make user-defined types public, so we have to make some functions to access those variables. Also, we need a way to store vectors, so make the PVector type. Add this code to the PCircle class:

(Just a side note here. Add a reference to DirectX to your application, use D3DVECTOR instead of 'PVector' and you're set to deal with 3D physics.. why was this done in 2D anyway?)

```Public Type PVector
X As Single
Y As Single
End Type

Private Position As PVECTOR
Private Velocity As PVECTOR
Private Acceleration As PVECTOR

Public Restitution As Single

Public Function SetPosition(ByVal X As Single, ByVal Y As Single)
Position.X = X
Position.Y = Y
End Function

Public Function GetPosition() As PVECTOR
GetPosition.X = Position.X
GetPosition.Y = Position.Y
End Function

Public Function ApplyPosition(ByVal X As Single, ByVal Y As Single)
Position.X = Position.X + X
Position.Y = Position.Y + Y
End Function

Public Function SetVelocity(ByVal X As Single, ByVal Y As Single)
Velocity.X = X
Velocity.Y = Y
End Function

Public Function GetVelocity() As PVECTOR
GetVelocity.X = Velocity.X
GetVelocity.Y = Velocity.Y
End Function

Public Function ApplyVelocity(ByVal X As Single, ByVal Y As Single)
Velocity.X = Velocity.X + X
Velocity.Y = Velocity.Y + Y
End Function

Public Function SetAcceleration(ByVal X As Single, ByVal Y As Single)
Acceleration.X = X
Acceleration.Y = Y
End Function

Public Function GetAcceleration() As PVECTOR
GetAcceleration.X = Acceleration.X
GetAcceleration.Y = Acceleration.Y
End Function

Public Function ApplyAcceleration(ByVal X As Single, ByVal Y As Single)
Acceleration.X = Acceleration.X + X
Acceleration.Y = Acceleration.Y + Y
End Function```

The Apply functions are useful for things like gravity, where you want to add to the existing variables. Restitution is how much energy something keeps after a collision. So putting 0.9 for Restitution means the the object keeps 90% of its energy when it hits something. Now we get to make Visual-Basic-style pointers. Actually, Visual Basic has pointer capabilities, without Windows API, but that's not for this article.

Go to the PEngine class. Make a variable array called Circles, along with a counter saying how many circles there are, like this:

```Private Circles() As PCircle
Private NumCircles As Long```

Make a function to allow adding circles:

```Public Function AddCircle(ByRef CircleData As PCircle)
ReDim Preserve Circles(NumCircles) As PCircle
Set Circles(NumCircles) = CircleData
NumCircles = NumCircles + 1
End Function```

There. We're getting closer to a functional engine. What this function does is set the Circles(NumCircles) variable to point to the source of CircleData. So changing CircleData changes Circle(NumCircles) and vice versa.

Remember those gravity variables? We need to access them, so make a public function called SetWorldProperties:

```Public Function SetWorldProperties(ByVal GravityX As Single, ByVal GravityY As Single, _
ByVal MaxTime As Double, _
ByVal WorldLeft As Single, ByVal WorldRight As Single, _
ByVal WorldTop As Single, ByVal WorldBottom As Single)
sGravityX = GravityX
sGravityY = GravityY
dMaxTime = MaxTime
sWorldLeft = WorldLeft
sWorldRight = WorldRight
sWorldTop = WorldTop
sWorldBottom = WorldBottom
End Function```

Closer! Now we need a way to update the position, velocity, and acceleration of the circles. However, I prefer time-based modeling, so add a class called PTimer and add this code:

```Private Declare Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As Currency) As Long
Private Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As Currency) As Long

Private Freq As Currency
Private StartTime As Currency
Private EndTime As Currency
Private TimeElapse As Double
Public TimeFactor As Double

Public Function Timing(ByVal Action As Boolean)
On Error Resume Next 'When the timer has not been initialize, but the timer
'is stopped, a divide by zero error occurs
Select Case Action
Case True 'Start
QueryPerformanceFrequency Freq
QueryPerformanceCounter StartTime
Case False 'Stop
QueryPerformanceCounter EndTime
TimeElapse = (EndTime - StartTime) / Freq
End Select
End Function

Public Property Get TimeElapsed() As Double
TimeElapsed = TimeElapse * TimeFactor
End Property```

The TimeFactor variable allows you to go in slow-motion, or fast-motion, by setting it higher or lower than 1, while 1 runs at normal time. This is one of those things you have to tweak with. Add an object called Timer at the very top of PEngine. Don’t forget the ‘New’ keyword. The top looks like this:

```Private Timer As New PTimer

Private Circles() As PCircle
Private NumCircles As Long```

Back to circles, we need a function to update circles. This will have two parameters. One is called TimeElapsed, to allow time-based modeling. If you don't want time-based modeling, just pass 1 to this function. The second is the index to the circle object:

(Sorry to interrupt this well written text here. The segment below is erratical: The increase in Y velocity should be dependant on TimeElapsed, and the new new Y position should be calculated based on (OldVelocity * TimeElapsed) + ((NewVelocity - OldVelocity) * 0.5 * TimeElapsed). Also, ApplyAcceleration and then SetAcceleration 0,0 seems a bit unnecessary. I leave to the original author to make the necessary fixes. /Waterman)

(Actually, if you made the changes or additions, people would know what you're talking about. When your suggestions are applied to this engine, the objects go UP. There is also no referrence to mass anywhere here. How do we deal with objects with different masses)

```Private Function UpdateCircle(ByVal TimeElapsed As Double, ByVal CircleIndex As Long)
With Circles(CircleIndex)
.ApplyVelocity .GetAcceleration.X, .GetAcceleration.Y 'Update Acceleration
.ApplyPosition .GetVelocity.X * TimeElapsed, .GetVelocity.Y * TimeElapsed 'Update Position
.SetAcceleration 0, 0 'Reset Acceleration
End With```

Just one last function! The UpdateWorld function, which updates all the circles. It too takes a TimeElapsed variable:

```Public Function UpdateWorld(ByVal TimeElapsed As Double)
Dim i As Long

For i = 0 To NumCircles - 1
Call UpdateCircle(TimeElapsed, i) 'Update Circle
Next i
End Function```

Now what do we have? A basic particle simulator. Also note that we didn’t use the Timer object. Why is this a basic particle simulator? None of the circles collide! So, whip out the 'Pool Hall Lessons' web page and follow along!(See Top)

## Collision Detection

After opening 'Pool Hall Lessons' [3], skip to the working code. Notice that we too need the vector functions. The first one you see is the Magnitude() function. So in modMain, type:

```Public Function VectorMagnitude(ByRef A As PVector) As Single
VectorMagnitude = Sqr(A.X * A.X + A.Y * A.Y)
End Function```

The second vector function you see is the Normalize() function, so add:

```Public Function VectorNormalize(ByRef A As PVector)
Dim Mag As Single

Mag = Sqr(A.X * A.X + A.Y * A.Y)

If Mag <> 0 Then
A.X = A.X / Mag
A.Y = A.Y / Mag
End If
End Function```

The third one you see is the Dot Product:

```Public Function VectorDotProduct(ByRef A As PVector, ByRef B As PVector) As Single
VectorDotProduct = A.X * B.X + A.Y * B.Y
End Function```

The last one is the Distance function:

```Public Function VectorDistance(ByRef A As PVector, ByRef B As PVector) As Single
VectorDistance = Sqr((A.X - B.X) * (A.X - B.X) + (A.Y - B.Y) * (A.Y - B.Y))
End Function```

Now to get started. Define the function in PEngine like this:

```Public Function CircleCircleCollision(ByVal TimeElapsed As Double, _
ByVal Index1 As Long, ByVal Index2 As Long) As Boolean```

At the top of the function, type this:

```Dim A As PVector, B As PVector, VA As PVector, VB As PVector
Dim MoveVec As PVector
A = Circles(Index1).GetPosition
B = Circles(Index2).GetPosition
VA = Circles(Index1).GetVelocity
VB = Circles(Index2).GetVelocity
VA.X = VA.X * TimeElapsed
VA.Y = VA.Y * TimeElapsed
VB.X = VB.X * TimeElapsed
VB.Y = VB.Y * TimeElapsed

MoveVec.X = VA.X - VB.X
MoveVec.Y = VA.Y - VB.Y```

This function first stores the Positions and Velocities of both circles in variables with shorter names. Not only is it easier to type "A" than "Circles(Index1).GetPosition", it's faster, because the latter calls a function. The next thing the code does is it subtracts one velocity from the other. Because the test is orginally for circles where one is moving and the other is stationary, we must find their relative velocities. Now to code. The first section in 'Pool Hall Lessons' is:

```// Early Escape test: if the length of the movevec is less
// than distance between the centers of these circles minus
// their radii, there's no way they can hit.
double dist = B.center.distance(A.center);
if(movevec.Magnitude() < dist){
return false;
}```

In Visual Basic, this is:

```Dim dist As Single, sumRadii As Single
dist = VectorDistance(A, B)
If VectorMagnitude(MoveVec) < dist Then
CircleCircleCollision = False
Exit Function
End If```

The next section is:

```// Normalize the movevec
Vector N = movevec.copy();
N.normalize();

// Find C, the vector from the center of the moving
// circle A to the center of B
Vector C = B.center.minus(A.center);

// D = N . C = ||C|| * cos(angle between N and C)
double D = N.dot(C);```

This just initializes some variables. It does not do any tests. In Visual Basic this is:

```Dim N As PVector, C As PVector, D As Single
N = MoveVec
Call VectorNormalize(N)

C.X = B.X - A.X
C.Y = B.Y - A.Y

D = VectorDotProduct(N, C)```

Now this new information is put to use. This section makes sure the ball is moving towards the other ball:

```// Another early escape: Make sure that A is moving
// towards B! If the dot product between the movevec and
// B.center - A.center is less that or equal to 0,
// A isn't isn't moving towards B
if(D <= 0){
return false;
}```

That last part is the code, for those who don't know C++. Easy:

```If D <= 0 Then
CircleCircleCollision = False
Exit Function
End If```

Another test makes sure they can even hit each other:

```// Find the length of the vector C
double lengthC = C.Magnitude();

double F = (lengthC * lengthC) - (D * D);

// Escape test: if the closest that A will get to B
// is more than the sum of their radii, there's no
// way they are going collide
return false;
}```

This is fairly simple to implement in Visual Basic:

```Dim LengthC As Single, F As Single, sumRadiiSquared As Single
LengthC = VectorMagnitude(C)
F = (LengthC * LengthC) - (D * D)

CircleCircleCollision = False
Exit Function
End If```

The next section gets a variable T and sees if it is less than 0:

```// We now have F and sumRadii, two sides of a right triangle.
// Use these to find the third side, sqrt(T)
double T = sumRadiiSquared - F;

// If there is no such right triangle with sides length of
// sumRadii and sqrt(f), T will probably be less than 0.
// Better to check now than perform a square root of a
// negative number.
if(T < 0){
return false;
}```

Also very simple in Visual Basic:

```Dim T As Single
If T < 0 Then
CircleCircleCollision = False
Exit Function
End If```

This last bit makes two variables and tests them:

```// Therefore the distance the circle has to travel along
// movevec is D - sqrt(T)
double distance = D - sqrt(T);

// Get the magnitude of the movement vector
double mag = movevec.Magnitude();

// Finally, make sure that the distance A has to move
// to touch B is not greater than the magnitude of the
// movement vector.
if(mag < distance){
return false;
}```

In Visual Basic:

```Dim Distance As Single, Mag As Single
Distance = D - Sqr(T)
Mag = VectorMagnitude(MoveVec)

If Mag < Distance Then
CircleCircleCollision = False
Exit Function
End If```

Finallly! Now that we are sure the circles collide, move them with all this data we have until they just touch. Note that the article only shows code for a stationary-moving circle test, so I made code for two moving circles:

```Dim UA As Single, UB As Single

If VectorMagnitude(VA) = 0 Then
UA = 0
Else
UA = VectorMagnitude(MoveVec) / VectorMagnitude(VA)
End If
If VectorMagnitude(VB) = 0 Then
UB = 0
Else
UB = VectorMagnitude(MoveVec) / VectorMagnitude(VB)
End If

VA.X = VA.X * UA
VA.Y = VA.Y * UA
VB.X = VB.X * UB
VB.Y = VB.Y * UB

Circles(Index1).ApplyPosition VA.X, VA.Y
Circles(Index2).ApplyPosition VB.X, VB.Y

VA = Circles(Index1).GetVelocity
VB = Circles(Index2).GetVelocity
A = Circles(Index1).GetPosition
B = Circles(Index2).GetPosition```

This makes the circle just touch, then restores the position and velocity variables. Now that we have collision detection, time for collision response.

## Collision Response

Getting our circles to move in a realistic manner is easy. I will simply transfer the code from Pool Hall Lessons directly to Visual Basic. In C++ the code looks like this:

```// First, find the normalized vector n from the center of
// circle1 to the center of circle2
Vector n = circle1.center - circle2.center;
n.normalize();
// Find the length of the component of each of the movement
// vectors along n.
// a1 = v1 . n
// a2 = v2 . n
float a1 = v1.dot(n);
float a2 = v2.dot(n);

// Using the optimized version,
// optimizedP =  2(a1 - a2)
//              -----------
//                m1 + m2
float optimizedP = (2.0 * (a1 - a2)) / (circle1.mass + circle2.mass);

// Calculate v1', the new movement vector of circle1
// v1' = v1 - optimizedP * m2 * n
Vector v1' = v1 - optimizedP * circle2.mass * n;

// Calculate v1', the new movement vector of circle1
// v2' = v2 + optimizedP * m1 * n
Vector v2' = v2 + optimizedP * circle1.mass * n;

circle1.setMovementVector(v1');
circle2.setMovementVector(v2');```

In Visual Basic, the code looks much easier:

```N.X = A.X - B.X 'NOTE:
N.Y = A.Y - B.Y 'N already exists above
Call VectorNormalize(N)

Dim A1 As Single, A2 As Single
A1 = VectorDotProduct(VA, N)
A2 = VectorDotProduct(VB, N)

Dim optimizedP As Single
optimizedP = (2 * (A1 - A2)) / (Circles(Index1).Mass + Circles(Index2).Mass)

Dim V1 As PVector, V2 As PVector
V1.X = VA.X - (optimizedP * Circles(Index2).Mass * N.X)
V1.Y = VA.Y - (optimizedP * Circles(Index2).Mass * N.Y)
V2.X = VB.X + (optimizedP * Circles(Index1).Mass * N.X)
V2.Y = VB.Y + (optimizedP * Circles(Index1).Mass * N.Y)

Circles(Index1).SetVelocity V1.X * Circles(Index1).Restitution, V1.Y * Circles(Index1).Restitution
Circles(Index2).SetVelocity V2.X * Circles(Index2).Restitution, V2.Y * Circles(Index2).Restitution

CircleCircleCollision = True```

This code, unlike 'Pool Hall Lessons', takes into account the restitution of the ball. Without it, the circles would just keep bouncing forever. Do NOT set the restitution above 1. Doing so would not only be unrealistic, it generates an overflow error.

## Almost Done

Now we just need to update the UpdateWorld function, which has taken quite a change:

```Dim i As Long, j As Long, bCollided As Boolean, T As Double

If TimeElapsed = 0 Then Exit Function

Timer.TimeFactor = 1
Do 'Loop until...
''''''''''Circle - Circle''''''''''
Timer.Timing True
bCollided = False
For i = 0 To NumCircles - 1
For j = i To NumCircles - 1
If i <> j Then
If CircleCircleCollision(TimeElapsed, i, j) Then
bCollided = True
End If
End If
Next j
Next i
'''''''''Circle - Line''''''''''''''
Dim tX As Single, tY As Single, vX As Single, vY As Single
For i = 0 To NumCircles - 1
With Circles(i)
tX = .GetPosition.X: tY = .GetPosition.Y
vX = .GetVelocity.X * .Restitution: vY = .GetVelocity.Y * .Restitution
If tX < sWorldLeft + .Radius Then
bCollided = True
.SetVelocity -vX, vY
ElseIf tX + .Radius > sWorldRight Then
bCollided = True
.SetVelocity -vX, vY
ElseIf tY < sWorldTop + .Radius Then
bCollided = True
.SetVelocity vX, -vY
ElseIf tY + .Radius > sWorldBottom Then
bCollided = True
.SetVelocity vX, -vY
End If
End With
Next i
''''''''''Update Timer''''''''''''''
Timer.Timing False
T = T + Timer.TimeElapsed
Loop Until (bCollided = False) Or (T > dMaxTime) 'no collisions are found, or the time has taken too long.
Timer.Timing True

'Now update circles
For i = 0 To NumCircles - 1
Call UpdateCircle(TimeElapsed, i) 'Update Circle
Next i

Timer.Timing False
UpdateWorld = T + Timer.TimeElapsed```

Now it makes sure the loop doesn’t freeze up. Also note that the UpdateWorld function returns a Double, telling how long the function took (in milliseconds I think). If it is in milliseconds, it is very fast. If it's in seconds, blame it on Visual Basic.

## Done

Now you can compile the DLL and link it to your programs.