A Dash of Game Development – 4. Display and Co-ordinate System.


The previous chapter dealt with the general outline of a game and we saw a basic flow of how a game works. We will slowly add more to that but before we move on, a little bit of theory has be looked into. The reason I want to get into this now, is because as a game developer you ought to have an understanding of display and co-ordinates. We will be using terms and phrases introduced here in later chapters quite frequently.

Graphics are almost always displayed on any display device via a bitmap also called as a raster graphics image. The bitmap is the more frequently used term of the two and is sometimes interchangeably used with the term image. At it’s very core a bitmap is a data structure having a rectangular grid of color points called pixels. A pixel is the smallest addressable bitmap element, i.e. it is the smallest unit of a bitmap that can be controlled. Every pixel in a bitmap has its own address. The address of the pixel corresponds to it’s co-ordinates inside the bitmap. You can think of a pixel as the tiniest dot inside a bitmap and a bitmap as several rows of these tiny dots (pixels). The image seen on your computer screen is also a bitmap and the computer draws/refreshes it several times a sec (60, 75, 100) depending on the refresh rate of your monitor.

Since the image displayed your computer screen is in fact a bitmap, most of the the time it is just addressed as a ‘Screen’ instead of a screen bitmap — and that is exactly why the ‘Screen’ dimensions are given in pixels. So if your monitor has the dimensions of 1024 x 768, it means that internally there is a bitmap somewhere of the size 1024  pixels (columns) by 768 pixels (rows) where all the drawing takes place. (Who does the drawing, how the drawing takes place and where the bitmap resides is something we will come to later, but that shouldn’t concern us right now.) Screen dimensions are known as resolutions and because of hardware limitations, monitors can’t (as of writing this) support any arbitrary resolution. There is usually a fixed set of screen resolutions a monitor can support. We will come to that in later chapters when we discuss portability issues.

Now that we know what bitmaps are, and that a screen is one large bitmap, lets look at the components of the screen that also have bitmaps. Yes, they are the individual windows. Think of it this way, every individual window on the screen has it’s own bitmap. While refreshing the screen, the system GUI draws all the individual window bitmaps (or parts that are visible), composites them into the screen bitmap and displays the bitmap on the monitor. This is probably a very simplistic view of a very complicated flow. The thing that concerns us is the window’s bitmap. That’s where our game contents are drawn and just like the screen, our window’s bitmap is associated with the view contents of the window. We will call this as our game view or simply as ‘View’.

The view just like our screen, corresponds to a bitmap and it’s dimensions are also given in pixels. In our example (first.py) we create a RenderWindow and give it a video mode of 800 x 600, which tells SFML  to create a View of size of 800 x 600 pixels. The View that is created has it’s origin on the top left corner, i.e. The pixel(0,0) represents the topmost and the leftmost pixel of the view. The rows and columns of the view are considered as axes of the view, with the x-axis representing the the columns and y-axis representing the rows of the pixels. We will call them co-ordinate axes.

View and Co-ordinate axes
View and Co-ordinate axes.

To draw anything on the view you need to tell SFML the location and that is done using co-ordinates. Let’s return back to our code and see how we do our drawing. The functions we are going to discuss are the ones highlighted below.

#!/usr/bin/python

from PySFML import sf

# Initialize the game components.
window = sf.RenderWindow(sf.VideoMode(800, 600), "Our First Animation")
text = sf.String("I am a priece of Scrolling Text")
x = 0.0
y = 30.0
running = True

# Start our Game Loop
while running:

    # Get the input from the player as an event
    event = sf.Event()
    while window.GetEvent(event):
        if event.Type == sf.Event.Closed:
            running = False

    # Update the position of our text.
    x = x + 0.3
    if x > 800.0 :
        x = -400.0
    text.SetPosition(x,y)

    # Draw the text.
    window.Clear()
    window.Draw(text)
    window.Display()

# Close the window and end the application.
window.Close()

What do the lines 22 to 25 do? Since we now understand the co-ordinate system, it’s  pretty easy to follow. The code simply sets the position of the text to be displayed (in pixels). For every iteration the x co-ordinate is updated. When the x co-ordinate becomes larger than 800, i.e. goes off the screen, we set the x co-ordinate to -400 so the text snaps back and appears to be scrolling.

Then we take a look at that the drawing code (lines 28-30). It’s now not difficult to understand what is going on there as well. First, we simply ask the window to clear the View — then we ask it to draw the text, and finally we ask the window to display the bitmap. But why are there 3 functions? Lets first look at the function Draw(). Draw seems to be self explanatory — yes, it does draw the text. However, Draw itself doesn’t show anything on the view. If you were to comment out the line window.Display() (line 30),  you will not see anything at all. Why? Because the draw happens on a bitmap internally but the bitmap itself isn’t shown. The function Display() is the one that flips/swaps the bitmap so that it can be displayed. The function Clear() (line 28) clears the contents of the bitmap before drawing anything. If you were to comment out Clear(), you will see the previous contents along with the new contents. Later on we will get into more details about buffer swapping and things like double buffering. For now let’s keep things a bit more simple.

Why is Display a separate function from Draw? That’s because a game can have a lot of entities that need to be drawn per frame. Swapping a bitmap is a slow process and ideally should only take place once per iteration of the loop. When there are a lot of entities, you would call a draw on each one before calling Display() only once after all the entities have finished drawing.

With that information, it’s time to extend the outline of our game to include the draw functions.

InitializeGame

while Running :
        CheckForUserInput
        BuildEventQueue
        if Required.Event in EventQueue :
               TakeAction

        UpdateGameEntities
        ClearTheView
        DrawEntities
        DisplayTheBitmap

CleanupAndClose

We have finished with our outline. In the next chapter we look at putting this in an object oriented framework.