Tutorial: Creating a ClanLib project in linux

We will be creating a graphical version of the game 'boxes'.

What you need

Preparation

I generally set up my desktop with emacs on one side of the screen and a console window on the other side. It looks like this:
Screenshot of desktop
Yeah, I know there are ways of making emacs compile with shortcut keys but I didn't find it as flexible as a text editor and a console. The console also allows you to view documentation and look at other examples while coding.

How to do it

  1. I will start by showing you how it will look when it's finished. This is the general idea I had in my mind before starting the project. Finished program
  2. Copy the following code into a file called lines.h:
    #include <ClanLib/application.h>
    #include <ClanLib/core.h>
    #include <ClanLib/display.h>
    #include <ClanLib/gl.h>
    #include <ClanLib/sound.h>
    #include <ClanLib/vorbis.h>
    
    #ifndef __LINES_H__
    #define __LINES_H__
    
    const int boardsize = 6;
    const int spacing = 50;
    const int border = 20;
    
    const int numsquares = int(pow(float(boardsize - 1), 2));
    
    class Lines : public CL_ClanApplication
    {
    
    public:
      virtual int Lines::main(int, char **);
    };
    
    #endif //__LINES_H__
    This is the header file which will be included in every source file. The #includes are fairly self-explanitory. Each loads a header for a different ClanLib subsystem. boardsize is how many dots wide and high the board should be. spacing is how many pixels we want between dots. border is how many pixels there should be between the window border and the dots. numsquares will be used later on, it is set to the number of squares in the board. main() is like the normal C++ main(), except it is inside the Lines class and is called by the ClanLib main() class.
  3. Copy the following code into a file called clanmain.cpp:
    #include "lines.h"
    int Lines::main(int, char **)
    {
      try {
        CL_SetupCore::init();
        CL_SetupDisplay::init();
        CL_SetupGL::init();
        CL_SetupSound::init();
        CL_SetupVorbis::init();
    
      
        CL_SetupVorbis::deinit();
        CL_SetupSound::deinit();
        CL_SetupGL::deinit();
        CL_SetupDisplay::deinit();
        CL_SetupCore::deinit();
      }
      catch (CL_Error err) {
        std::cout << "Exception caught: " << err.message.c_str() << std::endl;
      }
    
      return 0;
    }
    This is the main() function which will be called by ClanLib. The entire program will be placed in the try block, which means any ClanLib errors can be caught and displayed later by the catch statement. All those init() and deinit() are required for respective ClanLib subsystems to work. Core is needed for everything. Display and GL are needed if you want to display graphics. Sound is for, well, sound and Vorbis for music (that's right, there's music).
  4. The way ClanLib works is like this: You make a class which inherits the basic ClanLib one (called CL_ClanApplication). In this program, it is called Lines and is defined in lines.h. ClanLib holds the actual main() function which is run by the system, and in turn this function calls the main() defined in your class. Using this method means you can take any ClanLib program and compile it on any platform supported by ClanLib, without modification.
    Let's make a window on the screen to work with. You do this by creating a CL_DisplayWindow object. I'm going to call this game 'Line Pwner' but you can call it whatever you want. Enter this before the try statement:
      int winsize = spacing * (boardsize - 1) + border * 2;
    This is for working out how big the window should be.
    Enter this between the init and deinit sections:
        CL_DisplayWindow window("Line pwner", winsize, winsize);
    This will make a window on the screen with a title of "Line pwner", and a width and height of winsize.
  5. Generally to make a ClanLib application, you would make a loop that loops around until you stop the program. This loop takes in input, uses that to update the game and draws graphics. Put something like this next:
        while (!CL_Keyboard::get_keycode(CL_KEY_ESCAPE)) {
    
          CL_Display::flip();
          CL_System::keep_alive(20);
        }
    CL_Keyboard::get_keycode() returns true if the specified key is pressed, false otherwise. So this will loop around until escape is pressed.
    CL_Display::flip() swaps the front buffer and back buffers. The backbuffer is what you draw all your graphics to during the frame, and the front buffer is what is displayed on the screen.
    CL_System::keep_alive() updates all input and system events (like closing the window or moving it), and if given an argument will wait that many milliseconds before proceeding. This is a good idea because it frees up cpu cycles.
  6. This is a full working ClanLib application now. You can compile and run it. To do that, we'll use a Makefile and make. Here is a Makefile I put together (with help from kent). Copy the following into a file called Makefile:
    PACKAGES = clanCore-0.7 clanDisplay-0.7 clanApp-0.7 clanGL-0.7 clanVorbis-0.7 clanSound-0.7
    CPP = g++ -Wall `pkg-config --cflags $(PACKAGES)`
    lines: clanmain.o
    	$(CPP) clanmain.o -fmessage-length=0 -O3 -o boxes `pkg-config --libs $(PACKAGES)`
    clean:
    	@rm -f *.o boxes
    Once you have this, just enter the project directory and type 'make'. When it is finished compiling, you can run it by typing './lines'. A blank window should show up, and should disappear if you press escape. Cool! From now on, you can type 'make && ./lines' to see what it looks like. (Note: If you are using bash, it is really easy to do this sequence once and press up then return the next time you want to run it)
  7. Let's draw the dots on the screen, shall we? Enter this right before the CL_Display::flip() statement:
          CL_Display::clear();
          // Draw grid
          for (int x = 0; x < boardsize; x++)
            for (int y = 0; y < boardsize; y++)
              CL_Display::draw_rect(CL_Rect(x * spacing + border,
                                            y * spacing + border,
                                            x * spacing + border + 2,
                                            y * spacing + border + 2),
                                    CL_Color::white);
    The first line will clear the screen black. Easy.
    The next part loops around and draws the dots. I decided to use rectangles instead of single pixels for the dots, because single pixels are just too small. CL_Display::draw_rect() does this nicely. It takes 2 arguments: An object representing the rectangle (CL_Rect), and the colour (CL_Color). CL_Rect takes the location of the top-left hand pixel and the location of the bottom-right hand pixel as input. There are a whole list of colour presets in CL_Color, I'll just use white for this tutorial. Compile and run the program now, and you should see something like this:
    Window with dots
  8. ???
  9. Profit!
I'll finish this one day. Go back to the main page