Chopper-Game Example

(Compatible with CTP2 v 0.0.2.2-5)

(The entire source code for an expanded version of this example is available in Changeset 14546 (3/31/2012 or later) in the GdiGamingExample1 project.)

The Chopper-Game is a simple example game using the GdiGaming API. In this game, the player will fly a small helicopter and shoot missles at an invading alien fleet. This is a top-down, top-scrolling game with simple mechanics and game play.

The entire example project can be downloaded in a zip file, or you can download just the compiled assembly to play with the example. (Note: these downloads are based on version 0.0.2.1 - the only issue with the project is that you will have to adjust the scroll speed of the CloudyBackground from 3 to 96; the compiled game will include the associated DLL version)

Chopper-Game Example Screen-Shot
Chopper-Game Example

Setup the Form

To create the example, follow the steps in Getting Started to setup a basic game environment. For this game, set the Form size to 650x511 and the RenderCanvas size to 640x480. The same template code can be used for the Form of nearly every GdiGaming game. Here is the code:

Imports GdiGaming

Public Class Form1
    Private Sub Form1_Activated(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Activated
        GameEngine1.StartGame()
    End Sub

    Private Sub Form1_Deactivate(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Deactivate
        If GameEngine1.EngineState = GdiGaming.GameEngineState.Started Then
            GameEngine1.PauseGame()
        End If
    End Sub

    Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        If Not GameEngine1.EngineState = GdiGaming.GameEngineState.Stopped Then
            GameEngine1.EndGame()
            e.Cancel = True
        End If
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'Add the game's initial scene to the engine; replace "ExampleScene" with your custom scene
        GameEngine1.Scenes.Add(New ExampleScene)
    End Sub

    Private Sub GameEngine1_FrameComplete(ByVal sender As Object, ByVal e As GdiGaming.GameEngineEventArgs) Handles GameEngine1.FrameComplete
        'Uncomment the following line to easily monitor the framerate during development:
        Me.Text = e.Engine.FrameRate.ToString
    End Sub

    Private Sub GameEngine1_GameStopped(ByVal sender As Object, ByVal e As System.EventArgs) Handles GameEngine1.GameStopped
        Me.Close()
    End Sub
End Class

Download and Add the Assets

Download the three images attached to this page (AirVehicles.png, Clouds.png, and Explosion.png) and then add them to the Assets folder in the Solution Explorer. If you have not yet created an Assets folder, do so now by right-clicking the project in the Solution Explorer and choosing Add -> New Folder. Remember to set the Copy to Output Directory property to Copy Always for each file!

Image Examples:
Air Vehicles Explosion

Create Basic Game Objects - Player, Enemy, and Missle

The game requires three primary objects: the player chopper, the alien spaceship, and the missle fired by the chopper. There will only ever be one instance of the player chopper, but there will be multiple instance of the alien ship and missle. The three classes have some knowledge of one another, a choice made because the example is small - you could implement whatever OOP strategies you needed in order to abstract the logic, so you'll need to get them all created before the code errors disappear.

Create the following classes in your solution and paste in the code provided:

PlayerChopper

Imports GdiGaming

Public Class PlayerChopper
    Inherits GameObject

    Private _BlinkTime As Single
    Private _FireWait As Single
    Private _FramesHidden As Integer
    Private _NewPosition As Vector2

    Private _FireRate As Single
    Public Property FireRate() As Single
        Get
            Return _FireRate
        End Get
        Set(ByVal value As Single)
            _FireRate = value
        End Set
    End Property

    Private _HitsRemaining As Integer
    Public Property HitsRemaining() As Integer
        Get
            Return _HitsRemaining
        End Get
        Set(ByVal value As Integer)
            _HitsRemaining = value
        End Set
    End Property

    Private _Speed As Single = 96.0F
    Public Property Speed() As Single
        Get
            Return _Speed
        End Get
        Set(ByVal value As Single)
            _Speed = value
        End Set
    End Property

    Public Sub New()
        HasCollision = True
        CollisionRadius = 14
        _Name = "Player"
        _ProcessInput = True
        _ZOrder = 1
        _HitsRemaining = 10
        _FireRate = 3.0
    End Sub

    Protected Overrides Sub OnCollision(ByVal e As GdiGaming.CollisionEventArgs)
        MyBase.OnCollision(e)
        If _BlinkTime = 0.0F Then
            If TypeOf e.CollisionObject Is EnemySpaceship Then
                Dim spaceShip As EnemySpaceship = DirectCast(e.CollisionObject, EnemySpaceship)
                spaceShip.Position -= Vector2.GetVectorToward(spaceShip.Position, Position, CollisionRadius)
                _BlinkTime = 2.0F
                _HitsRemaining -= 1
            End If
        End If
    End Sub

    Protected Overrides Sub OnLoad(e As GdiGaming.GameEngineEventArgs)
        MyBase.OnLoad(e)
        Dim chopperSprite As New AnimatedSprite
        chopperSprite.SpriteSheet = "AirVehicles"
        chopperSprite.AnimationRate = 0.25F
        chopperSprite.AddFramesHV(1, 4, 48, 48)
        chopperSprite.Name = "Chopper"
        Sprites.Add(chopperSprite)
    End Sub

    Protected Overrides Sub OnInput(ByVal e As GdiGaming.GameEngineEventArgs)
        MyBase.OnInput(e)
        Dim input As GameInput = e.Engine.Input
        _RotationAngle = 0.0F
        _NewPosition = Vector2.Empty
        If input.IsKeyDown(Keys.W) Then
            _NewPosition += New Vector2(0, -_Speed * e.Time.LastFrame)
            _RotationAngle = 0.0F
        End If
        If input.IsKeyDown(Keys.S) Then
            _NewPosition += New Vector2(0, _Speed * e.Time.LastFrame)
            _RotationAngle = 180.0F
        End If
        If input.IsKeyDown(Keys.A) Then
            _NewPosition += New Vector2(-_Speed * e.Time.LastFrame, 0)
            If input.IsKeyDown(Keys.W) Then
                _RotationAngle = 315.0F
            ElseIf input.IsKeyDown(Keys.S) Then
                _RotationAngle = 225.0F
            Else
                _RotationAngle = 270.0F
            End If
        End If
        If input.IsKeyDown(Keys.D) Then
            _NewPosition += New Vector2(_Speed * e.Time.LastFrame, 0)
            If input.IsKeyDown(Keys.W) Then
                _RotationAngle = 45.0F
            ElseIf input.IsKeyDown(Keys.S) Then
                _RotationAngle = 135.0F
            Else
                _RotationAngle = 90.0F
            End If
        End If
        If input.WasKeyPressed(Keys.Space) Then
            If _FireWait = 0.0F Then
                _FireWait = 1.0F / _FireRate
                FireMissile(e.Engine)
            End If
        End If
    End Sub

    Protected Overrides Sub OnUpdate(ByVal e As GdiGaming.GameEngineEventArgs)
        MyBase.OnUpdate(e)
        Dim bounds As Rectangle = e.Engine.Canvas.DisplayRectangle
        bounds.Inflate(-24, -24)
        Dim deltaPosition As Vector2 = Position + _NewPosition
        If bounds.Contains(deltaPosition.ToPoint) Then
            Position = deltaPosition
        End If
        If _HitsRemaining < 1 Then
            e.Engine.CurrentScene.Objects.Remove(Me)
            Dim boom As New Explosion
            boom.Position = Position
            e.Engine.CurrentScene.Objects.Add(boom)
        End If
        If _BlinkTime > 0 Then
            If _FramesHidden = 0 Then
                _Visible = Not _Visible
                _FramesHidden = 5
            End If
            _FramesHidden -= 1
            _BlinkTime -= e.Time.LastFrame
        Else
            _BlinkTime = 0.0F
            _FramesHidden = 0
            _Visible = True
        End If
        If _FireWait > 0 Then
            _FireWait -= e.Time.LastFrame
        Else
            _FireWait = 0.0F
        End If
    End Sub

    Private Sub FireMissile(ByVal engine As GameEngine)
        Dim missileHeading As Vector2 = _NewPosition
        If missileHeading = Vector2.Empty Then
            missileHeading = New Vector2(0, -_Speed * engine.Time.LastFrame)
        End If
        missileHeading *= 1.5F
        Dim missile As New ChopperMissile(missileHeading, _RotationAngle)
        missile.Position = Position
        engine.CurrentScene.Objects.Add(missile)
    End Sub
End Class

EnemySpaceShip

Imports GdiGaming

Public Class EnemySpaceship
    Inherits GameObject

    Private Shared _Random As New Random

    Private _Destination As Vector2

    Private _Speed As Single = 72.0F
    Public Property Speed() As Single
        Get
            Return _Speed
        End Get
        Set(ByVal value As Single)
            _Speed = value
        End Set
    End Property

    Public Sub New()
        Dim spaceshipSprite As New AnimatedSprite
        spaceshipSprite.SpriteSheet = "AirVehicles"
        spaceshipSprite.AnimationRate = 0.75F
        spaceshipSprite.AddFramesHV(1, 4, 48, 48, 1)
        spaceshipSprite.Name = "Spaceship"
        Sprites.Add(spaceshipSprite)
        HasCollision = True
        CollisionRadius = 24
        _ZOrder = 2
    End Sub

    Protected Overrides Sub OnCollision(ByVal e As GdiGaming.CollisionEventArgs)
        MyBase.OnCollision(e)
        If TypeOf e.CollisionObject Is ChopperMissile Then
            Dim scene As ExampleScene = CType(e.Engine.CurrentScene, ExampleScene)
            scene.Enemies.Remove(Me)
            scene.Objects.Remove(Me)
            scene.Objects.Remove(e.CollisionObject)
            Dim boom As New Explosion
            boom.Position = Position
            scene.Objects.Add(boom)
        End If
        If TypeOf e.CollisionObject Is EnemySpaceship Then
            Position = Vector2.GetVectorToward(Position, e.CollisionObject.Position, -_Speed * e.Time.LastFrame)
            _Destination = GetRandomDestination(e.Engine.Canvas.DisplayRectangle.Size)
        End If
    End Sub

    Protected Overrides Sub OnLoad(ByVal e As GdiGaming.GameEngineEventArgs)
        MyBase.OnLoad(e)
        _Destination = GetRandomDestination(e.Engine.Canvas.DisplayRectangle.Size)
    End Sub

    Protected Overrides Sub OnUpdate(ByVal e As GdiGaming.GameEngineEventArgs)
        MyBase.OnUpdate(e)
        If Position.DistanceTo(_Destination) < CollisionRadius Then
            _Destination = GetRandomDestination(e.Engine.Canvas.DisplayRectangle.Size)
        End If
        Position = Vector2.GetVectorToward(Position, _Destination, _Speed * e.Time.LastFrame)
    End Sub

    Private Function GetRandomDestination(ByVal area As Size) As Vector2
        Return New Vector2((area.Width - CollisionRadius * 2) * _Random.NextDouble, (area.Height - CollisionRadius * 2) * _Random.NextDouble)
    End Function
End Class

ChopperMissile

Imports GdiGaming

Public Class ChopperMissile
    Inherits GameObject

    Private _Direction As Vector2

    Public Sub New(ByVal direction As Vector2, ByVal rotation As Single)
        Dim missileSprite As New AnimatedSprite
        missileSprite.SpriteSheet = "AirVehicles"
        missileSprite.AnimationRate = 0.25F
        missileSprite.AddFramesHV(1, 4, 48, 48, 2)
        missileSprite.Name = "Missile"
        Sprites.Add(missileSprite)
        IsCollider = True
        CollisionRadius = 3
        _ZOrder = 1
        _Direction = direction
        _RotationAngle = rotation
    End Sub

    Protected Overrides Sub OnUpdate(ByVal e As GdiGaming.GameEngineEventArgs)
        MyBase.OnUpdate(e)
        Position += _Direction
        Dim bounds As Rectangle = e.Engine.Canvas.DisplayRectangle
        If Not bounds.Contains(Position.ToPoint) Then
            e.Engine.CurrentScene.Objects.Remove(Me)
        End If
    End Sub
End Class

Other Visual Elements - Explosion, Background, and PlayerLifeLabel

In addition to the player, ship, and missile, the game also needs a sprite for the explosion when an enemy ship is destroyed, as well as a background for the chopper and ships to fly over. The explosion is simply another GameObject, but the background is a ScrollingBackground object which will scroll an image in a particular direction at a given speed, with infinite wrapping.

The example also implements a single HUD (Heads-Up-Display) element called PlayerLifeLabel. This is a GUI (Graphical User Interface) element which will display the number of lives remaining. Each time the player hits an enemy ship, they loose one life.

Explosion

Imports GdiGaming

Public Class Explosion
    Inherits GameObject

    Private _ElapsedTime As Single
    Private _DisplayTime As Single = 0.5F

    Public Sub New()
        Dim explosion As New AnimatedSprite
        explosion.SpriteSheet = "Explosion"
        explosion.AnimationRate = _DisplayTime
        explosion.AddFramesHV(4, 4, 48, 48)
        explosion.Name = "Explosion"
        Sprites.Add(explosion)
        _ZOrder = 3
    End Sub

    Protected Overrides Sub OnUpdate(ByVal e As GdiGaming.GameEngineEventArgs)
        MyBase.OnUpdate(e)
        If _ElapsedTime >= _DisplayTime Then
            e.Engine.CurrentScene.Objects.Remove(Me)
        End If
        _ElapsedTime += e.Time.LastFrame
    End Sub
End Class

CloudyBackground

Imports GdiGaming

Public Class CloudyBackground
    Inherits ScrollingBackground

    Public Sub New()
        _AutoScroll = True
        _BackgroundImage = "Clouds"
        _InvertDirection = True
        _ScrollDirection = Orientation.Vertical
        _ScrollSpeed = 96.0F
    End Sub
End Class

PlayerLifeLabel

Imports GdiGaming

Public Class PlayerLifeLabel
    Inherits HudLabel

    Protected _Player As PlayerChopper
    Public ReadOnly Property Player As PlayerChopper
        Get
            Return _Player
        End Get
    End Property

    Public Sub New(ByVal target As PlayerChopper)
        FontSize = 12
        FontStyle = Drawing.FontStyle.Bold
        TextColor = Color.Orange
        BackColor = Color.Gray
        _Player = target
    End Sub

    Protected Overrides Sub OnUpdate(ByVal e As GdiGaming.GameEngineEventArgs)
        Text = "LIFE - " & _Player.HitsRemaining.ToString
        MyBase.OnUpdate(e)
    End Sub
End Class

Tying it Together - LevelManager and Scene

Now that we have all of the interactive elements needed by our game, all that remains is to create a logic-controller to execute each level and to create the main game scene which will contain all of these objects.

To execute the logic of the game, we can create one last GameObject called LevelManager which will use its OnUpdate method to track the game progress and its OnDraw method to display the current level information. Note that this example is drawing the level text manually, but it could also be coded to change the text and visible state of a HudLabel game object instead.

LevelManager

Imports GdiGaming

Public Class LevelManager
    Inherits GameObject

    Private _Scene As ExampleScene
    Private _Random As New Random

    Private _ShowLevelTime As Single
    Private _IsLevelReady As Boolean
    Private _LevelText As String
    Private _LevelFont As Font
    Private _TextBounds As Rectangle

    Private _Level As Integer
    Public ReadOnly Property Level As Integer
        Get
            Return _Level
        End Get
    End Property

    Public Sub New(ByVal targetScene As ExampleScene)
        _IsLevelReady = True
        _Scene = targetScene
        _ShowLevelTime = -1.0F
        _ZOrder = 1000
        _LevelFont = New Font("Segoe UI", 32, FontStyle.Bold)
    End Sub

    Protected Sub SetupLevel()
        _IsLevelReady = False
        _Level += 1
        _ShowLevelTime = 2
        _LevelText = "LEVEL " & _Level.ToString
        Dim textSize As Size = TextRenderer.MeasureText(_LevelText, _LevelFont)
        _TextBounds = New Rectangle(_Scene.Engine.Canvas.DisplayRectangle.Width / 2 - textSize.Width / 2, _Scene.Engine.Canvas.DisplayRectangle.Height / 2 - textSize.Height / 2, textSize.Width, textSize.Height)
    End Sub

    Protected Overrides Sub OnDraw(ByVal e As GdiGaming.DrawEventArgs)
        MyBase.OnDraw(e)
        If _ShowLevelTime > 0.0F Then
            TextRenderer.DrawText(e.PaintEventArgs.Graphics, _LevelText, _LevelFont, _TextBounds, Color.Orange)
        End If
    End Sub

    Protected Overrides Sub OnUpdate(ByVal e As GdiGaming.GameEngineEventArgs)
        MyBase.OnUpdate(e)
        Dim scene As ExampleScene = CType(e.Engine.CurrentScene, ExampleScene)

        If _Scene.Enemies.Count = 0 Then
            If _IsLevelReady Then
                SetupLevel()
            Else
                If _ShowLevelTime > 0.0F Then
                    _ShowLevelTime -= e.Time.LastFrame
                Else
                    SpawnShips()
                    _IsLevelReady = True
                End If
            End If
        End If
    End Sub

    Protected Sub SpawnShips()
        Dim x, y As Single
        For i As Integer = 1 To 10 + (2 * (_Level - 1))
            x = _Scene.Engine.Canvas.DisplayRectangle.Width * _Random.NextDouble
            y = _Random.Next(-150, -51)

            Dim ship As New EnemySpaceship
            ship.Speed += (_Level * 5)
            ship.Position = New Vector2(x, y)
            _Scene.Objects.Add(ship)
            _Scene.Enemies.Add(ship)
        Next
    End Sub
End Class

ExampleScene

Imports GdiGaming

Public Class ExampleScene
    Inherits GameScene

    Private _LevelManager As LevelManager
    Public ReadOnly Property LevelManager As LevelManager
        Get
            Return _LevelManager
        End Get
    End Property

    Public Overrides ReadOnly Property Name As String
        Get
            Return "Example Scene 1"
        End Get
    End Property

    Protected _Enemies As New List(Of EnemySpaceship)
    Public ReadOnly Property Enemies As List(Of EnemySpaceship)
        Get
            Return _Enemies
        End Get
    End Property

    Protected _Player As PlayerChopper
    Public ReadOnly Property Player As PlayerChopper
        Get
            Return _Player
        End Get
    End Property

    Protected Overrides Sub OnLoad(ByVal e As GdiGaming.GameEngineEventArgs)
        Backgrounds.Add(New CloudyBackground)

        _Player = New PlayerChopper
        _Player.Position = New Vector2(320, 400)
        Objects.Add(_Player)

        Dim mainHud As New HudLayer
        HudLayers.Add(mainHud)

        Dim lifeLabel As New PlayerLifeLabel(_Player)
        lifeLabel.Position -= New Vector2(0, e.Engine.Canvas.Height / 2 * 0.9)
        mainHud.Labels.Add(lifeLabel)

        _LevelManager = New LevelManager(Me)
        Objects.Add(_LevelManager)

        MyBase.OnLoad(e)
    End Sub
End Class

That's All Folks! Run the Game and Blow Up Spaceships!

The example does not yet include any sounds, but you should be able to add sound effects easily by adding wav or mp3 (should work with any file Media Player can open) files to the assets folder and then using GameEngine.Audio to play them.

You might also try implementing a HUD and adding a score label. Enhance the player ship, enemy, and/or level manager to track a score as enemies are defeated. This example should provide a solid start for as many additions as you can imagine.

Last edited Mar 31, 2012 at 6:45 PM by ReedKimble, version 23

Comments

No comments yet.