Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / WPF

Targets

Rate me:
Please Sign up or sign in to vote.
4.94/5 (32 votes)
15 Apr 2011CPOL6 min read 64.7K   2.3K   41   33
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.

VB.NET
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.

VB.NET
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.

VB.NET
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:

VB.NET
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:

VB.NET
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:

VB.NET
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.

VB.NET
' 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.

VB.NET
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.

VB.NET
' 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:

VB.NET
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:

VB.NET
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:

VB.NET
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:

VB.NET
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:

VB.NET
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:

VB.NET
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Kenya Kenya
Experienced C# software developer with a passion for WPF.

Awards,
  • CodeProject MVP 2013
  • CodeProject MVP 2012
  • CodeProject MVP 2021

Comments and Discussions

 
BugCannot open project file Pin
Keith O. Williams9-Jul-14 15:01
professionalKeith O. Williams9-Jul-14 15:01 
GeneralRe: Cannot open project file Pin
Meshack Musundi10-Jul-14 4:01
professionalMeshack Musundi10-Jul-14 4:01 
GeneralRe: Cannot open project file Pin
Keith O. Williams10-Jul-14 4:29
professionalKeith O. Williams10-Jul-14 4:29 
GeneralRe: Cannot open project file Pin
Meshack Musundi11-Jul-14 2:43
professionalMeshack Musundi11-Jul-14 2:43 
GeneralRe: Cannot open project file Pin
Keith O. Williams11-Jul-14 4:28
professionalKeith O. Williams11-Jul-14 4:28 
GeneralMy vote of 5 Pin
Wendelius11-Jun-12 10:04
mentorWendelius11-Jun-12 10:04 
GeneralYou hit again Pin
Minhajul Shaoun20-May-11 11:50
Minhajul Shaoun20-May-11 11:50 
GeneralRe: You hit again Pin
Meshack Musundi20-May-11 18:44
professionalMeshack Musundi20-May-11 18:44 
GeneralMy vote of 5 Pin
Minhajul Shaoun20-May-11 11:45
Minhajul Shaoun20-May-11 11:45 
You hit again!
GeneralMy vote of 5 Pin
Rhuros12-May-11 0:58
professionalRhuros12-May-11 0:58 
GeneralRe: My vote of 5 Pin
Meshack Musundi12-May-11 20:34
professionalMeshack Musundi12-May-11 20:34 
GeneralMy vote of 5 Pin
Halil ibrahim Kalkan11-May-11 20:30
Halil ibrahim Kalkan11-May-11 20:30 
GeneralRe: My vote of 5 Pin
Meshack Musundi11-May-11 21:58
professionalMeshack Musundi11-May-11 21:58 
GeneralMy vote of 5 Pin
TweakBird10-May-11 18:31
TweakBird10-May-11 18:31 
GeneralRe: My vote of 5 Pin
Meshack Musundi10-May-11 20:29
professionalMeshack Musundi10-May-11 20:29 
GeneralLook, that's Predator behind the trees... Pin
Marcelo Ricardo de Oliveira25-Apr-11 12:49
mvaMarcelo Ricardo de Oliveira25-Apr-11 12:49 
GeneralRe: Look, that's Predator behind the trees... Pin
Meshack Musundi25-Apr-11 20:29
professionalMeshack Musundi25-Apr-11 20:29 
GeneralHave 5 Pin
rspercy6520-Apr-11 0:48
rspercy6520-Apr-11 0:48 
GeneralRe: Have 5 Pin
Meshack Musundi20-Apr-11 3:19
professionalMeshack Musundi20-Apr-11 3:19 
NewsTargets v1.1 Pin
Meshack Musundi15-Apr-11 20:20
professionalMeshack Musundi15-Apr-11 20:20 
GeneralFun time playing targets (My Vote of 4) Pin
Jyothikarthik_N14-Apr-11 23:59
Jyothikarthik_N14-Apr-11 23:59 
GeneralRe: Fun time playing targets (My Vote of 4) Pin
Jyothikarthik_N15-Apr-11 0:04
Jyothikarthik_N15-Apr-11 0:04 
GeneralRe: Fun time playing targets (My Vote of 4) Pin
Meshack Musundi15-Apr-11 2:51
professionalMeshack Musundi15-Apr-11 2:51 
GeneralRe: Fun time playing targets (My Vote of 4) Pin
Meshack Musundi15-Apr-11 2:48
professionalMeshack Musundi15-Apr-11 2:48 
GeneralI like the way you did images with Z-Indexing like masks cool. Pin
Sacha Barber14-Apr-11 9:47
Sacha Barber14-Apr-11 9:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.