-
Notifications
You must be signed in to change notification settings - Fork 8
Porting Allegro 4 to 5
Incomplete: This article is incomplete and needs some revision or has some pending TODOs. Please help by editing and finishing it. When the article becomes complete, you may remove this block.
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. See the source.
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.
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.
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.
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 in the manual.
All graphics are 'blitted' (in reality with GPU-accelerated draws) to the last selected target bitmap, which you can change with:
al_set_target_bitmap(bmp)
Some general changes:
- Colors:
int color = makecol(r, g, b)
will beALLEGRO_COLOR color = al_map_rgb(r, g, b)
- Components of color:
r = getr(color); g = getg(color); b = getb(color)
will beal_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 beal_clear_to_color(al_map_rgb(0, 0, 0))
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 beal_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 beal_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 beal_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 beal_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).
BITMAP
is changed to ALLEGRO_BITMAP
- Blitting:
blit(src, dst, sx, sy, dx, dy, sw, sh)
will beal_draw_bitmap_region(src, sx, sy, sw, sh, dx, dy, 0)
oral_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 bewidth = al_get_bitmap_width(bmp)
- Height of bitmap:
height = bmp->h
will beheight = al_get_bitmap_height(bmp)
FONT
is changed to ALLEGRO_FONT
- Drawing text:
textout_ex(bmp, fnt, text, x, y, c, bg)
will beal_draw_text(fnt, c, x, y, 0, text)
- Drawing centered text:
textout_centre_ex(bmp, fnt, text, x, y, c, bg)
will beal_draw_text(font, c, x, y, ALLEGRO_ALIGN_CENTRE, text)
- Width of text:
text_length(fnt, text)
will beal_get_text_width(fnt, text)
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) |
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);
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.