12/9/07

Fun with (very basic) physics

[GS 2.0 Beta]

So in developing a new system of sprite movement, I decided to try a little bit of really basic physics. Adding a few simple members to the sprite class to store values relating to velocity, momentum & friction:

 Protected objVelocity As Vector2
 Protected sngFriction, sngMass As Single

and then, adding method called ApplyForce:

 Public Sub ApplyForce(ByVal force As Vector2)

 If Me.sngMass > 0 Then
 Me.objVelocity += (force / Me.sngMass)
 Else
 Me.objVelocity += force
 End If

 End Sub

and finally, processing this information during update:

 Public Sub UpdatePhysics()

 Dim time As Single
 Dim negX, negY As Boolean

 'Our unit of measure is pixles per second
 time = Me.Game.GameTime.ElapsedGameTime.TotalMilliseconds / 1000

 'If there is any velocity...

 If Me.objVelocity <> Vector2.Zero Then

 'before we apply friction, let's remember whether
 'or not we started out with positive or negative
 'velocity value, for both x and y, by setting
 '"wasNegativeX/Y" booleans

 negX = Me.objVelocity.X < 0
 negY = Me.objVelocity.Y < 0

 'simulate some psuedo-friction by normalizing the
 'velocity vector and multiplying it by the friction
 'value.  This gives us an amount to "push" the object
 'in the exact opposite direction.  Multiplied by
 'time to do just enough for this frame only.  Subtract
 'the result from the object's velocity, to slow it down.

 Me.objVelocity -= (Vector2.Normalize(Me.objVelocity) * Me.sngFriction) * time

 'Because friction is simulated by pushing the object in
 'the opposite direction, towards the end when the object
 'is almost stopped we don't want the psuedo-friction to
 'start pushing the object backwards; at most, friction
 'stops something, but it shouldn't ever move it.  So using
 'or cached "whether or not it was negative" booleans,
 'check to see if the polarity of our X/Y velocity changed
 'due to friction.  If it did, snap the velocity of that
 'value to 0.

 If (negX And Me.objVelocity.X > 0) Or (Not negX And Me.objVelocity.X < 0) Then
 Me.objVelocity.X = 0
 End If

 If (negY And Me.objVelocity.Y > 0) Or (Not negY And Me.objVelocity.Y < 0) Then
 Me.objVelocity.Y = 0
 End If

 'increment the object's position by the amount of
 'velocity appropriate for this frame by multiplying
 'by the time value.

 Me.Position += (Me.objVelocity * time)

 End If

 End Sub

I haven't really discussed my existing system for movement on the blog yet; we'll get to that eventually, but I was amused by this velocity/friction/mass approach and it's probably more relevant to the needs of gaming anyway.

This is far from an actual simulator, I'm only using mass to resist initial force and not really applying it to friction but that's ok. This is supposed to be simple, and the results aren't too shabby.



In the video, just the new "physics" values introduced are being used to move the spaceship around. While the player holds down the thrust key, the current orientation of the ship * some thrust value determines a parameter for ApplyForce. Using a PlayerShip game object (as described in previous tutorials, it just inherits from sprite), I created a ThrusterOn method. As long as the user holds the truster key down, I call PlayerShip.ThrusterOn

 Public Sub ThrusterOn()

 Dim time As Single

 time = Me.Game.GameTime.ElapsedGameTime.TotalMilliseconds / 1000

 Me.ApplyForce((Vector2.Normalize(New Vector2(Math.Sin(CDbl(Me.sngRotation)), -Math.Cos(CDbl(Me.sngRotation)))) * 250) * time)

 End Sub


The ship has a little mass to it, so it resists force somewhat (this gives you a way to tweak acceleration curve vs. thruster power), and the ship's psuedo-friction value constantly pulls it's velocity toward zero. This example was built using my "real" 2D engine (which is a little further along than what the blog has covered so far, i.e. there's keyboard handling & debugging & screen management, etc.), but the movement you see is achieved through the simple additions described above.

0 comments: