animation and double-buffering. the animation methods described here are based on standard...
TRANSCRIPT
Animation and Double-Buffering
The animation methods described here are based on standard techniques of double-buffering applicable to most high-level programming languages. It DOES NOT make use of .NET or C# api double-buffering controls.
The trick to animation is to generate and display successive images without creating flicker or ghost images caused by an interference between the monitor/display refresh rate and/or the drawing of the animated objects in a way that is visible to the viewer.
Essential elements of a smooth animation:
(1) Frame Rate – When successive images of an animation are displayed at a rate faster than about 24 frames per second the human eye-brain perceives a continuous flow rather than a sequence of static images.
(2) Continuity – The amount of motion of “moving” objects should be relatively small between successive frames. When we need to animate a very fast moving object we have to simulate limitations in the human eye-brain by artificially blurring the image of the moving object in the direction of motion. (Not an issue in most animation applications.)
(3) Double-Buffering – The viewer SHOULD NOT see the image while it is being generated. Rather the viewer is shown the previous image in the sequence until the next image is ready. Then the new image is transferred to the display in a single display interval. Double-buffering requires a hidden location in which to generate new image (called a frame buffer) and a means of transferring an image to the display in a single time unit (called the bitmap block transfer or BitBlt).
Introduction
FormMain – this is the main form of the application it is comprised of a picBox and a timer to control the animation.
picBox – this pictureBox is docked to fill the available space in FormMain. It's background image is “starfield.jpg” with properties set to tile the picBox if it is larger than the image.
timerAnim – the interval for this timer is set to 20 milliseconds it calls a method to move the balls and to bounce any that have reached the border of the pictureBox
aBitmap – in order to enable double-buffering we create a device context (DC) in which to draw the objects being animated. Then the image aBitmap is ready it is passed to the pictureBox by
picBox.Image = aBitmap
ball class – this class defines the ball object, whose properties include position, x and y, velocity vx and vy, diameter called size and a color. This class also defines methods move( ) and bounce( ) for moving the ball and bouncing it off the containing boundaries
List<ball> - a generic list to hold the collection of balls being animated
Balls in SPACE !!! Overview
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Drawing.Drawing2D;using System.Linq;using System.Text;using System.Windows.Forms;
namespace BallsInSpace{ public partial class FormMain : Form { List<ball> balls = new List<ball>(); private Bitmap aBitmap; Random rnd = new Random();
public FormMain() { InitializeComponent(); makeBalls(); timerAnim.Interval = 30; timerAnim.Start(); }
Source Code
aBitmap will be our frame buffer
generic List to hold members of the ball class
30 millisecond frame time 1/0.03 = 33.33 frames/sec
private void resetFrameBuffer() { Graphics g = CreateGraphics(); if (aBitmap != null) aBitmap.Dispose(); aBitmap = new Bitmap(picBox.Width, picBox.Height, g); }
We will be using a bitmap image called aBitmap in which to draw the new frames of our animation.
The size of the picBox (since it is docked to FormMain) can be changed by the user. When this occurs we need to create a new frame buffer (aBitmap).
resetFrameBuffer( )
private void drawBalls(){ resetFrameBuffer(); Graphics g = Graphics.FromImage(aBitmap); System.Drawing.SolidBrush aBrush = new System.Drawing.SolidBrush(Color.Black);
foreach (ball b in balls) { aBrush.Color = b.Color; g.FillEllipse(aBrush, b.X -b.Size/2.0F, b.Y, b.Size/2.0F, b.Size, b.Size); } picBox.Image = aBitmap;}
drawBalls( )
Graphics region g not visible in picBox
center of Ellipse (ball)
color of ball
width, height of ball
transfer finished frame to picBox
private void updateBalls(){ foreach (ball b in balls) { b.move(); b.bounce(picBox.Width, picBox.Height); }}
private void timerAnim_Tick(object sender, EventArgs e){ drawBalls(); updateBalls();}
updateBalls( ) & timerAnim_Tick(. . .)
public void move(){ x += vx; y += vy;}
public void bounce(float w,
float h){ if (x < size/2.0F) { vx *= -1.0F; x = size/2.0F; } if (x > w - size/2.0F) { vx *= -1.0F; x = w - size/2.0F; } if (y < size/2.0F) { vy *= -1.0F; y = size/2.0F; } if (y > h - size/2.0F) { vy *= -1.0F; y = h - size/2.0F; }}
methods from the ball Class
private void makeBalls(){
int w = picBox.Width;int h = picBox.Height;float vmax = 10.0F;float s;
for (int i = 0; i < 200; i++) {
s = (float)(rnd.Next(40) + 10);
balls.Add(new ball( (float)rnd.Next()/(float)int.MaxValue*(w - s) + s / 2.0F, (float)rnd.Next() / (float)int.MaxValue * (h - s) + s / 2.0F, (float)rnd.Next() / (float)int.MaxValue * vmax - vmax / 2.0F, (float)rnd.Next() / (float)int.MaxValue * vmax - vmax / 2.0F, s, Color.FromArgb(rnd.Next(128)+127,
rnd.Next(128)+127, rnd.Next(128)+127)));
}}
makeBalls( )
calls the constructor for the ball Class
Original Size of FormMain
Effect of resizing FormMain
private void picBox_MouseDown(object sender, MouseEventArgs e) { float xp; float yp; xp = (float)(MousePosition.X - FormMain.ActiveForm.Location.X); yp = (float)(MousePosition.Y - FormMain.ActiveForm.Location.Y); foreach (ball b in balls) { b.X = xp; b.Y = yp; } }
picBox_MouseDown(. . .)
This method resets the x,y positions of all the balls to the mouse click location, without changes the ball velocities vx, vy.
Effect of Clicking in FormMain
using System;using System.Drawing;using System.Drawing.Drawing2D;using System.Collections.Generic;using System.Linq;using System.Text;
namespace BallsInSpace{ class ball { protected float x; protected float y; protected float vx; protected float vy; protected float size; protected Color color;
public float Y { get { return y; } set { y = value; } }
public float X { get { return x; } set { x = value; } }
ball Class
ball Class defines the properties and provides methods for an instance of a ball.
Properties: position, velocity, size and color
Methods: move( ), and bounce( )
public float Vx { get { return vx; } set { vx = value; } }
public float Vy { get { return vx; } set { vx = value; } }
public float Size { get { return size; } set { size = value; } }
public Color Color { get { return color; } set { color = value; } }
More Accessors for ball Class Properties
public ball(float newX, float newY, float newVx, float newVy, float newSize, Color newColor){ x = newX; y = newY; vx = newVx; vy = newVy; size = newSize; color = newColor;}
public void bounce(float w, float h){ if (x < size/2.0F) { vx *= -1.0F; x = size/2.0F; } if (x > w - size/2.0F) { vx *= -1.0F; x = w - size/2.0F; } if (y < size/2.0F) { vy *= -1.0F; y = size/2.0F; } if (y > h - size/2.0F) { vy *= -1.0F; y = h - size/2.0F; }}
public void move() { x += vx; y += vy; }
ball Constructor and move( ) & bounce( ) methods
Expressing the position and velocity of the balls as floats rather than integers significanly improves the flow of the animation. This is true even though all objects are drawn using integer data types.
The bounce(. . .) method checks to see if the ball position is outside the drawing boundary, moves the ball back inside the boundary and reverses the sign of the velocity.