Day 2 - First Drawing

Objectives

  • Draw a simple board with colored Gems
  • Gems will be circles for now as that's easy to draw

Screenshot

screenshot_1.jpeg

The Drawing Surface

Going through the SDL tutorial I mentioned the day before, I learned that SDL works with the principle of a surface to draw things on the screen. To create that surface we need to set a specific video mode to be used. This can be done through the static method SetVideoMode provided by the Video class from the SDL Graphics namespace:

drawingSurface = Video.SetVideoMode(320, 200, 32, false, false, false, true);

For now I'm creating a surface of 320 pixels by 200 pixels, with a 32bit color depth and the following properties:

  • Resizable: False - We don't want our Window to be User resizable
  • OpenGL Surface: False - We don't want that OpenGL is able to render to our Surface
  • Fullscreen: False - We just want a Window managed by the Operating System
  • Hardware Surface: True - Of course we want a Surface which resides in the Video Memory of our Graphics Cards. You should always create Hardware Surfaces when possible for the sake of performance

The Event Mechanism

SDL generates events for serveral purposes. I will be using 2 of them today. To tell SDL to start the event mechanism whe simpy need a call to Events.Run(). Beware, this is a blocking method that will only exit when the application quits, so this should be the last call after all your objects are initialized.

1. Frame Ticks

Events.Tick is an event that is generated at a specific frame rate to allow updating the state of the game and redrawing. This is how I add my own event handler to the Tick event:

Events.Tick += Events_Tick;

And this is how the event handler looks like:

private void Events_Tick(object sender, TickEventArgs e)
{
    Draw();
    drawingSurface.Update();
}

As you can see I call the Draw() method to draw the gems on the screen and after drawing I update the surface. Updating is necessary to do the actual drawing. It's like flipping the backbuffer to the frontbuffer for those who know what double buffering means.

2. Quitting the Application

Events.Quit is an event that is generated when the user tries to Quit the application (by clicking the red cross in the upper right corner for example). In our case we actually Quit the application by a call to Events.QuitApplication. This is how I add my own event handler to the Quit event:

Events.Quit += Events_Quit;

And this is how the event handler looks like:

private static void Events_Quit(object sender, QuitEventArgs e)
{
    Events.QuitApplication();
}

The Actual Drawing

To simplify the drawing for now, we use circles as gems. For drawing circles or any other primitives, we just need to call the Draw method on our surface with the primitive as a parameter (in our case an Ellipse with the same width and height), the Color to be used, and some other optional parameters. The random colors used in the following piece of code were generated in the constructor of our Game class (see final code):

private void Draw()
{
    drawingSurface.Fill(Color.Black);
    for(int i=0; i < 10; i++)
    {
        for(int j=0; j < 10; j++)
        {
            drawingSurface.Draw(new Ellipse(new Point(16 + i*32,10 + j*20), new Size(8, 8)), gemColors[i][j],true,true);
        }
    }
}

As you can see I made a call to the Fill method on our surface to make the background black.

Final Code

Game.cs

using System;
using System.Drawing;
using SdlDotNet.Core;
using SdlDotNet.Graphics;
using SdlDotNet.Graphics.Primitives;
 
namespace Bejeweled
{
    class Game
    {
        private readonly Surface drawingSurface;
        private static readonly Color[] COLORS = new[] {Color.Red,Color.Green,Color.Blue,Color.Yellow};
        private readonly Color[][] gemColors;
 
        public Game()
        {
            drawingSurface = Video.SetVideoMode(320, 200, 32, false, false, false, true);
            Events.Quit += Events_Quit;
            Events.Tick += Events_Tick;
 
            var randomGenerator = new Random((int)DateTime.Now.Ticks);
            gemColors = new Color[10][];
            for(int i=0; i < 10; i++)
            {
                gemColors[i] = new Color[10];
                for(int j=0; j < 10; j++)
                {
                    gemColors[i][j] = COLORS[randomGenerator.Next(COLORS.Length)];
                }
            }
        }
 
        private static void Events_Quit(object sender, QuitEventArgs e)
        {
            Events.QuitApplication();
        }
 
        private void Events_Tick(object sender, TickEventArgs e)
        {
            Draw();
            drawingSurface.Update();
        }
 
        private void Draw()
        {
            drawingSurface.Fill(Color.Black);
            for(int i=0; i < 10; i++)
            {
                for(int j=0; j < 10; j++)
                {
                    drawingSurface.Draw(new Ellipse(new Point(16 + i*32,10 + j*20), new Size(8, 8)), gemColors[i][j],true,true);
                }
            }
        }
 
        public void Play()
        {
            Events.Run();
        }
    }
}

Program.cs

namespace Bejeweled
{
    class Program
    {
        static void Main()
        {
            new Game().Play();
        }
    }
}

UML Diagram

ClassDiagram1.jpg
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License