Prevent Flickering for moving object - c#

I'm trying to make a breakout game for an assignment in windows form and I've made games before, but I've just not used winforms before. I've found things that are supposed to help like OnPaint() (which you're supposed to override), DoubleBuffered andInvalidate`. But I'm just struggling applying it to my code
Here's what I have:
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);
}
I'm drawing the ball in Update method (timer1_tick), but I feel like I shouldn't be.
Thanks :)

You can create your custom Control or Form and:
In constructor Set styles for flicker free painting OptimizedDoubleBuffer, UserPaint, AllPaintingInWmPaint
In constructor Set style for redraw if the size changes ResizeRedraw
In Tick event just update position of ball and then Invalidate the control
override OnPaint method of your control and put the paint logic there
Also you should use properties for speed or ball size, to be able to invalidate the control if those values changed. (I didn't implement properties in below code.) Also you can increase the interval a little.
For example:
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();
}
}

The flickering is happening because you are drawing directly to the display. First you clear the display, which the user will see for a split second, then you draw a circle over it, which is only displayed for a short time before the process repeats. That "overdrawing" is where the flicker is coming from.
One method to get rid of it is to do all your drawing to a memory bitmap and, once complete, move the entire bitmap to the display.
Once other issue I see with your code is that you create a Graphics instance in the constructor and keep it around for the life of the program. That's a pattern that you should avoid. Instead you should create a new Graphics object, preferably in a using statement for each "frame". That will make sure that everything is getting cleaned up properly.

Related

How to get my Ellipse to flash with Timer.Interval on C#? (Emergency Red Light Simulation) (Using MonoDevelop on Ubuntu)

I cannot figure how to get the red light on my code to flash with respect to an interval that I have set for it.
I've tried to use Invalidate and Update, along with Refresh and none of them seems to work. I'm very new to C# and coding in general so it could be that the code is deeply flawed and if that is the case don't hesitate to yell at me.
public class UI: Form
{
protected Label lights_title = new Label();
protected Button pause, resume, exit;
protected bool isPaint = true;
public static System.Timers.Timer g_shock = new System.Timers.Timer();
public UI()
{
// text initialization
this.Text = "Chukwudi's Flashing Red Light";
this.lights_title.Text = "Ikem's Emergency Lights!";
// size initialization - determine the size of the UI Form
this.lights_title.Size = new Size(700, 40);
this.Size = new Size(1920, 1080);
// location initialization (x,y) x pulls you right as the number increase, y pulls you down
this.lights_title.Location = new Point(598, 60);
// title config & background color
this.lights_title.BackColor = Color.Coral;
this.lights_title.TextAlign = (System.Drawing.ContentAlignment)HorizontalAlignment.Center;
this.BackColor = Color.DimGray;
Render();
g_shock.Enabled = false;
g_shock.Elapsed += ManageOnPaint;
g_shock.Enabled = true;
g_shock.Start();
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics shape = e.Graphics;
if (isPaint)
{
shape.FillRectangle(Brushes.Black, new Rectangle(598, 90, 700, 700));
shape.FillEllipse(Brushes.Red, new Rectangle(650, 140, 600, 600));
}
shape.FillRectangle(Brushes.Brown, new Rectangle(598, 785, 700, 60));
}
protected void ManageOnPaint(object sender, System.Timers.ElapsedEventArgs elapsed)
{
g_shock.Interval = 1000;
Invalidate();
Update();
// turn something to true
}
}
As Alex F pointed out, you're not toggling something to keep track of whether the control is red or not.
And you're not painting anything in the timereventhandler.
My suggestion is below, I left out bits that don't concern my point.
private bool isRed;
private static System.Timers.Timer g_shock = new System.Timers.Timer();
public UI()
{
g_shock.Elapsed += ManageOnPaint;
g_shock.Interval = 1000;
g_shock.Start();
}
private void ManageOnPaint(object sender, System.Timers.ElapsedEventArgs elapsed)
{
if (isRed)
{
// Set ellipse color non red
}
if (!isRed)
{
// Set ellipse color red
}
// Toggle isRed
isRed = !isRed;
}
protected override void OnPaint(PaintEventArgs e)
{
/// paint ellipse with current ellipse color
}

