Use sprites to animate several bouncing balls in C#

[sprites]

This example is similar to the one described in the post Animate several bouncing balls in C# except it uses sprites to control the balls. The previous example stores the ball data in a group of arrays. This approach works but it puts a lot of information about the balls in the main program’s arrays.

This example uses a simple “sprite” class to store information about a ball. The main program then calls the sprites’ methods to move and draw the balls instead of doing that directly. This approach makes it much easier for the main program to coordinate the sprite objects (sprites). It’s a particularly useful technique if the sprites represent different kinds of objects, possibly with different movement rules.

The following code shows the BallSprite class. The code is reasonably straightforward. If you compare it to the previous program’s code, you should be able to see how the new version provides the same features as the old one.

class BallSprite
{
    public Rectangle Location;
    public Point Velocity;
    public Color BackColor, ForeColor;
    public int MaxX, MaxY;

    // Constructor that initializes randomly.
    private static Random rand = new Random();
    public BallSprite(int min_r, int max_r,
        int max_x, int max_y, int min_v, int max_v)
    {
        MaxX = max_x;
        MaxY = max_y;
        int width = rand.Next(min_r, max_r);
        Location = new Rectangle(
            rand.Next(0, max_x - width),
            rand.Next(0, max_y - width),
            width, width);

        int vx = rand.Next(min_v, max_v);
        int vy = rand.Next(min_v, max_v);
        if (rand.Next(0, 2) == 0) vx = -vx;
        if (rand.Next(0, 2) == 0) vy = -vy;
        Velocity = new Point(vx, vy);

        BackColor = RandomColor();
        ForeColor = RandomColor();
    }

    // Return a random color.
    private Color[] colors =
    {
        Color.Red,
        Color.Green,
        Color.Blue,
        Color.Lime,
        Color.Orange,
        Color.Fuchsia,
    };
    private Color RandomColor()
    {
        return colors[rand.Next(0, colors.Length)];
    }

    // Move the ball.
    public void Move()
    {
        // Move the ball.
        int new_x = Location.X + Velocity.X;
        int new_y = Location.Y + Velocity.Y;
        bool bounced = ((new_x < 0) || (new_y < 0) ||
            (new_x + Location.Width > MaxX) ||
            (new_y + Location.Height > MaxY));
        if (new_x < 0)
            Velocity.X = -Velocity.X;
        else if (new_x + Location.Width > MaxX)
            Velocity.X = -Velocity.X;
        if (new_y < 0)
            Velocity.Y = -Velocity.Y;
        else if (new_y + Location.Height > MaxY)
            Velocity.Y = -Velocity.Y;

        if (bounced) Boing();

        Location = new Rectangle(
            new_x, new_y,
            Location.Width,
            Location.Height);
    }

    // Play the boing sound file resource.
    private static void Boing()
    {
        using (SoundPlayer player = new SoundPlayer(
            Properties.Resources.boing))
        {
            player.Play();
        }
    }

    // Draw the ball.
    public void Draw(Graphics gr)
    {
        using (SolidBrush the_brush = new SolidBrush(BackColor))
        {
            gr.FillEllipse(the_brush, Location);
        }
        using (Pen the_pen = new Pen(ForeColor))
        {
            gr.DrawEllipse(the_pen, Location);
        }
    }
}

The following code shows how the main program keeps track of the sprites. This code is much simpler than the previous version.

// The sprites.
private BallSprite[] Sprites;
private Size FormSize;

private void Form1_Load(object sender, EventArgs e)
{
    // Make random balls.
    Random rand = new Random();
    const int num_balls = 10;
    Sprites = new BallSprite[num_balls];
    for (int i = 0; i < num_balls; i++)
    {
        Sprites[i] = new BallSprite(10, 40,
            ClientSize.Width, ClientSize.Height,
            2, 10);
    }

    // Save the form's size.
    FormSize = ClientSize;

    // Use double buffering to reduce flicker.
    this.SetStyle(
        ControlStyles.AllPaintingInWmPaint |
        ControlStyles.UserPaint |
        ControlStyles.DoubleBuffer,
        true);
    this.UpdateStyles();
}

// Move the balls and refresh.
private void tmrMoveBall_Tick(object sender, EventArgs e)
{
    foreach (BallSprite sprite in Sprites) sprite.Move();
    Refresh();
}

// Redraw.
private void Form1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    e.Graphics.Clear(BackColor);
    foreach (BallSprite sprite in Sprites) sprite.Draw(e.Graphics);
}

The sprite class handles all of the details about how each object moves and draws itself. That means the main program merely needs to call each object’s Move and Draw methods.

Fancier versions of sprites could take an elapsed time as a parameter to the Move method. Then the object could calculate how far it should have moved if Tick events don’t occur at exactly the desired intervals.


Download Example   Follow me on Twitter   RSS feed   Donate




This entry was posted in algorithms, animation, audio, graphics, multimedia and tagged , , , , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *