Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Targets

0.00/5 (No votes)
15 Apr 2011 1  
A WPF Target Shooting Game
Screenshot_1.png

Introduction

I'm a big fan of first-person shooter games and Targets is my attempt at creating something in WPF with the feel of those genre of games. Targets is no Ghost Recon or Call of Duty but it does a bit of justice to the world of 2D WPF games. Its plot is simple. There is no mission proper where you have to kill multiple enemies, with an array of weapons at your disposal. In Targets, you simply have to shoot at multiple targets, which explains the origin of its name, and score a certain number of points, within a specific time period, to proceed to the next level. The jungle is your 'battlefield' and your 'weapon' is a reliable Glock 17.

There are only three levels, because I'm sure you have better things to do with your time, and going through those levels is easy or hard depending on how fast you can move your mouse and how fast you can click your mouse buttons, those targets pop up-and-down pretty quickly.

Requirements

To open the solution provided from the download link above, you require either of the following:

  • Visual Studio 2010
  • Expression Blend

NB: If you're using the Express Edition of Visual Studio, ensure that you open the solution using Visual Basic Express.

Targets

How it Works

When playing Targets, the two major actions are firing and reloading your weapon.

  • Firing: Press the left mouse button to fire your weapon. In Level 1, you'll have 10 rounds per magazine at your disposal, 17 rounds in Level 2, and 19 rounds in Level 3.
  • Reloading: To reload your weapon, you have to click the right mouse button twice. The first click is to 'eject' the spent clip while the second click is to 'load' a new magazine. A fast double click will do you some good. You can only reload once you have spent all the rounds in a magazine.

Your objective is to reach a certain number of points at each level in a time of 40secs:

  • Level 1: Get 450+ points in 40secs to proceed to the next level
  • Level 2: Get 550+ points in 40secs to proceed to the next level
  • Level 3: Get 600+ points in 40secs to be declared Targets' Champion

As you can see, the logic is quite simple though the simplicity will not be aided by the targets which, as I mentioned earlier, show up and disappear very quickly.

Design and Layout

I designed most of Targets' elements in Expression Design and added/designed some extra elements in Expression Blend. The following image shows some of the elements of interest:

Screenshot_2.png

JungleCanvas contains two Image controls. The first Image control, JungleImage, contains a full image of some jungle while the second Image control, OverlayImage, contains an image that is a cut-out of a section of the image in JungleImage. The following image shows the cut-out image on top of the original image:

Screenshot_3.png

The overlay image merely serves to create the illusion that some of the targets are appearing from behind some bushes and a fallen branch, more precisely from beneath the ground and behind some bushes and a fallen branch. The Targets are placed between the two Image controls.

Screenshot_4.png

The CrossHairs in the game is nothing more than a ViewBox containing several Path objects. Its IsHitTestVisible property is set to false.

Target

The targets have the challenging task of showering with virtual lead are Target UserControls. If you open up Target.xaml in Expression Blend, you won't see the elements that show-up in the game. This is because the ClipToBounds property of LayoutRoot is set to true.

Screenshot_5.png

Unchecking the checkbox reveals the visual elements:

Screenshot_6.png

The numbered regions are ViewBoxes, containing several Path objects, while the whitish area is just a Path object:

Screenshot_7.png

Target contains a Storyboard named TargetStoryboard that causes the apparent popping-up and down of the target in the game.

Storyboard.gif

Screenshot_11.png

You can adjust the Storyboard to your liking if you feel like it.

NB: The RepeatBehavior of TargetStoryboard is set to 1x.

Dent

The dents that show up on the targets when you fire at them are courtesy of the Dent UserControl. The dent you see on a target is made up of a single elliptical Path object with a radial gradient. The following is an enlarged view of the Dent UserControl.

Screenshot_8.png

GlockRound

The rounds of ammo that you see at the top of the window are courtesy of the GlockRound UserControl. The following image shows an enlarged view of the UserControl:

Screenshot_9.png

To progress through the game, you will interact with a few dialog boxes that allow you to move to the next level or restart a specific level. These dialog boxes are just Grids containing several elements. The following image shows Level_1_Grid and LevelFailedGrid.

Screenshot_10.png

The Code

Target

In the code for the Target UserControl, there are five event handlers for the MouseLeftButtonDown events of the four Viewboxes and the Path that make up the visual elements of the control.

Private Sub MainArea_MouseLeftButtonDown(ByVal sender As Object, _
                                  ByVal e As System.Windows.Input.MouseButtonEventArgs) _
                                  Handles MainArea.MouseLeftButtonDown
    HitTarget(e)
End Sub

