65.9K
CodeProject 正在变化。 阅读更多。
Home

目标

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (32投票s)

2011年4月13日

CPOL

6分钟阅读

viewsIcon

70601

downloadIcon

2289

一个基于 WPF 的射击游戏

Screenshot_1.png

引言

我非常喜欢第一人称射击游戏,而Targets就是我尝试用WPF创作一款具有这类游戏感觉的作品。Targets算不上Ghost ReconCall of Duty,但它在2D WPF游戏领域也算尽了一份力。它的情节很简单。没有让你用各种武器杀死多个敌人的任务。在Targets中,你只需在限定时间内射击多个目标(这就是它名字的由来),获得一定分数,即可进入下一关。丛林是你的“战场”,你的“武器”是一把可靠的Glock 17。

只有三关,因为我确信你有更重要的事情要做,而通过这些关卡则取决于你移动鼠标的速度和点击鼠标按钮的速度,因为那些目标弹跳得非常快。

要求

要打开上面下载链接提供的解决方案,您需要以下任一条件:

  • Visual Studio 2010
  • Expression Blend

注意:如果您使用的是Visual Studio的Express版本,请确保您使用Visual Basic Express打开解决方案。

目标

工作原理

在玩Targets时,两个主要操作是射击和重新装弹。

  • 射击:按鼠标左键射击。在第1关,每弹匣有10发子弹,第2关有17发,第3关有19发。
  • 重新装弹:要重新装弹,您需要快速双击鼠标右键。第一次点击“弹出”用完的弹匣,第二次点击“装入”新弹匣。快速双击会有帮助。只有当弹匣中的子弹全部用完后才能重新装弹。

您的目标是在40秒内达到每关的特定分数

  • 第1关:在40秒内获得450+分以进入下一关
  • 第2关:在40秒内获得550+分以进入下一关
  • 第3关:在40秒内获得600+分,即可被宣布为Targets的冠军

正如您所见,逻辑很简单,但目标的出现和消失速度非常快,这使得这种简单性难以维持。

设计和布局

我设计了Targets的大部分元素在Expression Design中,并在Expression Blend中添加/设计了一些额外的元素。下图显示了一些重要的元素

Screenshot_2.png

JungleCanvas包含两个Image控件。第一个Image控件JungleImage包含一幅完整的丛林图像,第二个Image控件OverlayImage包含一幅从JungleImage图像中剪切出的部分图像。下图显示了叠加图像在原始图像上的效果

Screenshot_3.png

叠加图像只是为了制造一些目标从灌木丛和树枝后面(更准确地说,是从地面下方和灌木丛及树枝后面)出现的错觉。Targets放置在两个Image控件之间。

Screenshot_4.png

游戏中的CrossHairs只不过是一个包含多个Path对象的ViewBox。其IsHitTestVisible属性设置为false

目标

负责用虚拟铅弹“扫射”的目标是Target UserControl。如果您在Expression Blend中打开Target.xaml,您将看不到游戏中的元素。这是因为LayoutRootClipToBounds属性设置为true

Screenshot_5.png

取消选中checkbox会显示视觉元素

Screenshot_6.png

数字区域是ViewBox,包含多个Path对象,而白色区域只是一个Path对象

Screenshot_7.png

Target包含一个名为TargetStoryboardStoryboard,它会导致目标在游戏中出现和消失。

Storyboard.gif

Screenshot_11.png

如果您愿意,可以根据自己的喜好调整Storyboard

注意:TargetStoryboardRepeatBehavior设置为1x。

凹痕

当您射击目标时出现的凹痕是Dent UserControl的功劳。您在目标上看到的凹痕由一个具有径向渐变的椭圆形Path对象组成。下图是Dent UserControl的放大视图。

Screenshot_8.png

GlockRound

您在窗口顶部看到的弹药是GlockRound UserControl的功劳。下图显示了该UserControl的放大视图

Screenshot_9.png

要通过游戏,您将与几个对话框进行交互,这些对话框允许您进入下一关或重新开始特定关卡。这些对话框只是包含多个元素的Grid。下图显示了Level_1_GridLevelFailedGrid

Screenshot_10.png

代码

目标

Target UserControl的代码中,有五个事件处理程序用于构成该控件视觉元素的四个ViewboxPathMouseLeftButtonDown事件。

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

变量Points的值在Viewbox的事件处理程序中设置,并且所有事件处理程序都调用了HitTarget方法。

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

HitTarget方法中,在用户点击目标的位置将一个Dent UserControl添加到TargetCanvas

Target中您应该注意的另一个方法是PlayStoryboard

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

MainWindow

MainWindowLoaded事件处理程序中,我们执行以下操作

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

在这里,我们为几个SoundPlayer对象加载声音文件。声音文件是项目资源,您可以在Visual Studio中点击“Resources”选项卡查看项目属性窗口。

Screenshot_12.png

提供逼真音效的WAV文件来自www.soundbible.com

一旦您点击“开始”按钮开始第1关,就会调用以下方法

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

StartNewLevel按钮执行以下操作

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

由于我们在上面的方法中调用了DispatcherTimer对象的Start方法,因此它们的Tick事件处理程序会在MainWindowLoaded事件中指定的间隔被调用。

' 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

TargetsTimer_Tick中调用的ShowTargets方法会以指定的时间间隔随机显示不同的目标。

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

SecondsTimer_Tick中调用的CheckPoints方法会在时间结束后检查用户是否获得了足够的积分以进入下一关。

' 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

当您疯狂地点击鼠标左键进行射击时,第一个被调用的方法是JungleCanvasMouseLeftButtonDown事件处理程序

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

当您疯狂地点击鼠标右键以弹出用完的弹匣并装入新弹匣时,会调用JungleCanvasMouseRightButtonDown事件处理程序

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

希望您射击技术不错,并且成功击中了目标,此时会调用以下任一事件处理程序

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

UpdatePoints方法执行以下操作

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

如果您获得了足够的积分进入第2关,并且点击了开始该关卡的按钮,那么就会调用以下方法

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

ClearTargetDents方法为您提供干净的目标,以应对下一个挑战

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

结论

我希望您喜欢阅读这篇文章,并从中获得了一些有用的东西。Targets没有最高分功能,但如果您喜欢这类东西,可以自己添加。您也可以尝试添加一些额外的关卡,毕竟我已经打好了基础。祝您愉快!

历史

  • 2011年4月13日:初始发布
© . All rights reserved.