这是一个普遍的问题,而不是与特定项目相关的问题。自从我开始在学校学习编程以来,我们一直在使用VB语言。我想发展我的技能并做一些很酷的项目。最近我一直在制作一些小游戏,甚至进入了Ludum Dare 32。
然而,我一直遇到的一个大问题是,随着我的游戏变得越来越复杂并且有更多元素(特别是增加图片框的数量),它们变得非常缓慢。在我进入Ludum Dare的过程中,几乎所有的评论都谈到了游戏开始时的好坏,但随着时间的推移变得越来越慢。
以下是我目前制作的pacman风格游戏的截图:
2人工智能(橙色)和1人(绿色)游戏
与3 AI(橙色)和1个玩家(绿色)相同的游戏,大约15行代码
正如您在第二个屏幕截图中看到的,再添加一个AI(15行代码和一个图片框)将游戏的FPS减半。我希望有人可以帮助解释在VB中优化游戏的更好方法。
游戏详情:
如果有人需要进一步的信息,请问。
答案 0 :(得分:5)
PictureBox是一个便利控件。方便和速度是相反的目标,控制没有任何东西强迫你做正确的事情。你需要注意的事项,它们对渲染速度的影响顺序:
它会自动重新调整图像以适应您要求的控件和SizeMode。它使用高质量的重新缩放算法,可以产生最佳的图像,它会消耗大量的cpu周期。并且为了不使用太多的内存,它每次都会自行绘制。你必须避免这种情况,只使用SizeMode = Normal来避免这个成本。反过来通常意味着您必须在分配Image属性之前自己预先缩放图像。
Image的像素格式非常非常重要。只有一个是快速的,允许位图直接复制到视频适配器的帧缓冲区而无需转换。这是任何现代机器上的PixelFormat.Format32bppPArgb。这几乎不是您使用绘图程序创建的位图的像素格式。在分配Image属性之前,需要进行转换。重要的是,32bppPArgb图像的渲染速度比其他图像快十倍。
它通过将BackColor设置为Color.Transparent来支持透明度。通过首先让Parent绘制自己进入控制窗口,然后在它上面绘制Image来实现。必须避免这种情况,它使渲染时间增加一倍以上。设置PictureBox.Size以匹配图像大小。如果你需要图像中的透明度,那么根本不要使用PictureBox,而是在父的Paint事件处理程序中调用e.Graphics.DrawImage()。
PictureBox 不方便资源管理,它不会取得您分配给Image属性的位图的所有权。忘记调用图像的Dispose()方法是很常见的。在某些情况下,可能会导致您的应用程序消耗大量内存并导致速度下降。在玩游戏一段时间之后,你通常会注意到你的FPS掉线了。您必须在FormClosed事件处理程序中或在重新分配Image属性时编写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
答案 1 :(得分:2)
这个问题引起了我的兴趣,尽管我对VB知之甚少。我最初来自游戏背景(但在模式13h VGA图形,Borland Turbo C,装配,AdLib合成器等的90年代早期回来),我想用更多的问题解决你的问题的更一般的一面一般答案。
据我所知,您正在使用这些视觉形式元素来尝试模拟游戏精灵和瓷砖。我猜你正在做的动画包括随着时间的推移改变x/y
位置类型的属性,交换图像和类似的东西,动态创建新的控件等等。
我的建议,可能存在一些反VBish的风险,不。对于游戏而言,这些高级控件和表单元素实际上可以让您的生活更加艰难。在某些方面,这些高级别,事件驱动的系统在我们仅使用运行循环的全屏应用程序直接操作视频内存的日子里比使用更难以使用。与异步,分散处理相比,这些类型的游戏从同步,集中处理中获益更多,其中代码分散在您控制之外的事件中。
对于这些复古风格的街机游戏,你需要一个具有“主循环”的结构,类似这样(简化):
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适合这一类。但是,例如,您可能希望将所有这些高级计时器合并到一个计时器中以模拟游戏循环。多个计时器往往会使一切都保持同步变得困难。
对于游戏而言,你想要在游戏的时间和绘制方面成为一个控制狂,它会为你提供更多的优化空间(例如:使用加速结构,如网格或四叉树到除了快速碰撞检测之外,还要使用最少量的检查来避免对屏幕外对象进行多次多余的绘图调用。)
渲染方面除了空白屏幕/窗口和一组良好,快速的绘图程序之外,您不需要任何其他内容。
例如,您不希望在游戏中有两个敌人的情况下,一个在一个帧中移动一个像素而另一个在下一帧中移动一个像素,如果它们都应该是同时移动。这给人一种僵硬和奇怪的感觉,感觉不太正确,如果你无法控制绘图/刷新是如何在逐帧的基础上完成的,那么可能很难避免。
成为那个控制狂,通过逐帧控制掌握自己的绘图和输入处理,我实际上建议它会让你的生活变得更轻松,并帮助扩展你的视野,制作更具活力的游戏
最后,我建议查看人们如何为任天堂和雅达利这样的游戏机编写游戏,以及今天人们如何继续这种趋势。关于这个主题应该有一些很好的资源,我建议从主要游戏循环的概念开始,以及如何绘制和动画和碰撞通常。他们不是用一堆图片盒和计时器来做的,例如,并不是因为他们当时没有那种奢侈品,而是因为这样的事情会成为障碍。
从好的方面来说,这些天你不需要编写汇编代码来制作具有精美平滑的精灵动画和良好帧速率的酷2D游戏。您可以使用高级解释器编写它们,只要您的绘图库和可能的声音库/混音器很快就可以。你想要确定的是你可以控制到底画什么以及何时画画。你想要那种主要的循环心态。