Private Sub GreenZone_MouseLeftButtonDown(ByVal sender As Object, _
                                  ByVal e As System.Windows.Input.MouseButtonEventArgs) _
                                  Handles GreenZone.MouseLeftButtonDown
    HitTarget(e)
    Points = 7
End Sub

Private Sub BlueZone_MouseLeftButtonDown(ByVal sender As Object, _
                                  ByVal e As System.Windows.Input.MouseButtonEventArgs) _
                                  Handles BlueZone.MouseLeftButtonDown
    HitTarget(e)
    Points = 8
End Sub

Private Sub YellowZone_MouseLeftButtonDown(ByVal sender As Object, _
                                  ByVal e As System.Windows.Input.MouseButtonEventArgs) _
                                  Handles YellowZone.MouseLeftButtonDown
    HitTarget(e)
    Points = 9
End Sub

Private Sub RedZone_MouseLeftButtonDown(ByVal sender As Object, _
                                  ByVal e As System.Windows.Input.MouseButtonEventArgs) _
                                  Handles RedZone.MouseLeftButtonDown
    HitTarget(e)
    Points = 10
End Sub

The value of a variable, Points, is set in the event handlers of the Viewboxes and a method, HitTarget, is called in all.

Private Sub HitTarget(ByVal e As System.Windows.Input.MouseButtonEventArgs)
    Dim x As Double = e.GetPosition(TargetCanvas).X
    Dim y As Double = e.GetPosition(TargetCanvas).Y
    Dim dent As New Dent()

    Canvas.SetLeft(dent, x)
    Canvas.SetTop(dent, y)
    TargetCanvas.Children.Add(dent)
End Sub

In the HitTarget method, a Dent UserControl is added to the TargetCanvas at the location where the user clicks on the target.

The other method in Target that you should take note of is PlayStoryboard.

Public Sub PlayStoryboard()
    Dim targeter As Storyboard
    targeter = CType(Me.Resources("TargetStoryboard"), Storyboard)
    targeter.Begin(Me)
End Sub

MainWindow

In the MainWindow Loaded event handler, we do the following:

Private Sub MainWindow_Loaded(ByVal sender As Object, _
                              ByVal e As System.Windows.RoutedEventArgs) _
                              Handles Me.Loaded
    Gunshot.Stream = My.Resources.Gunshot
    DryFire.Stream = My.Resources.Dry_Fire
    EjectMag.Stream = My.Resources.Ejecting_Magazine
    PopInClip.Stream = My.Resources.Pop_Clip_In
    Gunshot.Load()
    DryFire.Load()
    EjectMag.Load()
    PopInClip.Load()
        
    JungleCanvas.IsEnabled = False

    AddHandler TargetsTimer.Tick, AddressOf TargetsTimer_Tick
    TargetsTimer.Interval = New TimeSpan(0, 0, 0, 0, 3000)

    AddHandler SecondsTimer.Tick, AddressOf SecondsTimer_Tick
    SecondsTimer.Interval = New TimeSpan(0, 0, 1)
End Sub

Here, we load the sound files for several SoundPlayer objects. The sound files are project resources which you can see in the project properties window by clicking on the Resources tab in Visual Studio.

Screenshot_12.png

The WAV files that provide the realistic sound effects are courtesy of www.soundbible.com.

Once you click the Start button to start Level 1, the following method is called:

Private Sub StartLevel1Btn_Click(ByVal sender As Object, _
                                 ByVal e As System.Windows.RoutedEventArgs) _
                                 Handles StartLevel1Btn.Click
    Level_1_Grid.Visibility = Windows.Visibility.Hidden        
    StartNewLevel()
End Sub

The StartNewLevel button does the following:

Private Sub StartNewLevel()
    TotalPoints = 0
    SevenPoints = 0
    EightPoints = 0
    NinePoints = 0
    TenPoints = 0

    SevensTxtBlock.Text = "0"
    EightsTxtBlock.Text = "0"
    NinesTxtBlock.Text = "0"
    TensTxtBlock.Text = "0"
    TotalPointsTxtBlck.Text = "0"
    SecTextBlck.Text = "40"

    JungleCanvas.IsEnabled = True
    RestartGameBtn.IsEnabled = True
    RestartLevelBtn.IsEnabled = True

    ' Remove any visible rounds, if any.
    If (RoundsStack.Children.Count > 0) Then
        RoundsStack.Children.Clear()
    End If

    ammo = MagCapacity
    ' Show rounds.
    Dim i As Integer = ammo
    Do While i > 0
        Dim round As New GlockRound()
        RoundsStack.Children.Add(round)
        i -= 1
    Loop

    TargetsTimer.Start()
    SecondsTimer.Start()
