TexasQuest v.2.0





5.00/5 (8投票s)
TexasQuest 是一款 2D 横版卷轴平台游戏,类似于超级马里奥。
- *必需* 下载 TexasQuest 库 - 1235 KB
- *必需* 下载 TexasQuest 图片/声音 - 554 KB
- 下载 TexasQuest 源代码 - 659 KB
- 下载 TexasQuest mp3 文件 - 41098 KB
目录
引言
关于
TexasQuest 是一款 2D 横版卷轴平台游戏,类似于《超级马里奥兄弟》。游戏的目标是让角色“Aaron”到达终点,在那里他将从一个工作过度压榨员工的可怕老板手中救出他的女朋友。一路上,会有敌人试图阻止 Aaron 到达目的地。然而,Aaron 有一些工具可以帮助他(例如,苏打水、冰棒等),伴随他的旅程。你能帮助 Aaron 在他被老板的手下抓走之前,在他度过一个富有成效的工作日之前,帮助他到达他的女朋友身边并拯救她吗?
背景
历史
这实际上是对之前使用 Microsoft DirectX 技术的 TexasQuest 版本的更新。在此版本中,我已切换到使用名为 SlimDX 的开源项目。有关该项目的更多信息可在 http://slimdx.org [^] 找到。
其次,为了彻底摆脱使用专有的 DirectX 技术,在此版本中我正在尝试使用 NAudio 开源项目。有关该项目的更多信息可在 [^] 找到。
致谢
让我先说明一点,不,这不是原创代码。原始 C# 代码可在 [这里] 找到。我只是需要将其移植到 VB.NET,以便我能更好地研究它。原始作者 Aaron Dail 是为他的女朋友写的。阅读更多内容并下载 [这里],并查看更多 [屏幕截图]。Aaron 慷慨地提供了游戏的完整源代码,所以如果你想要一些 2D 滚动示例代码(C#),这里是一个不错的选择。
目的
我在此应用程序中的目的是学习 VB.NET 编程。我无法向你解释有多少次我在网上搜索有关 VB 开发人员如何实现和创建优秀游戏的可靠示例。视频游戏不是我从事编程的主要原因,但我发现很多人都想获得娱乐。所以,我说,如果人们想要蛋糕,就给他们蛋糕。
成果
这里学习到的通用原理可以应用于多种类型的游戏,无论是 2D 策略游戏、2D 俯视冒险游戏、2D 益智游戏,还是更多的 2D 策略游戏。我不确定它对 3D 游戏有何用处,除了在图形方面完全没用。
目标
我们将介绍如何使用名为 SlimDX [^] 的开源项目来启动并运行游戏的渲染部分。其次,我们将讨论图像以及如何绘制它们。之后,您应该就有足够的知识来实际构建一个游戏。然后,我们将介绍关卡架构以及如何绘制关卡(通过关卡架构,我的意思是定义关卡的结构,而不是单个关卡的具体内容)。接下来可能是显示关卡中的角色和对象的介绍。
然后,我们应该讨论角色当前所进行的活动以及他们正在执行的动作(如果有)。接下来,我们将进行“碰撞检测”,以及最难处理的主题之一:事件处理。也就是说,当 A 跳到 B 上时,会发生 X,但当 B 跳到 A 上时,会发生 Y。你必须处理所有这些事情。这很难做到正确。这会直接导致响应事件而改变角色的目标或活动,或者我称之为思考。实际上,用户输入也会在那里涵盖。是的,这就是对游戏的一个相当完整的概述。
要求
- SlimDX.dll
- NAudio.dll
- NAudio.WindowsMediaFormat.dll
安装
您的“Build”目录应该看起来像这样。
代码
SlimDX 渲染到某种目标,在 .NET 中,目标可以是 Form
或 Control
(技术上来说,因为 Form
是一个 Control
,所以它总是渲染到 Control
)。所以,首先,你必须有一个窗口,然后,你可以选择性地在窗口上有一个 Control
来进行渲染(这允许你在渲染周围放置其他控件,如按钮或列表框)。很简单,对吧?好吧,让我们编写一个执行此操作的 Main
函数。所以,启动你的 IDE,然后创建一个 VB.NET Windows 应用程序项目。
Public Shared Sub Main(ByVal args As String())
Dim mw As MainWindow = New MainWindow ()
mw.Show()
Do While mw.Created
Application.DoEvents()
Loop
End Sub
这部分代码放在你的 MainWindow
类中。我们正在做什么应该很清楚。因为此时我们并没有做太多,或者任何与 DirectX 相关的事情。我们所做的只是创建一个窗口,显示它,然后在它被创建的同时,反复处理它的事件(当你点击关闭按钮时,Form
的 Created
属性会变成 False
)。
现在,编译、构建并运行。应该能完美运行。
控制器类
现在,我们准备进入重点内容了。我喜欢将渲染与设置渲染所需的内部逻辑分开,并将它们与游戏引擎以及启动和停止应用程序的代码尽可能地分开。这会导致更多的类,起初可能会令人困惑,但一旦你弄明白了,它就会很好地运作。
基本上,我喜欢的一种结构是有一个 Controller
类,它处理所有与准备好渲染相关的事情,但不进行任何渲染。渲染实际上是通过一个 Renderer
类完成的,Controller
对象拥有一个该类的实例。原因是你可以轻松地替换和插入不同的 Renderer
对象来处理不同的任务。在我的游戏中,我有一个用于游戏主要部分的 Renderer
,用于显示地图等,然后有几个用于菜单。这其实并不难,而且能使你的代码更整洁。
现在,我所说的关于引擎与渲染分离这一点也很重要。所有 Renderer
所做的只是显示游戏的当前状态。它从不影响游戏状态。引擎会这样做。虽然它们通常同步运行,但这并不是必需的。也就是说,你可以暂停引擎而仍然进行渲染,或者你可以进行 1000 轮游戏而不渲染其中任何一轮。这是基本但重要的。所以,让我们继续创建我刚才提到的 Controller
类。向你的项目添加一个名为 Controller
的新类。添加以下 Import 语句
Imports System.Windows.Forms
Imports Microsoft.DirectX
Imports Microsoft.DirectX.Direct3D
现在,向 Controller
类添加以下私有变量
Private m_Target As Control
Private m_Device As Device
Private m_Renderer As Renderer
Private m_presentParameters As PresentParameters
Private DeviceLost As Boolean
m_Target
变量是我们之前谈到的目标,所有渲染都去那里。m_Device
是执行渲染等操作的 DirectX 设备接口。m_presentParameters
是我们用来创建设备的一些信息。我们保留这些信息是因为设备有时可能会丢失,而我们可能需要在不重启应用程序的情况下重新创建它。这与 IsDeviceLost
相关,它是在设备丢失时设置的一个标志。m_Renderer
变量是我们之前谈到的 Renderer
。
现在,将你的构造函数更改为如下所示
Public Sub New(ByVal target As Control, ByVal renderer As Renderer)
Me.m_Target = target
Me.m_Renderer = renderer
IsDeviceLost = False
m_presentParameters = New PresentParameters ()
InitializeGraphics()
End Sub
很简单,只是设置私有成员,然后调用 IntializeGraphics
,它应该如下所示
Protected Sub InitializeGraphics()
m_presentParameters.Windowed = True
' If you want to use Full Screen mode (this assumes the target is a Form not just
' a Control), delete the previous line and add:
' m_presentParameters.Windowed = false
m_presentParameters.SwapEffect = SwapEffect.Discard
m_presentParameters.AutoDepthStencilFormat = DepthFormat.D16
m_presentParameters.EnableAutoDepthStencil = True
' store our default adapter
Dim adapterOrdinal As Integer = Manager.Adapters.Default.Adapter
' get our device capabilities so we can check stuff
Dim caps As Caps = Manager.GetDeviceCaps(adapterOrdinal,DeviceType.Hardware)
Dim createFlags As CreateFlags
If caps.DeviceCaps.SupportsHardwareTransformAndLight Then
createFlags = CreateFlags.HardwareVertexProcessing
Else
createFlags = CreateFlags.SoftwareVertexProcessing
End If
If caps.DeviceCaps.SupportsPureDevice Then
createFlags = createFlags Or CreateFlags.PureDevice
End If
' create our device
m_device = New Device(adapterOrdinal, DeviceType.Hardware, target, createFlags, _
presentParameters)
' Hook the DeviceReset event so OnDeviceReset will get called every
' time we call m_device.Reset()
AddHandler m_device.DeviceReset, AddressOf OnDeviceReset
SetupDevice()
End Sub
非常复杂。而且,我也不知道它到底在做什么。就像我说的,阅读其他教程,或者买本书。基本上,它的大部分工作是准备好创建设备,然后创建它。告诉 SlimDX 关于如何创建它的很多信息。它还在最后做了两件事,添加一个 DeviceReset
的处理程序并调用 SetupDevice
。设备重置处理程序也调用 setup device,因为那应该处理设备何时丢失以及何时可能恢复。
SetupDevice
负责相机、照明和视图设置
Protected Sub SetupDevice()
m_device.RenderState.AlphaBlendEnable = True
m_device.SetSamplerState(0, SamplerStageStates.MinFilter, _
CInt((TextureFilter.Linear)))
m_device.SetSamplerState(0, SamplerStageStates.MagFilter, _
CInt((TextureFilter.Linear)))
m_device.SetSamplerState(0, SamplerStageStates.MipFilter, _
CInt((TextureFilter.Linear)))
m_device.RenderState.Lighting = False
' get camera vectors
Dim width As Single = CSng(target.Size.Width)
Dim height As Single = CSng(target.Size.Height)
Dim centerX As Single = width / 2.0f
Dim centerY As Single = height / 2.0f
Dim cameraPosition As Vector3 = New Vector3(centerX, centerY, -5.0f)
Dim cameraTarget As Vector3 = New Vector3(centerX, centerY, 0.0f)
' create our transforms
m_device.Transform.View = Matrix.LookAtLH(cameraPosition, cameraTarget, _
New Vector3(0.0f, 1.0f, 0.0f))
m_device.Transform.Projection = Matrix.OrthoLH(width, height, 1.0f, 10.0f)
End Sub
Protected Sub OnDeviceReset(ByVal sender As Object, ByVal e As EventArgs)
' We use the same setup code to reset as we do for initial creation
SetupDevice()
End Sub
这基本上就是让设备(相机、视图、照明)准备好正常绘制 2D 内容。现在,让我们添加非常重要的 Render
方法
Public Sub Render()
If DeviceLost Then
' Try to get the device back
AttemptRecovery()
End If
' If we couldn't get the device back, don't try to render
If DeviceLost Then
Return
End If
m_Device.Clear(ClearFlags.Target Or ClearFlags.ZBuffer, _
System.Drawing.Color.Blue, 1.0f, 0)
m_Device.BeginScene()
m_Renderer.Render(Me)
m_Device.EndScene()
Try
' Copy the back buffer to the display
m_Device.Present()
Catch e1 As DeviceLostException
' Indicate that the device has been lost
m_DeviceLost = True
' Spew a message into the output window of the debugger
System.Diagnostics.Debug.WriteLine("Device was lost")
End Try
End Sub
首先,我们检查设备是否丢失,如果丢失,我们尝试恢复它。如果之后设备仍然丢失,那么我们就返回此方法而不做任何操作。请注意,当屏幕保护程序出现或其他类似情况时,设备可能会丢失。如果一切正常,那么我们清除显示,然后调用 BeginScene
。所有渲染都必须发生在调用 BeginScene
和 EndScene
之间。SlimDX 就是这样工作的。在这两者之间,我们调用 Renderer
的 Render
方法,它应该完成所有绘图。然后,我们尝试呈现我们所做的工作,这时我们可能会注意到设备已丢失,因此在这种情况下,我们将 deviceLost
标志设置为 True
(因此,下次调用 Render
时,我们将尝试恢复设备)。
现在,AttemptRecovery
Protected Sub AttemptRecovery()
Try
device.TestCooperativeLevel()
Catch e1 As DeviceLostException
Catch e2 As DeviceNotResetException
Try
device.Reset(presentParameters)
deviceLost = False
' Spew a message into the output window of the debugger
System.Diagnostics.Debug.WriteLine("Device successfully reset")
Catch e3 As DeviceLostException
' If it's still lost or lost again, just do
' nothing
End Try
End Sub
我们姑且假设它能正常工作。最后,我们有一个简单的属性来获取和设置 Renderer
Public Property Renderer() As Renderer
Get
Return m_Renderer
End Get
Set(ByVal value As Renderer)
m_Renderer = value
End Set
End Property
好了,Controller
类到此为止。你还不能构建或运行它,因为我们还没有创建 Renderer
,所以我们接下来就做这件事。
渲染器类
在你的项目中创建一个名为 Renderer 的新类。添加 Imports
指令
Imports SlimDX
Imports SlimDX.Direct3D
然后,添加 Render
方法
Public Class Renderer
Public Sub New()
End Sub
Public Overridable Sub Render(ByVal controller As Controller)
End Sub
End Class
就是这样。目前 Render
类将非常简单,因为它目前什么也不做。实际上,Render
类本身将永远不做任何事情,但它的子类会。事实上,我们将在下一个版本中将其设为抽象类,因为没有人需要使用它本身。但目前,如果它是抽象的,我们就无法测试我们刚起步的程序,所以最好将其设为具体。
总结
我们要做的最后一部分是稍微重写一下 Main
函数
Public Shared Sub Main(ByVal args As String())
Dim mw As MainWindow = New MainWindow ()
Dim r As Renderer = New Renderer ()
Dim c As Controller = New Controller (mw, r)
mw.Show()
Do While mw.Created
c.Render()
Application.DoEvents()
Loop
End Sub
构建并运行它。你应该会看到一个深蓝色的窗口,这将是你引以为豪的“孩子”。恭喜。