Porting from A4 to A5

From Allegro Wiki
Jump to: navigation, search
This article is incomplete and needs some revision or has some pending TODOs. Please help Allegro by editing and finishing it. When the article becomes complete, you may remove this tag.


a4_aux.c

Versions of Allegro 4.9.19 and greater started shipping with a basic compatibility layer that translates Allegro 4 function calls (actually rewrites the functions) into Allegro 5 calls. The files are located in demos/speed and are named a4_aux.c and a4_aux.h. These could be helpful porting aids. The game "Speed" written by Shawn Hargreaves for Speedhack 1999 has been ported (as you may have already guessed) to Allegro 5 using this thin wrapper. Check out the source releases or svn for these files.

Documentation

Just like with A4, also in A5 the developers try to maintain a high quality and up-to-date reference manual - it always should be your first stop to look up how something is supposed to work: http://docs.liballeg.org

Mainloop and events

Your A4 mainloop likely looked like:

volatile int ticks;
void timer(void) {ticks++;}

...

install_int(timer, 1000.0 / FPS);

...

while (1) {
    if (game_ticks < ticks) {
        poll_input();
        handle_game_tick();
        game_ticks++;
        need_redraw = true;
    }
    else if (need_redraw) {
        render_last_frame();
        need_redraw = false;
    }

    rest(1);
}

With various variations as how to handle frame skipping and other things. The main points are that input was polled, timing was done by installing a timer, and the loop was constructed so rest(1) would be executed whenever nothing else was to do.

With A5, everything is events based, so you would do something like:

al_install_timer(1.0 / FPS);

...

while (1) {
    al_wait_for_event(queue, &event);

    /* handle input events */

    if (event.type == ALLEGRO_EVENT_TIMER) {
        handle_game_tick();
        need_redraw = true;
    }

    if (need_draw && al_event_queue_is_empty(queue)) {
        render_last_frame();
        need_redraw = false;
    }
}

Once you understand what is happening, this is a much cleaner solution. Whenever any input occurs, you get an event instantly. So you could have more fine-controlled input handling than in A4, or otherwise just collect input until the next game tick so it would work like A4. Similar, you get an event now at the exact time the timer ticks. There is no more need for something like rest(1), as your program will not even run as long as there are no events (al_wait_for_event will only return once there is a new event). There are again various ways to deal with frame skipping and other things - everything possible in A4 is still possible in A5.

Graphics

A5 uses floating point coordinates. While it needs some time to get used to, it often leads to clearer code, and is the only way to properly deal with sub-pixel accuracy (which was impossible with A4).

A detailed explanation can be found at Grid Coordinates vs. Pixel Coordinates.

All graphics are blitted to the last selected target bitmap, which you can change with:

al_set_target_bitmap( bmp)

General

Some general changes:

  • Colors:
    int color = makecol( r, g, b)
    
    will be
    ALLEGRO_COLOR color = al_map_rgb( r, g, b)
    
  • Components of color:
    r = getr( color); g = getg( color); b = getb( color)
    
    will be
    al_unmap_rgb( color, &r, &g, &b);
    
  • Change pixel:
    putpixel( bmp, x, y, c)
    
    will be
    al_put_pixel( x, y, c)
    
  • Clear bitmaps:
    clear( bmp);
    
    will be
    al_clear_to_color( al_map_rgb( 0, 0, 0))
    

Drawing primitives

You need the primitives addon, to draw following figures. To run the code, you need initialize it before:

#include <allegro5/allegro_primitives.h>
al_init_primitives_addon();

Some rules to convert drawing primitives would be, in short:

  • filled rectangle:
    rectfill(b, x1, y1, x2, y2, c)
    
    will be
    al_draw_filled_rectangle(x1, y1, x2 + 1.f, y2 + 1.f, c)
    
  • outlined rectangle with width 1:
    rect(b, x1, y1, x2, y2, c)
    
    will be
    al_draw_rectangle(x1 + 0.5f, y1 + 0.5f, x2 + 0.5f, y2 + 0.5, c, 1.f)
    
  • horizontal line with width 1:
    line(b, x1, y1, x2, y2, c)
    
    will be
    al_draw_line(x1, y1 + 0.5f, x2, y2 + 0.5f, c, 1.f)
    
  • vertical line with width 1:
    line(b, x1, y1, x2, y2, c)
    
    will be
    al_draw_line(x1 + 0.5f, y1, x2 + 0.5f, y2, c, 1.f)
    