End Sub

Since we call the Start method of DispatcherTimer objects in the method above, their Tick event handlers are called at the intervals specified in the MainWindow Loaded event.

' TargetsTimer Tick event handler.
Private Sub TargetsTimer_Tick(ByVal sender As Object, ByVal e As EventArgs)
    ShowTarget()
End Sub

' SecondsTimer Tick event handler.
Private Sub SecondsTimer_Tick(ByVal sender As Object, ByVal e As EventArgs)
    If (seconds > -1) Then
        SecTextBlck.Text = seconds.ToString()
        seconds -= 1
    Else
        TargetsTimer.Stop()
        SecondsTimer.Stop()
        CheckPoints()
        seconds = 40
    End If
End Sub

The ShowTargets method that is called in TargetsTimer_Tick, randomly displays a different target at the specified interval.

Private Sub ShowTarget()
    Dim rN As Integer = RandomTarget.Next(1, 5)

    If (rN <> RandomNumber) Then
        RandomNumber = rN
        Select Case RandomNumber
            Case 1
                Target_1.PlayStoryboard()
            Case 2
                Target_2.PlayStoryboard()
            Case 3
                Target_3.PlayStoryboard()
            Case 4
                Target_4.PlayStoryboard()
        End Select
    Else
        ' Recall method to ensure a new target
        ' is shown.
        ShowTarget()
    End If
End Sub

The CheckPoints method that is called in SecondsTimer_Tick checks whether the user gained enough points to proceed to the next level once time is up.

' Check points gained when 40secs are over. 
Private Sub CheckPoints()
    ' Check points gained in Level 1.
    If (Level = 1) And (TotalPoints >= 450) Then
        Level_2_Grid.Visibility = Windows.Visibility.Visible
        DisableSomeControls()
    ElseIf (Level = 1) And (TotalPoints < 450) Then
        LevelFailedGrid.Visibility = Windows.Visibility.Visible
        DisableSomeControls()
    End If
    ' Check points gained in Level 2.
    If (Level = 2) And (TotalPoints >= 550) Then
        Level_3_Grid.Visibility = Windows.Visibility.Visible
        DisableSomeControls()
    ElseIf (Level = 2) And (TotalPoints < 550) Then
        LevelFailedGrid.Visibility = Windows.Visibility.Visible
        DisableSomeControls()
    End If
    ' Check points gained in Level 3.
    If (Level = 3) And (TotalPoints >= 600) Then
        FinalSevens += SevenPoints
        FinalEights += EightPoints
        FinalNines += NinePoints
        FinalTens += TenPoints
        ActualTotalPoints += TotalPoints
        ChampGrid.Visibility = Windows.Visibility.Visible
        TotalScoreTxtBlck.Text = ActualTotalPoints.ToString()
        TotalSevensTxtBlck.Text = FinalSevens.ToString()
        TotalEightsTxtBlck.Text = FinalEights.ToString()
        TotalNinesTxtBlck.Text = FinalNines.ToString()
        TotalTensTxtBlck.Text = FinalTens.ToString()
        DisableSomeControls()
    ElseIf (Level = 3) And (TotalPoints < 600) Then
        Level3FailedGrid.Visibility = Windows.Visibility.Visible
        DisableSomeControls()
    End If
End Sub

When you're frantically tapping on your left mouse button to get off a shot, the first method that is called is the JungleCanvas MouseLeftButtonDown event handler:

Private Sub JungleCanvas_MouseLeftButtonDown(ByVal sender As Object, _
                                             ByVal e As MouseButtonEventArgs) _
                                             Handles JungleCanvas.MouseLeftButtonDown
    If (ammo > 0) Then
        Gunshot.Play()
        Dim i As Integer = RoundsStack.Children.Count - 1
        RoundsStack.Children.RemoveAt(i)
        ammo -= 1
    Else
        ' Disable targets.
        Target_1.IsEnabled = False
        Target_2.IsEnabled = False
        Target_3.IsEnabled = False
        Target_4.IsEnabled = False
        DryFire.Play()
    End If
End Sub

When you frantically tap your right mouse button to pop out the spent clip and load a fresh magazine, the JungleCanvas MouseRightButtonDown event handler is called:

Private Sub JungleCanvas_MouseRightButtonDown(ByVal sender As Object, _
                                              ByVal e As MouseButtonEventArgs) _
                                              Handles JungleCanvas.MouseRightButtonDown
    If (ammo = 0) Then
        If IsMagEjected = False Then
            EjectMag.Play()
            IsMagEjected = True
        Else
            PopInClip.Play()
            IsMagEjected = False
            ammo = MagCapacity
            ' Show Ammo.
            Dim i As Integer = ammo
            Do While i > 0
                Dim round As New GlockRound()
                RoundsStack.Children.Add(round)
                i -= 1
            Loop
            Target_1.IsEnabled = True
            Target_2.IsEnabled = True
            Target_3.IsEnabled = True
            Target_4.IsEnabled = True
        End If
    Else
        Exit Sub
    End If

