我刚开始学习.NET,所以很可能这是一个很大的错误:
我正在尝试制作一个简单的乒乓球游戏,使用一个表格,然后系统::绘图::图形类将游戏绘制成表格。
我的代码的重要部分如下所示:
(主游戏循环):
void updateGame()
{
//Update Game Elements
(Update Paddle And Ball) (Ex. paddle.x += paddleDirection;)
//Draw Game Elements
//Double Buffer Image, Get Graphics
Bitmap dbImage = new Bitmap(800, 600);
Graphics g = Graphics::FromImage(dbImage);
//Draw Background
g.FillRectangle(Brushes::White, 0, 0, 800, 600);
//Draw Paddle & Ball
g.FillRectangle(Brushes::Black, paddle);
g.FillRectangle(Brushes::Red, ball);
//Dispose Graphics
g.Dispose();
//Draw Double Buffer Image To Form
g = form.CreateGraphics();
g.DrawImage(dbImage, 0, 0);
g.Dispose();
//Sleep
Thread.sleep(15);
//Continue Or Exit
if(contineGame())
{
updateGame();
}
else
{
exitGame();
}
}
(表单初始化代码)
void InitForm()
{
form = new Form();
form.Text = "Pong"
form.Size = new Size(800, 600);
form.FormBorderStyle = FormBorderStyle::Fixed3D;
form.StartLocation = StartLocation::CenterScreen;
Application::Run(form);
}
PS。这不是确切的代码,我只是根据记忆写的,所以这就是任何打字错误或错误的名称,或者初始化表单的一些重要代码行可能来自。
这就是代码。我的问题是,游戏肯定不是每15毫秒(约60帧/秒)更新一次,速度要慢得多,所以我必须做的是每次将球拍/球移动更大的距离,以弥补它更新得不快,这看起来很糟糕。
简言之,当涉及到绘制图形时,某些事情会大大减慢游戏的速度。我有一种感觉,这与我的双重缓冲有关,但我无法摆脱这一点,因为这会产生一些令人讨厌的闪烁。我的问题是,我该如何摆脱这种滞后?
此代码有几个问题会严重影响应用程序性能:
- 每帧创建一个大的
Bitmap
缓冲区而不进行处理 - 实现双缓冲,而WinForms已经很好地实现了这种行为
- updateGame()是递归的,但它不一定是递归的
- 在GUI线程中调用
Thread.Sleep()
代码示例,它使用单独的线程来计算游戏节拍和WinForms内置的双缓冲:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new GameWindow());
}
class GameWindow : Form
{
private Thread _gameThread;
private ManualResetEvent _evExit;
public GameWindow()
{
Text = "Pong";
Size = new Size(800, 600);
StartPosition = FormStartPosition.CenterScreen;
FormBorderStyle = FormBorderStyle.Fixed3D;
DoubleBuffered = true;
SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.UserPaint,
true);
}
private void GameThreadProc()
{
IAsyncResult tick = null;
while(!_evExit.WaitOne(15))
{
if(tick != null)
{
if(!tick.AsyncWaitHandle.WaitOne(0))
{
// we are running too slow, maybe we can do something about it
if(WaitHandle.WaitAny(
new WaitHandle[]
{
_evExit,
tick.AsyncWaitHandle
}) == 0)
{
return;
}
}
}
tick = BeginInvoke(new MethodInvoker(OnGameTimerTick));
}
}
private void OnGameTimerTick()
{
// perform game physics here
// don't draw anything
Invalidate();
}
private void ExitGame()
{
Close();
}
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
g.Clear(Color.White);
// do all painting here
// don't do your own double-buffering here, it is working already
// don't dispose g
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_evExit = new ManualResetEvent(false);
_gameThread = new Thread(GameThreadProc);
_gameThread.Name = "Game Thread";
_gameThread.Start();
}
protected override void OnClosed(EventArgs e)
{
_evExit.Set();
_gameThread.Join();
_evExit.Close();
base.OnClosed(e);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// do nothing
}
}
}
甚至可以进行更多的改进(例如,只使游戏屏幕的一部分失效)。
不要每帧创建一个新的Bitmap
。
不要使用Thread.Sleep
。相反,请检查Timer
组件(System.Windows.Forms
命名空间中的组件)。