For all other lines (not horizontal/vertical) there usually no 1:1 way to draw them as it depends on the specific implementation of the line drawer used in A4. Using the special width of 0 may be close but likely have a few missing or extra pixels on some GPUs (as in A5 all drawing is done by the GPU).

Bitmaps

BITMAP is changed to ALLEGRO_BITMAP

  • Blitting:
    blit( src, dst, sx, sy, dx, dy, sw, sh)
    
    will be
    al_draw_bitmap_region( src, sx, sy, sw, sh, dx, dy, 0)
    
    or
    al_draw_bitmap( src, dx, dy, 0) // if sx = 0, sy = 0, sw = src->w and sh = src->h
    
  • Width of bitmap:
    width = bmp->w
    
    will be
    width = al_get_bitmap_width( bmp)
    
  • Height of bitmap:
    height = bmp->h
    
    will be
    height = al_get_bitmap_height( bmp)
    

Fonts and Text

FONT is changed to ALLEGRO_FONT

  • Drawing text:
    textout_ex( bmp, fnt, text, x, y, c, bg)
    
    will be
    al_draw_text( fnt, c, x, y, 0, text)
    
  • Drawing centered text:
    textout_centre_ex( bmp, fnt, text, x, y, c, bg)
    
    will be
    al_draw_text( font, c, x, y, ALLEGRO_ALIGN_CENTRE, text)
    
  • Width of text:
    text_length( fnt, text)
    
    will be
    al_get_text_width( fnt, text)
    

Conversion

Allegro 4 Allegro 5
clear( bmp) al_clear_to_color( al_map_rgb( 0, 0, 0))
putpixel( bmp, x, y, c) al_put_pixel( x, y, c)
blit( src, dst, 0, 0, dx, dy, src->w, src->h) al_draw_bitmap( src, dx, dy, 0)
blit( src, dst, sx, sy, dx, dy, sw, sh) al_draw_bitmap_region( src, sx, sy, sw, sh, dx, dy, 0)
line( bmp, x1, y1, x2, y2, c) al_draw_line( x1 + 0.5f, y1 + 0.5f, x2 + 0.5f, y2 + 0.5f, c, 1.f)
rect( bmp, x1, y1, x2, y2, c) al_draw_rectangle( x1 + 0.5f, y1 + 0.5f, x2 + 0.5f, y2 + 0.5, c, 1.f)
rectfill( bmp, x1, y1, x2, y2, c) al_draw_filled_rectangle( x1, y1, x2 + 1.f, y2 + 1.f, c)
circle( bmp, cx, cy, rad, c) al_draw_circle( cx + 0.5f, cy + 0.5f, rad, c, 1.f)
circlefill( bmp, cx, cy, rad, c) al_draw_filled_circle( cx + 0.5f, cy + 0.5f, rad, c)
textout_ex( bmp, fnt, text, x, y, c, -1) al_draw_text( fnt, c, x, y, 0, text)
textout_centre_ex( bmp, fnt, text, x, y, c, -1) al_draw_text( fnt, c, x, y, ALLEGRO_ALIGN_CENTRE, text)
text_length( fnt, text) al_get_text_width( fnt, text)
makecol( r, g, b) al_map_rgb( r, g, b)

Sound

In A5 there is now an explicit distinction between a (hardware-)voice and a (software-)mixer so you can decide to stream sound directly to the hardware if you want or let Allegro do some mixing. Usually you likely don't care and just use the defaults, then play samples and stream music. For samples you still can get full control of how many are played, like with A4's voices. The simple API looks something like:

al_install_audio(ALLEGRO_AUDIO_DRIVER_AUTODETECT);
al_reserve_samples(8);

/* Load a sample. */
sample = al_load_sample("ding.ogg");

/* Play it. */
al_play_sample(sample, 1, 0, 1, ALLEGRO_PLAYMODE_ONCE, NULL);

A4.2, A4.4 and A5.0 on same system

Working with legacy code in Linux

When working with legacy code, from 4.2.x series, or 4.4.x series, and porting to 5.0.x series, you probably will want to have both series installed to test your code until you finish porting.

In linux, is not easy to install 4.2.x, 4.4.x, and 5.0.x together at the same time.

But, you can install: 4.2.x and 5.0.x, OR , 4.4.x and 5.0.x at the same time easy.

The differences are, basically, that 4.4 has plugins builtin, and in 5, the libraries stay separate. (You still have a monolith option).

Also, Allegro 4 doesn't really work with recent OSXs.