Hoping that you're not such a bad shot, and you manage to hit the targets, either one of the following event handlers is called:

Private Sub Target_1_MouseLeftButtonDown1(ByVal sender As Object, _
                                  ByVal e As System.Windows.Input.MouseButtonEventArgs) _
                                  Handles Target_1.MouseLeftButtonDown
    UpdatePoints(Target_1.Points)
End Sub

Private Sub Target_2_MouseLeftButtonDown(ByVal sender As Object, _
                                  ByVal e As System.Windows.Input.MouseButtonEventArgs) _
                                  Handles Target_2.MouseLeftButtonDown
    UpdatePoints(Target_2.Points)
End Sub

Private Sub Target_3_MouseLeftButtonDown(ByVal sender As Object, _
                                   ByVal e As System.Windows.Input.MouseButtonEventArgs) _
                                   Handles Target_3.MouseLeftButtonDown
    UpdatePoints(Target_3.Points)
End Sub

Private Sub Target_4_MouseLeftButtonDown(ByVal sender As Object, _
                                   ByVal e As System.Windows.Input.MouseButtonEventArgs) _
                                   Handles Target_4.MouseLeftButtonDown
    UpdatePoints(Target_4.Points)
End Sub

The UpdatePoints method does the following:

Private Sub UpdatePoints(ByVal points As Integer)
    If (ammo > 0) Then
        Select Case points
            Case 7
                SevenPoints += 1
                TotalPoints += 7
                SevensTxtBlock.Text = SevenPoints.ToString()
                TotalPointsTxtBlck.Text = TotalPoints.ToString()
            Case 8
                EightPoints += 1
                TotalPoints += 8
                EightsTxtBlock.Text = EightPoints.ToString()
                TotalPointsTxtBlck.Text = TotalPoints.ToString()
            Case 9
                NinePoints += 1
                TotalPoints += 9
                NinesTxtBlock.Text = NinePoints.ToString()
                TotalPointsTxtBlck.Text = TotalPoints.ToString()
            Case 10
                TenPoints += 1
                TotalPoints += 10
                TensTxtBlock.Text = TenPoints.ToString()
                TotalPointsTxtBlck.Text = TotalPoints.ToString()
        End Select
    End If
End Sub

If you managed to get enough points to proceed to Level 2, and you click on the button to start that level, then the following method is called:

Private Sub StartLevel2Btn_Click(ByVal sender As Object, _
                                 ByVal e As System.Windows.RoutedEventArgs) _
                                 Handles StartLevel2Btn.Click
    Level_2_Grid.Visibility = Windows.Visibility.Hidden
    MagCapacity = 17
    FinalSevens = SevenPoints
    FinalEights = EightPoints
    FinalNines = NinePoints
    FinalTens = TenPoints
    ActualTotalPoints = TotalPoints
    StartNewLevel()
    ClearTargetDents()
    LevelTxtBlock.Text = "2"
    Level = 2
End Sub

The ClearTargetDents method provides you with clean targets for your next challenge:

Private Sub ClearTargetDents()
    ' Clear/Hide dents from Target_1.
    For Each el As UIElement In Target_1.TargetCanvas.Children
        If TypeOf (el) Is Dent Then
            el.Visibility = Windows.Visibility.Collapsed
        End If
    Next
    ' Clear dents from Target_2.
    For Each el As UIElement In Target_2.TargetCanvas.Children
        If TypeOf (el) Is Dent Then
            el.Visibility = Windows.Visibility.Collapsed
        End If
    Next
    ' Clear dents from Target_3.
    For Each el As UIElement In Target_3.TargetCanvas.Children
        If TypeOf (el) Is Dent Then
            el.Visibility = Windows.Visibility.Collapsed
        End If
    Next
    ' Clear dents from Target_4.
    For Each el As UIElement In Target_4.TargetCanvas.Children
        If TypeOf (el) Is Dent Then
            el.Visibility = Windows.Visibility.Collapsed
        End If
    Next
End Sub

Conclusion

I hope you enjoyed reading the article and that you picked up something useful. Targets doesn't have a high score feature but if you're into that sort of thing, you can go ahead and add it. You could also try adding some extra levels, after all I've already done the groundwork. Cheers!

History

  • 13th April, 2011: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here