How to make an animation look consistent and smooth

`So I have a total of 6 images that when animated, creates the illusion of a person walking. The problem is its not smooth, refresh(), invalidate(),update() have failed me. How do I go about this.
namespace Runner
{
public partial class Form1 : Form
{
Keys moveRight;
Keys moveLeft;
public static bool isMovingR = false;
public static bool isMovingL = false;
Bitmap stnd = new Bitmap(Properties.Resources.Standing);
static Bitmap wlk_1_RL = new Bitmap(Properties.Resources.Walk_1_RL);
static Bitmap wlk_2_RL = new Bitmap(Properties.Resources.Walk_2_RL);
static Bitmap wlk_3_RL = new Bitmap(Properties.Resources.Walk_3_RL);
static Bitmap wlk_4_LL = new Bitmap(Properties.Resources.Walk_4_LL);
static Bitmap wlk_5_LL = new Bitmap(Properties.Resources.Walk_5__LL);
static Bitmap wlk_6_LL = new Bitmap(Properties.Resources.Walk_6_LL);
Graphics gfx;
Animation animate = new Animation(new Bitmap[] { wlk_1_RL, wlk_2_RL, wlk_3_RL,
wlk_4_LL, wlk_5_LL, wlk_6_LL });
Timer timer = new Timer();
int imageX = 5;
int imageY = 234;
public Form1()
{
InitializeComponent();
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
moveRight = Keys.D;
moveLeft = Keys.A;
if (Keys.D == moveRight)
{
isMovingR = true;
timer.Enabled = true;
timer.Interval = 50;
timer.Tick += timer1_Tick;
//imageX += 5;
Refresh();
} else if (Keys.A == moveLeft)
{
isMovingL = true;
imageX -= 5;
Refresh();
}
}
private void timer1_Tick(object sender, EventArgs e)
{
gfx = this.CreateGraphics();
gfx.DrawImage(animate.Frame2Draw(), imageX, imageY);
//Refresh(); Invalidate(); Update();
}
}
}
Now, the problem is being able to make the animation consistent and smooth
UPDATE...animation class
public class Animation
{
int slctdImg = 0;
public static Bitmap[] images;
Bitmap frame2Draw;
public Animation(Bitmap[] frames)
{
foreach (Bitmap btm in frames)
{
btm.MakeTransparent();
}
images = frames;
}
public Bitmap Frame2Draw()
{
if (slctdImg < images.Length)
{
frame2Draw = images[slctdImg];
slctdImg++;
}
if (slctdImg >= images.Length)
{
slctdImg = 0;
frame2Draw = images[slctdImg];
}
return frame2Draw;
}
}
Many issues:
I wonder why you would call MakeTransparent on each Tick?? I doubt it does what you expect.. It is changing pixels and rather expensive; you ought to cache the images instead.
Nor why you create an array of bitmap on each Tick??? And since it is always created the same way it always displays only the 1st image.. This should answer your question.
Further issues:
Using this.CreateGraphics(); will fail to create a persistent result although that may not be your aim as you try to animate.
Remember that a Timer.Interval can't run faster than 15-30 ms; also that winforms is notoriously bad at animation.
Remember that c# is zero-based, so this slctdImg > images.Length should probably be slctdImg >= images.Length
Here is what you should do instead:
Move the instantiation either to the form load or maybe to a key event.
Move the drawing to the Paint event of the form.
In the Tick count up frames and/or position and trigger the Paint by calling Invalidate on the form!
Update:
One more issue is the way you hook up the Tick event each time the right key is pressed. Hooking up an event multiple times will result in it running multiple times; this will create to gaps/jumps in your animation..
Either add an unhook each time before hooking up (it will fail quietly the 1st time)
timer.Tick -= timer1_Tick;
timer.Tick += timer1_Tick;
or (better) hook it up only once in the original set up!

Struggling to get new graphics to appear with each tick of the timer

Sorry if this sounds a bit of a dim question - I'm completely new to this. I'm trying to get a new ellipse to appear on each tick of a timer. So far, I have:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication6
{
public partial class Form1 : Form
{
private Bitmap DrawingArea;
int numberofcircles = 0;
int[] narrary = new int[30];
int newcircle;
Random rnd = new Random();
public Form1()
{
InitializeComponent();
Invalidate();
}
private void button1_Click(object sender, EventArgs e)
{
numberofcircles = numberofcircles + 1;
newcircle = (rnd.Next(15) * 6) + 76;
narrary[numberofcircles] = newcircle;
Invalidate();
timer1.Start();
}
private void Form1_Load(object sender, EventArgs e)
{
DrawingArea = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Pen pen = new Pen(Color.Black);
using (var canvas = Graphics.FromImage(DrawingArea))
{ canvas.Clear(Color.Transparent);
canvas.DrawLine(pen, 100, 100, 700, 100);
for (int i = 1; i <= numberofcircles; i++)
{
canvas.DrawEllipse(pen, 180 + (30 * i), narrary[i], 8, 6);
}
}
this.Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Pen pen = new Pen(Color.Red);
for (int i = 1; i <= numberofcircles; i++)
{
canvas.DrawEllipse(pen, 180 + (30 * i), narrary[i], 8, 6);
}
e.Graphics.DrawImage(DrawingArea, 0, 0, DrawingArea.Width, DrawingArea.Height);
}
private void timer1_Tick(object sender, EventArgs e)
{
numberofcircles = numberofcircles + 1;
newcircle = (rnd.Next(15) * 6) + 76;
narrary[numberofcircles] = newcircle;
for (int i = 1; i <= numberofcircles; i++)
{
canvas.DrawEllipse(pen, 180 + (30 * i), narrary[i], 8, 6);
}
Invalidate();
}
}
}
"canvas" and "pen" references are flagging up as errors in the Form1_Paint and timer1_Tick sections ("The name 'canvas' does not exist in the current context"). I'm sure I must be referencing them wrong, but I'm afraid I don't have the basic C# knowledge to be able to sort this out!
I'd be very grateful for any help.
There are a number of concepts it looks like you need explaining.
First, as noted in the comments, you need to pay attention to "scoping". In C# (and most other languages), variables have a well-defined scope that prevent them from being visible except where they are relevant. The variable you're having trouble with are "local variables", and are valid only in the method in which they are declared.
If you want those variables to be visible to other methods, they need to be declared somewhere that they are visible to all methods. For example, you could declare them as instance variables.
But in this case, that would be the wrong thing to do. Another concept you seem to have trouble with is how drawing in a Winforms program works. You should only draw to the screen in the Paint event handler. There are a couple of ways to approach this:
Keep track of the data that is the basis of what you're drawing, and then redraw everything any time the Paint event is raised.
Maintain an off-screen bitmap, drawing new data to it as needed, and then draw this bitmap to the screen when the Paint event is raised.
Either way, you cause the Paint event to be raised by calling Invalidate().
Here is an example of the first approach, based on your original code:
public partial class Form1 : Form
{
const int maxCircleCount = 30;
List<int> circles = new List<int>();
Random rnd = new Random();
public Form1()
{
InitializeComponent();
DoubleBuffered = true;
}
private void button1_Click(object sender, EventArgs e)
{
// Arguably, you only need to start the timer and can skip these first two lines
circles.Add(rnd.Next(15) * 6 + 76);
Invalidate();
timer1.Start();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawLine(Pens.Black, 100, 100, 700, 100);
for (int i = 0; i < circles.Count; i++)
{
e.Graphics.DrawEllipse(Pens.Red, 180 + (30 * i), circles[i], 8, 6);
}
}
private void timer1_Tick(object sender, EventArgs e)
{
if (circles.Count < maxCircleCount)
{
circles.Add(rnd.Next(15) * 6 + 76);
Invalidate();
}
else
{
timer1.Stop();
}
}
}
The above will draw 30 circles after you click the button, one per timer tick, and then stop the timer. Since in your code example, when the Load event handler is called you don't have any circles yet, it wasn't clear to me what your intent with that code was. So I did not bother to draw any black circles.
Other changes include:
Setting DoubleBuffered to true, to avoid flickering when Invalidate() is called.
Using the stock Pens objects instead of creating them (note that your code, which did create new Pen objects, should have disposed the Pen objects it created…that it did not was also a bug).
Use a List<int> for the circles. This encapsulates the storage and the count in a single object.
Note that an improvement I didn't bother to make would be to consolidate the circles.Add() and Invalidate() calls into a separate method that can be called by any place it needs to be.
Hopefully the above gets you back on track. There are lots of other questions on Stack Overflow discussing the various nuances of how to draw in a Winforms program, whether from raw data or by caching to an off-screen bitmap. You should be able to use those posts to refine your techniques.
See also e.g. Force Form To Redraw? and How do I call paint event?, which include some answers that elaborate on the basic "call Invalidate()" concept.

How do I autohide a form

I have a very simple question i guess...for which i am not able to find an answer for. I am trying to add an autohide feature for a borderless WinForm which is located at (0,0) with a width of 150. I have the following code:
private int dx;
private void autohide()
{
for (dx = 0; dx > -150; dx--)
{
this.Width = dx;
Thread.Sleep(2);
}
}
Even after, using Thread.Sleep(x), the Form just snaps off to final Width without giving/having any effect of delay. I am trying to put a bit of effect on to it .
Please help...
The issue you are facing is because the window is not re-drawing itself at any point because your code doesn't exit the autohide() routine until dx is 150, so it will just have a delay before re-drawing in the final position.
You probably also want to change the position rather than the width.
The better option would be to start up a Timer which then changes the position each time it fires, which would cause the change to be animated:
private Timer t;
private int step = 1;
private void autohide()
{
t = new Timer();
t.Interval = 2;
t.Tick += T_Tick;
t.Start();
}
private void T_Tick(object sender, EventArgs e)
{
if (this.Location.X > 0 - this.Width)
{
this.Location = new Point(this.Location.X - step, this.Location.Y);
}
else
{
t.Stop();
}
}

Stuck trying to get my c# text scroller to run smoothly

I'm doing a small project where my vc# application needs to include a text scroller / news ticker. Application will be running on 30+ screens showing off internal ad production at my workplace.
I've been googling and testing for a couple of months now but has yet to find / create a good solution where the movement is smooth and not choppy.
So my question is: is it possible to create perfect smooth scroll motion in c# or do I need to go about it some other way?
The code I'm using at the moment, part of a sample I edited, is running almost smooth except it seems to lag every 100 ms or so.
Here is the code I'm using:
namespace ScrollDemo1
{
public partial class NewsTicker : Panel
{
private Timer mScroller;
private int mOffset;
private string mText;
private Size mPixels;
private Bitmap mBuffer;
public NewsTicker()
{
mScroller = new Timer();
mScroller.Interval = 1;
mScroller.Enabled = false;
mScroller.Tick += DoScroll;
}
[Browsable(true)]
public override string Text
{
get { return mText; }
set
{
mText = value;
mScroller.Enabled = mText.Length > 0;
mPixels = TextRenderer.MeasureText(mText, this.Font);
mOffset = this.Width;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
}
private void DoScroll(object sender, EventArgs e)
{
mOffset -= 1;
if (mOffset < -mPixels.Width) mOffset = this.Width;
Invalidate();
Update();
}
protected override void OnPaint(PaintEventArgs e)
{
if (mBuffer == null || mBuffer.Width != this.Width || mBuffer.Height != this.Height)
mBuffer = new Bitmap(this.Width, this.Height);
Graphics gr = Graphics.FromImage(mBuffer);
Brush bbr = new SolidBrush(this.BackColor);
Brush fbr = new SolidBrush(this.ForeColor);
Bitmap bmp = global::ScrollDemo1.Properties.Resources.text_bg1;
TextureBrush tb = new TextureBrush(bmp);
int iLoc = (this.Height / 2) - (mPixels.Height / 2);
//Console.WriteLine(iLoc.ToString());
//gr.FillRectangle(bbr, new Rectangle(0, 0, this.Width, this.Height));
gr.FillRectangle(tb, new Rectangle(0, 0, this.Width, this.Height));
gr.DrawString(mText, this.Font, fbr, mOffset, iLoc);
e.Graphics.DrawImage(mBuffer, 0, 0);
bbr.Dispose();
fbr.Dispose();
gr.Dispose();
}
}
}
I'd suggest you go with WPF. It has rich and easy to use support for animations and tends to be be very smooth since it is Direct3D based.

Categories