Home
Projects
My Games
My Book
Coding
Links

Coding

This section will be used to list a couple of commonly used code snippets.

Index

 

Running with the same speed on all computers

A common problem is that games run with different speeds on different computers. Luckily, that's an easy to fix problem:
Assume that we'd like to run at a speed of 60 frames per second. Let's also assume for a second, that our game runs always at this speed, regardless of the hardware it's on. This means that we can fine tune all movements and animations depending on this speed.

If we move a sprite by 1 pixel once per frame, it will have moved 60 pixel after one second. What we need to ensure now, is that the game actually runs with this fixed speed on all computers.

The most easy way to ensure this is by separating logic and drawing code. If we call the logic code a constant amount of times per second, and update the screen as soon as the logic code has been called at least once, we'll get the desired result: All objects will move as if the game runs with our fixed frame rate and if the computer is fast enough the screen will be updated with this framerate - if the computer is slower the logical frame rate will stay the same, but the number of screen updates will decrease:

volatile int timerCounter = 0;
static void timerCounterUpdater() {
    timerCounter++;    
}
END_OF_STATIC_FUNCTION(timerCounterUpdater);


/* ... */

install_timer();    
/* Make sure the function and variable doesn't get swapped out */
LOCK_FUNCTION(timerCounterUpdater);
LOCK_VARIABLE(timerCounter);

/* Update timerCounter with 60 frames per second */
install_int_ex(timerCounterUpdater, BPS_TO_TIMER(60));


/* Inside your main loop: */

/* Never skip more than 6 frames per update */
int maxSkip = 6;
int curSkip = 0;
if (timerCounter > 0) {                    
    do {
        
        /* actual logic here */
        
        /* Book keeping */
        timerCounter--;
        curSkip++;        
        if (curSkip >= maxSkip) {
            timerCounter = 0;
            break;
        }
    } while (timerCounter > 0);
    needsRefresh = TRUE;
}

if (needsRefresh) {
    needsRefresh = FALSE;
    
    /* display code */
}
 

Macros for often used timer actions

If you use Allegro timers, you'll end up with the same code most of the time:
A volatile variable that's updated in a function which has a guard around it. Later you'll have code to lock both the variable and the function and then install the the timer. A typical example of this can be found above.

Macros are normally not recommanded. But for this example you have no other choice, since we need to construct the name of the update function based on the name of the variable we want to update. If we want the timer variable to be name fooCounter the update function should be called fooCounterUpdater(). This can be done easily by using the concat operator of the preprocessor: ##. The ## operator combines the value of a macro variable with the literal following it:


#define TIMER_DECL(VAR)          \
    volatile int VAR = 0;        \
    static void VAR##Updater(){  \
        VAR++;                   \
    } END_OF_STATIC_FUNCTION(VAR##Updater)

#define TIMER_SET(VAR, FPS)      \
    LOCK_FUNCTION(VAR##Updater); \
    LOCK_VARIABLE(VAR);          \
    install_int_ex(VAR##Updater, BPS_TO_TIMER(FPS))

This allows you to define both the function and the variable with a single TIMER_DECL(fooCounter);. To set the timer to 60 frames per second all you need to do is: TIMER_SET(fooCounter, 60);. The locking of the variable and the function will be done by the macro just before install_int_ex() is called.

 

Accessing the windows clipboard

The following code shows how to read a BITMAP from the windows clipboard, and how to put a BITMAP into it. The example code tries to read a bitmap from the clipboard, sets every other line to black, and copies it back to the clipboard.

#include <allegro.h>
#include <winalleg.h>

BITMAP* getBitmapFromClipboard() {
    BITMAP *result = NULL;
    
    if (!IsClipboardFormatAvailable(CF_BITMAP)) {
        return NULL;
    }
    
    if (OpenClipboard(win_get_window())) {
        HBITMAP hbmp;
        
        hbmp = GetClipboardData(CF_BITMAP);
        if (hbmp != NULL) {
            result = convert_hbitmap_to_bitmap(hbmp);
        }
        CloseClipboard();
    }
    return result;
}

void copyBitmapToClipboard(BITMAP *bmp) {
    if (OpenClipboard(win_get_window())) {
        if (EmptyClipboard()) {
            HBITMAP hbmp = convert_bitmap_to_hbitmap(bmp);
            if (hbmp) {
                if (!SetClipboardData(CF_BITMAP, hbmp)) {
                    DeleteObject(hbmp);
                }                
                CloseClipboard();
            }
        }
    }
}



int main(int argc, char** argv) {
    BITMAP *bmp;
    
    allegro_init();
    set_color_depth(16);
    set_gfx_mode(GFX_GDI, 400, 400, 0, 0);
    
    install_keyboard();
    install_timer();
    
    bmp = getBitmapFromClipboard();
    if (bmp) {
        int a;
        for (a=0; a < bmp->h; a+=2) {
            hline(bmp, 0, a, bmp->w, 0);
        }
        copyBitmapToClipboard(bmp);
    }
    
    while (!key[KEY_ESC]) {
        if (bmp) {
            blit(bmp, screen, 0, 0, 0, 0, bmp->w, bmp->h);
        }
        rest(2);
    }
    destroy_bitmap(bmp);
}
END_OF_MAIN()
All pages copyright 2003 Lennart Steinke

Haftungsausschluss : : Anbieter