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

TexasQuest v.2.0

starIconstarIconstarIconstarIconstarIcon

5.00/5 (8投票s)

2013 年 3 月 22 日

CPOL

8分钟阅读

viewsIcon

33989

downloadIcon

1904

TexasQuest 是一款 2D 横版卷轴平台游戏,类似于超级马里奥。

566268/screenshot.jpg

目录

引言

关于

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”目录应该看起来像这样。
566268/install.jpg

代码

SlimDX 渲染到某种目标,在 .NET 中,目标可以是 FormControl(技术上来说,因为 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 相关的事情。我们所做的只是创建一个窗口,显示它,然后在它被创建的同时,反复处理它的事件(当你点击关闭按钮时,FormCreated 属性会变成 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。所有渲染都必须发生在调用 BeginSceneEndScene 之间。SlimDX 就是这样工作的。在这两者之间,我们调用 RendererRender 方法,它应该完成所有绘图。然后,我们尝试呈现我们所做的工作,这时我们可能会注意到设备已丢失,因此在这种情况下,我们将 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

构建并运行它。你应该会看到一个深蓝色的窗口,这将是你引以为豪的“孩子”。恭喜。

© . All rights reserved.