在 VB 中制作游戏和优化
Making Games in VB and optimisation
这更像是一个一般性问题,而不是与特定项目相关的问题。自从我开始在学校学习编程以来,我们一直在使用 VB 语言。我想发展自己的技能并做一些很酷的项目。最近在做一些小游戏,还进了Ludum Dare 32
然而,我一直 运行 遇到的一个大问题是,随着我的游戏变得越来越复杂并且包含更多元素(尤其是增加图片框的数量),它们变得非常缓慢。在我进入 Ludum Dare 时,几乎所有的评论都谈到游戏如何开始时很好,但随着游戏的进行变得越来越慢。
以下是我目前正在制作的吃豆人风格游戏的一些截图:
有 2 个 AI(橙色)和 1 个玩家(绿色)的游戏
同样的游戏有 3 个 AI(橙色)和 1 个玩家(绿色),大约多了 15 行代码
正如您在第二个屏幕截图中看到的那样,再添加一个 AI(15 行代码和一个图片框)会使游戏的 FPS 减半。我希望有人可以帮助解释 VB.
中优化游戏的更好方法
游戏详情:
- AI和玩家都是画框
- 我使用计时器通过 .left 和 .top 语句控制它们的移动
- 我用了 4 个定时器(一个只用于 FPS)
- 我在地形上使用了图片框来进行碰撞(使用 .bounds.intersectswith 语句,这些语句在计时器中不断循环)
如果有人需要更多信息,请直接询问。
PictureBox 是一个方便的控件。方便和速度是相反的目标,控制不会强制你做正确的事。您必须注意的事项,按照它们对渲染速度的影响顺序:
它会自动重新缩放图像以适合您要求的控件和 SizeMode。它使用高质量的重新缩放算法来产生尽可能好的图像,它会消耗很多 cpu 个周期。为了不使用太多内存,它每次绘制自己时都会这样做。您必须避免这种情况,只能使用 SizeMode = Normal 来避免这种成本。这反过来通常意味着您必须在分配图像 属性.
之前自己预先缩放图像
图片的像素格式非常非常重要。只有一种速度很快并且允许位图直接复制到视频适配器的帧缓冲区而无需转换。这是任何现代机器上的 PixelFormat.Format32bppPArgb。这几乎不是您使用绘画程序创建的位图的像素格式。在分配图像 属性 之前需要进行转换。这很重要,32bppPArgb 图像的渲染速度比其他所有图像快十倍。
它通过将背景颜色设置为 Color.Transparent 来支持透明度。通过首先让 Parent 将自己绘制到控件中 window,然后在其上绘制 Image 来实现。必须避免这种情况,它会使渲染时间增加一倍以上。设置 PictureBox.Size 以匹配图像大小。如果您需要图像的透明度,那么根本不要使用 PictureBox,而是在父级的 Paint 事件处理程序中调用 e.Graphics.DrawImage()。
PictureBox 不 便于资源管理,它不拥有您分配给 Image 属性 的位图的所有权。忘记调用图像的 Dispose() 方法是很常见的。在某些情况下,这可能会导致您的应用消耗大量内存并导致速度变慢。在玩游戏一段时间后,您通常会注意到 FPS 下降。您必须在 FormClosed 事件处理程序中或每当您重新分配图像时编写 Dispose() 调用 属性.
还有一些您无法从 PictureBox 中获得的额外加速。在该列表的顶部是将图像存储在视频适配器的内存中而不是机器的主 RAM 中。视频 RAM 的时钟比主 RAM 快得多,因此位图复制要快得多。这需要 DirectX,这是游戏编程中非常常见的选择。 SharpDX 和 SlimDX 库很受欢迎。
一些帮助您处理这些项目符号的代码:
Public Shared Sub GenerateFastImage(pbox As PictureBox, img As Image, ownsImage As Boolean)
'' Calculate effective size, like SizeMode = AutoSize
Dim zoom = Math.Min(CDbl(pbox.ClientSize.Width) / img.Width, _
CDbl(pbox.ClientSize.Height) / img.Height)
Dim size = New Size(CInt(zoom * img.Width), CInt(zoom * img.Height))
Dim pos = New Point((pbox.ClientSize.Width - size.Width) \ 2, _
(pbox.ClientSize.Height - size.Height) \ 2)
'' Generate 32bppPArgb image
Dim bmp = New Bitmap(pbox.ClientSize.Width, pbox.ClientSize.Height, _
System.Drawing.Imaging.PixelFormat.Format32bppPArgb)
Using gr = Graphics.FromImage(bmp)
gr.Clear(pbox.BackColor)
gr.DrawImage(img, New Rectangle(pos, size),
New Rectangle(0, 0, img.Width, img.Height), GraphicsUnit.Pixel)
End Using
'' Dispose images
If ownsImage Then
If pbox.Image IsNot Nothing Then pbox.Image.Dispose()
img.Dispose()
End If
pbox.Image = bmp
pbox.SizeMode = PictureBoxSizeMode.Normal
End Sub
如果显示在图片框中的图像未在程序的其他任何地方使用,则为 ownsImage 参数传递 True。此方法的示例用法:
Public Sub New()
InitializeComponent()
GenerateFastImage(PictureBox1, My.Resources.Player, True)
GenerateFastImage(PictureBox2, My.Resources.Monster, True)
End Sub
这个问题引起了我的兴趣,尽管我对VB知之甚少。我最初来自游戏背景(但在 90 年代初期,在模式 13h VGA 图形、Borland Turbo C、汇编、AdLib 合成器等时代),我想用更广泛的方式解决你问题的更一般的一面一般答案。
据我所知,您正在使用这些视觉形式元素来尝试为游戏精灵和图块建模。我猜你正在做的动画包括随着时间的推移改变 x/y
位置类型属性和交换图像和类似的东西,动态创建新控件等等。
我的建议是不要,这可能有点反VB的风险。对于游戏来说,这些高级控件和表单元素实际上会让您的生活变得更加艰难。在某些方面,这些高级的、事件驱动的系统比我们只是直接使用全屏应用程序 运行 循环操作视频内存的日子更难使用。与您的代码分散在您无法控制的事件中的那种异步、分散处理相比,这些类型的游戏更受益于同步、集中处理。
对于这些复古风格的街机游戏,您需要一个具有 "main loop" 的结构,类似于(简化):
while the game is running:
process input and game logic
draw frame (to offscreen buffer if you can afford it)
swap/copy your hidden buffer to front, visible one
如果你能在 VB 中找到一种方法来获得这种结构,并使用绘图库手动绘制你自己的绘图(如果可以的话加分)做精灵的快速 alpha blitting 之类的事情。
例如,也许主循环不适合大量事件驱动的系统,我怀疑 VB 适合这一类。但是,例如,您可能希望将所有这些高级计时器整合到一个计时器中以模拟游戏循环。多个计时器往往会使一切保持同步变得困难。
对于游戏来说,当涉及到事物的时间和绘图时,你会想成为一个控制狂,它会给你更多的优化空间(例如:使用像网格或四边形这样的加速结构树,除了快速碰撞检测之外,还可以用最少的检查量避免屏幕外对象的许多多余绘图调用。
除了空白 screen/window 和一组良好、快速的绘图例程外,您不需要渲染方面的任何东西。
例如,您不希望游戏中有两个敌人,一个在一帧中移动一个像素,另一个在下一帧中移动一个像素,如果他们都假设同时移动。这给人一种僵硬和奇怪的感觉,感觉不太对劲,如果您不能准确控制 drawing/refreshes 是如何逐帧完成的,则可能很难避免这种情况基础。
成为那个控制狂,通过逐帧控制掌握自己的绘图和输入处理,我实际上建议这会让你的生活更轻松,并有助于扩大你的视野,创造更多动态游戏。
最后,我建议您了解一下人们是如何为 Nintendo 和 Atari 等游戏机编写游戏的,以及人们是如何在今天延续这种趋势的。应该有一些关于这个主题的优秀资源,我建议从主游戏循环的想法开始,以及绘图、动画和碰撞通常是如何完成的。例如,他们不会用一堆图片框和计时器来做这件事,并不是因为他们当时没有那么奢侈,而是因为这些东西会成为障碍。
从好的方面来说,现在您不需要编写汇编代码来制作具有精美流畅的精灵动画和良好帧率的炫酷 2D 游戏。你可以用高级解释器编写它们,只要你的绘图库和可能的声音 library/mixer 很快就可以了。您只想确保您可以准确控制绘制的内容和时间。你想要那种主循环心态。
这更像是一个一般性问题,而不是与特定项目相关的问题。自从我开始在学校学习编程以来,我们一直在使用 VB 语言。我想发展自己的技能并做一些很酷的项目。最近在做一些小游戏,还进了Ludum Dare 32
然而,我一直 运行 遇到的一个大问题是,随着我的游戏变得越来越复杂并且包含更多元素(尤其是增加图片框的数量),它们变得非常缓慢。在我进入 Ludum Dare 时,几乎所有的评论都谈到游戏如何开始时很好,但随着游戏的进行变得越来越慢。
以下是我目前正在制作的吃豆人风格游戏的一些截图:
有 2 个 AI(橙色)和 1 个玩家(绿色)的游戏
同样的游戏有 3 个 AI(橙色)和 1 个玩家(绿色),大约多了 15 行代码
正如您在第二个屏幕截图中看到的那样,再添加一个 AI(15 行代码和一个图片框)会使游戏的 FPS 减半。我希望有人可以帮助解释 VB.
中优化游戏的更好方法游戏详情:
- AI和玩家都是画框
- 我使用计时器通过 .left 和 .top 语句控制它们的移动
- 我用了 4 个定时器(一个只用于 FPS)
- 我在地形上使用了图片框来进行碰撞(使用 .bounds.intersectswith 语句,这些语句在计时器中不断循环)
如果有人需要更多信息,请直接询问。
PictureBox 是一个方便的控件。方便和速度是相反的目标,控制不会强制你做正确的事。您必须注意的事项,按照它们对渲染速度的影响顺序:
它会自动重新缩放图像以适合您要求的控件和 SizeMode。它使用高质量的重新缩放算法来产生尽可能好的图像,它会消耗很多 cpu 个周期。为了不使用太多内存,它每次绘制自己时都会这样做。您必须避免这种情况,只能使用 SizeMode = Normal 来避免这种成本。这反过来通常意味着您必须在分配图像 属性.
之前自己预先缩放图像
图片的像素格式非常非常重要。只有一种速度很快并且允许位图直接复制到视频适配器的帧缓冲区而无需转换。这是任何现代机器上的 PixelFormat.Format32bppPArgb。这几乎不是您使用绘画程序创建的位图的像素格式。在分配图像 属性 之前需要进行转换。这很重要,32bppPArgb 图像的渲染速度比其他所有图像快十倍。
它通过将背景颜色设置为 Color.Transparent 来支持透明度。通过首先让 Parent 将自己绘制到控件中 window,然后在其上绘制 Image 来实现。必须避免这种情况,它会使渲染时间增加一倍以上。设置 PictureBox.Size 以匹配图像大小。如果您需要图像的透明度,那么根本不要使用 PictureBox,而是在父级的 Paint 事件处理程序中调用 e.Graphics.DrawImage()。
PictureBox 不 便于资源管理,它不拥有您分配给 Image 属性 的位图的所有权。忘记调用图像的 Dispose() 方法是很常见的。在某些情况下,这可能会导致您的应用消耗大量内存并导致速度变慢。在玩游戏一段时间后,您通常会注意到 FPS 下降。您必须在 FormClosed 事件处理程序中或每当您重新分配图像时编写 Dispose() 调用 属性.
还有一些您无法从 PictureBox 中获得的额外加速。在该列表的顶部是将图像存储在视频适配器的内存中而不是机器的主 RAM 中。视频 RAM 的时钟比主 RAM 快得多,因此位图复制要快得多。这需要 DirectX,这是游戏编程中非常常见的选择。 SharpDX 和 SlimDX 库很受欢迎。
一些帮助您处理这些项目符号的代码:
Public Shared Sub GenerateFastImage(pbox As PictureBox, img As Image, ownsImage As Boolean)
'' Calculate effective size, like SizeMode = AutoSize
Dim zoom = Math.Min(CDbl(pbox.ClientSize.Width) / img.Width, _
CDbl(pbox.ClientSize.Height) / img.Height)
Dim size = New Size(CInt(zoom * img.Width), CInt(zoom * img.Height))
Dim pos = New Point((pbox.ClientSize.Width - size.Width) \ 2, _
(pbox.ClientSize.Height - size.Height) \ 2)
'' Generate 32bppPArgb image
Dim bmp = New Bitmap(pbox.ClientSize.Width, pbox.ClientSize.Height, _
System.Drawing.Imaging.PixelFormat.Format32bppPArgb)
Using gr = Graphics.FromImage(bmp)
gr.Clear(pbox.BackColor)
gr.DrawImage(img, New Rectangle(pos, size),
New Rectangle(0, 0, img.Width, img.Height), GraphicsUnit.Pixel)
End Using
'' Dispose images
If ownsImage Then
If pbox.Image IsNot Nothing Then pbox.Image.Dispose()
img.Dispose()
End If
pbox.Image = bmp
pbox.SizeMode = PictureBoxSizeMode.Normal
End Sub
如果显示在图片框中的图像未在程序的其他任何地方使用,则为 ownsImage 参数传递 True。此方法的示例用法:
Public Sub New()
InitializeComponent()
GenerateFastImage(PictureBox1, My.Resources.Player, True)
GenerateFastImage(PictureBox2, My.Resources.Monster, True)
End Sub
这个问题引起了我的兴趣,尽管我对VB知之甚少。我最初来自游戏背景(但在 90 年代初期,在模式 13h VGA 图形、Borland Turbo C、汇编、AdLib 合成器等时代),我想用更广泛的方式解决你问题的更一般的一面一般答案。
据我所知,您正在使用这些视觉形式元素来尝试为游戏精灵和图块建模。我猜你正在做的动画包括随着时间的推移改变 x/y
位置类型属性和交换图像和类似的东西,动态创建新控件等等。
我的建议是不要,这可能有点反VB的风险。对于游戏来说,这些高级控件和表单元素实际上会让您的生活变得更加艰难。在某些方面,这些高级的、事件驱动的系统比我们只是直接使用全屏应用程序 运行 循环操作视频内存的日子更难使用。与您的代码分散在您无法控制的事件中的那种异步、分散处理相比,这些类型的游戏更受益于同步、集中处理。
对于这些复古风格的街机游戏,您需要一个具有 "main loop" 的结构,类似于(简化):
while the game is running:
process input and game logic
draw frame (to offscreen buffer if you can afford it)
swap/copy your hidden buffer to front, visible one
如果你能在 VB 中找到一种方法来获得这种结构,并使用绘图库手动绘制你自己的绘图(如果可以的话加分)做精灵的快速 alpha blitting 之类的事情。
例如,也许主循环不适合大量事件驱动的系统,我怀疑 VB 适合这一类。但是,例如,您可能希望将所有这些高级计时器整合到一个计时器中以模拟游戏循环。多个计时器往往会使一切保持同步变得困难。
对于游戏来说,当涉及到事物的时间和绘图时,你会想成为一个控制狂,它会给你更多的优化空间(例如:使用像网格或四边形这样的加速结构树,除了快速碰撞检测之外,还可以用最少的检查量避免屏幕外对象的许多多余绘图调用。
除了空白 screen/window 和一组良好、快速的绘图例程外,您不需要渲染方面的任何东西。
例如,您不希望游戏中有两个敌人,一个在一帧中移动一个像素,另一个在下一帧中移动一个像素,如果他们都假设同时移动。这给人一种僵硬和奇怪的感觉,感觉不太对劲,如果您不能准确控制 drawing/refreshes 是如何逐帧完成的,则可能很难避免这种情况基础。
成为那个控制狂,通过逐帧控制掌握自己的绘图和输入处理,我实际上建议这会让你的生活更轻松,并有助于扩大你的视野,创造更多动态游戏。
最后,我建议您了解一下人们是如何为 Nintendo 和 Atari 等游戏机编写游戏的,以及人们是如何在今天延续这种趋势的。应该有一些关于这个主题的优秀资源,我建议从主游戏循环的想法开始,以及绘图、动画和碰撞通常是如何完成的。例如,他们不会用一堆图片框和计时器来做这件事,并不是因为他们当时没有那么奢侈,而是因为这些东西会成为障碍。
从好的方面来说,现在您不需要编写汇编代码来制作具有精美流畅的精灵动画和良好帧率的炫酷 2D 游戏。你可以用高级解释器编写它们,只要你的绘图库和可能的声音 library/mixer 很快就可以了。您只想确保您可以准确控制绘制的内容和时间。你想要那种主循环心态。