目标






4.94/5 (32投票s)
一个基于 WPF 的射击游戏

引言
我非常喜欢第一人称射击游戏,而Targets就是我尝试用WPF创作一款具有这类游戏感觉的作品。Targets算不上Ghost Recon或Call 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中添加/设计了一些额外的元素。下图显示了一些重要的元素
JungleCanvas
包含两个Image
控件。第一个Image
控件JungleImage
包含一幅完整的丛林图像,第二个Image
控件OverlayImage
包含一幅从JungleImage
图像中剪切出的部分图像。下图显示了叠加图像在原始图像上的效果
叠加图像只是为了制造一些目标从灌木丛和树枝后面(更准确地说,是从地面下方和灌木丛及树枝后面)出现的错觉。Target
s放置在两个Image
控件之间。
游戏中的CrossHairs
只不过是一个包含多个Path
对象的ViewBox
。其IsHitTestVisible
属性设置为false
。
目标
负责用虚拟铅弹“扫射”的目标是Target
UserControl
。如果您在Expression Blend中打开Target.xaml,您将看不到游戏中的元素。这是因为LayoutRoot
的ClipToBounds
属性设置为true
。
取消选中checkbox
会显示视觉元素
数字区域是ViewBox
,包含多个Path
对象,而白色区域只是一个Path
对象
Target
包含一个名为TargetStoryboard
的Storyboard
,它会导致目标在游戏中出现和消失。
如果您愿意,可以根据自己的喜好调整Storyboard
。
注意:TargetStoryboard
的RepeatBehavior
设置为1x。
凹痕
当您射击目标时出现的凹痕是Dent
UserControl
的功劳。您在目标上看到的凹痕由一个具有径向渐变的椭圆形Path
对象组成。下图是Dent
UserControl
的放大视图。
GlockRound
您在窗口顶部看到的弹药是GlockRound
UserControl
的功劳。下图显示了该UserControl
的放大视图
要通过游戏,您将与几个对话框进行交互,这些对话框允许您进入下一关或重新开始特定关卡。这些对话框只是包含多个元素的Grid
。下图显示了Level_1_Grid
和LevelFailedGrid
。
代码
目标
在Target UserControl
的代码中,有五个事件处理程序用于构成该控件视觉元素的四个Viewbox
和Path
的MouseLeftButtonDown
事件。
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
在MainWindow
的Loaded
事件处理程序中,我们执行以下操作
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”选项卡查看项目属性窗口。
提供逼真音效的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
事件处理程序会在MainWindow
的Loaded
事件中指定的间隔被调用。
' 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
当您疯狂地点击鼠标左键进行射击时,第一个被调用的方法是JungleCanvas
的MouseLeftButtonDown
事件处理程序
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
当您疯狂地点击鼠标右键以弹出用完的弹匣并装入新弹匣时,会调用JungleCanvas
的MouseRightButtonDown
事件处理程序
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日:初始发布