防止移动物体闪烁
Prevent Flickering for moving object
我正在尝试为 windows 形式的作业制作一个突破游戏,我以前制作过游戏,但我以前没有使用过 winforms。我发现了一些应该有帮助的东西,比如 OnPaint()
(你应该覆盖)、DoubleBuffered and
Invalidate`。但我只是在努力将它应用到我的代码中
这是我的资料:
int xSpeed, ySpeed;
Graphics display;
Brush brush;
Rectangle ballRect;
public Form1()
{
InitializeComponent();
timer1.Enabled = true;
timer1.Interval = 1;
xSpeed = 5;
ySpeed = 5;
ballRect = new Rectangle(10, 10, 20, 20);
display = this.CreateGraphics();
brush = new SolidBrush(Color.Red);
}
private void timer1_Tick(object sender, EventArgs e)
{
DoubleBuffered = true;
ballRect.X += xSpeed;
ballRect.Y += ySpeed;
if (ballRect.X >= 469)
xSpeed = -xSpeed;
if (ballRect.Y >= 457)
ySpeed = -ySpeed;
if (ballRect.X <= 0)
xSpeed = -xSpeed;
if (ballRect.Y <= 0)
ySpeed = -ySpeed;
display.Clear(Color.White);
display.FillEllipse(brush, ballRect);
}
我在 Update 方法中绘制球 (timer1_tick),但我觉得我不应该这样做。
谢谢:)
出现闪烁是因为您直接在显示器上绘图。首先清除显示,用户将在一瞬间看到它,然后在其上画一个圆圈,该圆圈仅显示很短的时间,然后重复该过程。 "overdrawing" 就是闪烁的来源。
摆脱它的一种方法是将所有绘图绘制到内存位图,完成后,将整个位图移动到显示器。
我在您的代码中看到的另一个问题是您在构造函数中创建了一个 Graphics
实例,并在程序的整个生命周期内保留它。这是您应该避免的模式。相反,您应该创建一个新的 Graphics 对象,最好在每个 "frame" 的 using
语句中。这将确保一切都得到正确清理。
您可以创建自定义 Control
或 Form
和:
- 在构造函数中为无闪烁绘画设置样式
OptimizedDoubleBuffer
、UserPaint
、AllPaintingInWmPaint
- 在构造函数中设置大小改变时重绘的样式
ResizeRedraw
- 在
Tick
事件中只更新球的位置然后Invalidate
控制
- 覆盖
OnPaint
控件的方法并将绘制逻辑放在那里
- 您还应该使用速度或球大小的属性,以便能够在这些值更改时使控件无效。 (我没有在下面的代码中实现属性。)你也可以稍微增加间隔。
例如:
public partial class CustomControl1 : Control
{
public CustomControl1()
{
InitializeComponent();
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
timer1.Enabled = true;
timer1.Interval = 1;
xSpeed = 5;
ySpeed = 5;
ballRect = new Rectangle(10, 10, 20, 20);
brush = new SolidBrush(Color.Red);
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.FillEllipse(brush, ballRect);
}
int xSpeed, ySpeed;
Brush brush;
Rectangle ballRect;
private void timer1_Tick(object sender, EventArgs e)
{
ballRect.X += xSpeed;
ballRect.Y += ySpeed;
if (ballRect.X + ballRect.Width >= this.Width)
xSpeed = -xSpeed;
if (ballRect.Y + ballRect.Height >= this.Height)
ySpeed = -ySpeed;
if (ballRect.X <= 0)
xSpeed = -xSpeed;
if (ballRect.Y <= 0)
ySpeed = -ySpeed;
this.Invalidate();
}
}
我正在尝试为 windows 形式的作业制作一个突破游戏,我以前制作过游戏,但我以前没有使用过 winforms。我发现了一些应该有帮助的东西,比如 OnPaint()
(你应该覆盖)、DoubleBuffered and
Invalidate`。但我只是在努力将它应用到我的代码中
这是我的资料:
int xSpeed, ySpeed;
Graphics display;
Brush brush;
Rectangle ballRect;
public Form1()
{
InitializeComponent();
timer1.Enabled = true;
timer1.Interval = 1;
xSpeed = 5;
ySpeed = 5;
ballRect = new Rectangle(10, 10, 20, 20);
display = this.CreateGraphics();
brush = new SolidBrush(Color.Red);
}
private void timer1_Tick(object sender, EventArgs e)
{
DoubleBuffered = true;
ballRect.X += xSpeed;
ballRect.Y += ySpeed;
if (ballRect.X >= 469)
xSpeed = -xSpeed;
if (ballRect.Y >= 457)
ySpeed = -ySpeed;
if (ballRect.X <= 0)
xSpeed = -xSpeed;
if (ballRect.Y <= 0)
ySpeed = -ySpeed;
display.Clear(Color.White);
display.FillEllipse(brush, ballRect);
}
我在 Update 方法中绘制球 (timer1_tick),但我觉得我不应该这样做。 谢谢:)
出现闪烁是因为您直接在显示器上绘图。首先清除显示,用户将在一瞬间看到它,然后在其上画一个圆圈,该圆圈仅显示很短的时间,然后重复该过程。 "overdrawing" 就是闪烁的来源。
摆脱它的一种方法是将所有绘图绘制到内存位图,完成后,将整个位图移动到显示器。
我在您的代码中看到的另一个问题是您在构造函数中创建了一个 Graphics
实例,并在程序的整个生命周期内保留它。这是您应该避免的模式。相反,您应该创建一个新的 Graphics 对象,最好在每个 "frame" 的 using
语句中。这将确保一切都得到正确清理。
您可以创建自定义 Control
或 Form
和:
- 在构造函数中为无闪烁绘画设置样式
OptimizedDoubleBuffer
、UserPaint
、AllPaintingInWmPaint
- 在构造函数中设置大小改变时重绘的样式
ResizeRedraw
- 在
Tick
事件中只更新球的位置然后Invalidate
控制 - 覆盖
OnPaint
控件的方法并将绘制逻辑放在那里 - 您还应该使用速度或球大小的属性,以便能够在这些值更改时使控件无效。 (我没有在下面的代码中实现属性。)你也可以稍微增加间隔。
例如:
public partial class CustomControl1 : Control
{
public CustomControl1()
{
InitializeComponent();
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
timer1.Enabled = true;
timer1.Interval = 1;
xSpeed = 5;
ySpeed = 5;
ballRect = new Rectangle(10, 10, 20, 20);
brush = new SolidBrush(Color.Red);
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.FillEllipse(brush, ballRect);
}
int xSpeed, ySpeed;
Brush brush;
Rectangle ballRect;
private void timer1_Tick(object sender, EventArgs e)
{
ballRect.X += xSpeed;
ballRect.Y += ySpeed;
if (ballRect.X + ballRect.Width >= this.Width)
xSpeed = -xSpeed;
if (ballRect.Y + ballRect.Height >= this.Height)
ySpeed = -ySpeed;
if (ballRect.X <= 0)
xSpeed = -xSpeed;
if (ballRect.Y <= 0)
ySpeed = -ySpeed;
this.Invalidate();
}
}