12/11/07

A thruster component

[GS 2.0 Beta]

One of the things I want to feature in Star Maze are ship upgrades, allowing you to attach different thrusters & weapons to your ship.

Given the parent/child system, we know that we can "attach" one sprite to another, like what we did in Tutorial 7. And since our game objects are nothing more than "sprites with a brain", we can actually attach one game object to another. So after incorporating the psuedo-physics parameters & processing to the sprite class, I created a game component meant soley to be a child of another; a thruster module which can be attached to anything to make it move. This is the base thruster class from which you derive to create a custom thruster object:

Public Interface IThruster
 Sub StopThrusterAnim()
 Sub StartThrusterAnim()
End Interface

Public MustInherit Class BaseThruster
 Inherits Sprite
 Implements IThruster

 Protected sngForce As Single
 Protected enumKey As Keys
 Protected bolIsOn As Boolean
 Protected enumType As ThrusterType

 Public MustOverride Sub StopThrusterAnim() Implements IThruster.StopThrusterAnim
 Public MustOverride Sub StartThrusterAnim() Implements IThruster.StartThrusterAnim

 Public Property Force() As Single
 Get
 Return Me.sngForce
 End Get
 Set(ByVal value As Single)
 Me.sngForce = value
 End Set
 End Property

 Enum ThrusterType
 Linear
 Rotational
 End Enum

 Public Overrides Sub Update()

 Dim quant As Quaternion
 Dim direction As Vector2

 If Me.Game.KeyboardState.IsKeyDown(Me.enumKey) Then

 If Not Me.bolIsOn Then
 Me.StartThrusterAnim()
 Me.bolIsOn = True
 End If

 Select Case Me.enumType

 Case ThrusterType.Linear

 Me.WorldMatrix.Decompose(Nothing, quant, Nothing)
 Vector2.Transform(New Vector2(Math.Sin(CDbl(Me.sngRotation)), -Math.Cos(CDbl(Me.sngRotation))), Matrix.CreateFromQuaternion(quant), direction)

 Me.Parent.ApplyForce((direction * Me.sngForce) * (Me.Game.GameTime.ElapsedGameTime.TotalMilliseconds / 1000))

 Case ThrusterType.Rotational

 Me.Parent.ApplyRotationalForce(Me.sngForce * (Me.Game.GameTime.ElapsedGameTime.TotalMilliseconds / 1000))

 End Select

 ElseIf Me.bolIsOn Then

 Me.StopThrusterAnim()
 Me.bolIsOn = False

 End If

 MyBase.Update()

 End Sub

 Public Sub New(ByVal parent As Ship, ByVal type As ThrusterType, ByVal key As Keys, ByVal name As String, ByVal force As SingleByVal containerSize As Vector2)
 MyBase.New(Program.StarMaze, parent, name, TrueTrue, containerSize)
 Me.sngForce = force
 Me.enumKey = key
 Me.enumType = type
 End Sub

 Public Sub New(ByVal parent As Ship, ByVal type As ThrusterType, ByVal key As Keys, ByVal name As String, ByVal force As SingleByVal sheet As String, ByVal subtexture As String)
 MyBase.New(Program.StarMaze, parent, name, TrueTrue, sheet, subtexture)
 Me.sngForce = force
 Me.enumKey = key
 Me.enumType = type
 End Sub

End Class


With the base thruster class you can create a rotational thruster for spinning around or a linear thruster for vector movement. A keyboard key parameter will be monitored by the thruster to turn itself on and off, and there's a parameter to hold how much force the thruster applies to it's parent (whatever it's attached to).

When it Updates, it calls the ApplyForce or ApplyRotationalForce methods of it's parent, and the system moves accordingly.

A simple interface defines two routines, StopThrusterAnim & StartThrusterAnim, which provides the implementor with a place & time to perform graphical feedback as it relates to the thruster entering an On or Off state.

Using this base thruster class I created 4 different game components; custom thrusters which I can attach to the player's ship. Two different aft thrusters (linear), a port thruster (clockwise rotational), and a starboard thruster (counterclockwise rotational).

The player's ship object can now be defined very simply, by creating instances of these thrusters and attaching whichever ones we want accordingly:

Public Class Ship
 Inherits Sprite

 Protected aftThruster, portThruster, starboardThruster As BaseThruster

 Public Sub New()

 MyBase.New(Program.StarMaze, Nothing"playership"TrueTrue"starmaze""playership")

 Me.Origin = New Vector2(54, 82)

 Me.sngMass = 1
 Me.sngFriction = 25

 Me.aftThruster = New AftThruster2(Me)
 Me.portThruster = New PortThruster1(Me)
 Me.starboardThruster = New StarboardThruster1(Me)

 Me.AddChild(Me.aftThruster)
 Me.AddChild(Me.portThruster)
 Me.AddChild(Me.starboardThruster)

 End Sub

End Class

Here's a quick lo-fi vid of this in action:


(direct link to youtube)

In the video I start out using one of two aft thruster classes which derive from base thruster (the green one, it produces 500 pixels per second (pps) of linear force). When the app stops and I go into the IDE, I swap out the green 500pps thruster for a different thruster class, a red one that produces 250 pps of linear force. You my also be able to see the tiny little port and starboard thrusters applying the counter- and clockwise rotational forces as well.

As you can see from the Ship class code above, the ship itself has no inherit way to move itself; it's just a hull that doesn't handle keyboard input or anything of the sort. To make it move, we simply attach thruster components. The neat part about this system is that you can attach any thruster you want to any game object you want. You can put thrusters on the player's ship, on enemy AI ships, you can stick one on a bowling ball if you want. The thruster component is a somewhat generic way to apply force, to any game object.

My laptop was lagging for unrelated reasons when I recorded this, so the fps is kinda low, but in an otherwise normal environment the code runs quite nicely.

2 comments:

Zygote said...

The code looks pretty slick :D I would like to see the video however but youtube says it is no longer available :(

Ziggy
Ziggyware XNA News and Tutorials

emachine74 said...

Thanks :) I have changed it somewhat by adding a speed cap, so thrusters understand to max out once the parent velocity matches their force value. The current code just keeps getting faster and faster...

I dunno what youtube's problem is, I'll just add an additional link beneath the embeded video and hope visiting youtube directly does the trick. I see the vid ok, but that may not count since I own it.