Allegro.cc - Online Community

Allegro.cc Forums » Game Design & Concepts » [A4] Old tile smooth scrolling code by Elias Pschernig from 1999

This thread is locked; no one can reply to it. rss feed Print
[A4] Old tile smooth scrolling code by Elias Pschernig from 1999
Neil Roy
Member #2,229
April 2002
avatar

I was digging through some old code and found this by Elias Pschernig from back in 1999. I only really had to change 1 line of code for this to be up to date with Allegro 4 (the textprintf line).

I just thought that this was a nice little gem that shows how to code large 2D tile scrolling maps for anyone looking for help in this area. This does it all, creates it's own bitmaps for the example, uses keyboard or mouse and has a really large map.

Hope this helps someone, I hate to see this kind of thing go to waste, it really helped me understand how to do it back then. (note: I doubt that Elias' email is still at geocities, but I left it in for kicks ;))

Edit: Oh yes, I also changed the mouse code so that when you press the left mouse button it acts more like you are dragging the map around (it was the opposite before). So now with the left mouse button clicked the map can be "dragged", which is kind of a kewl effect.

Edit2: Changed the -1 in textprintf_ex to makecol(0,0,0)... oops. ;) (for background colour).

#SelectExpand
1/* 2// Elias Pschernig 1999 3*/ 4 5/* 6// tile.c 7*/ 8 9/* 10// displays a tile map 11*/ 12 13/* 14// extreme speed increase can be achieved by 15// using hardware acceleration (vbeaf driver) 16// and replacing 'create_bitmap' with 17// 'create_video_bitmap' throughout the program 18// 19// speed increase will also result from using 20// rle_sprites for the tiles instead of bitmaps 21*/ 22 23#include <allegro.h> 24#include <stdio.h> 25 26#define COLOR_BITS 16 // video mode 27 28#define NUM_OF_TILES 100 // possible different tiles 29 30#define MAP_WIDTH 100 // how big do you want the 31#define MAP_HEIGHT 100 // test map ? 32 33#define TILE_WIDTH 32 // how big is one (square) tile ? 34 35#define WIN_L 0 36#define WIN_T 0 37#define WIN_R 640 38#define WIN_B 359 39 40#define FPS 60 41// how many frames to do each second... 42// i don't really understand all the timer things 43// i guess there are better ways of keeping sync 44// between time and frames 45// i just set this FPS, then set 'starttime' and 46// keep the number of frames and the time in sync... 47 48int gametime=0; // the application timer 49void gametimer() { 50 gametime++; 51} 52int starttime; // starttime 53int pframes,gframes; // counts the different frames 54int second_ago_gframetime;// used to calculate the fps 55int second_ago_gframes; 56int fps; // this are the grafix frames 57 // the process frames always run 58 // as defined in FPS 59 60BITMAP *tile[NUM_OF_TILES]; // this is the tiles 61BITMAP *map=0; // the map 62int scrollx=0,scrolly=0; // the pixel position of upper left 63 // screen corner inside map 64 65// load one single tile 66// (normally you use datafiles) 67void loadtile(int n) { 68 tile[n]=create_video_bitmap(TILE_WIDTH,TILE_WIDTH); 69 switch(n) { 70 default: clear_to_color(tile[n],makecol(0,0,0)); break; 71 case 1: 72 clear_to_color(tile[n],makecol(255,0,0)); 73 circlefill( 74 tile[n], 75 TILE_WIDTH/2, 76 TILE_WIDTH/2, 77 TILE_WIDTH/2, 78 makecol(255,255,0)); 79 break; 80 case 2: 81 clear_to_color(tile[n],makecol(255,255,0)); 82 triangle( 83 tile[n], 84 TILE_WIDTH/2,0, 85 0,TILE_WIDTH-1, 86 TILE_WIDTH-1,TILE_WIDTH-1, 87 makecol(255,0,0)); 88 break; 89 } 90} 91 92// load all the tiles 93// (normally this is already done by loading datafile) 94void loadtiles() { 95 int n; 96 for(n=0;n<NUM_OF_TILES;n++) { 97 loadtile(n); 98 } 99} 100 101// load the map 102// (normally datafile...) 103void loadmap() { 104 int x,y; 105 map=create_bitmap_ex(8,MAP_WIDTH,MAP_HEIGHT); 106 clear_to_color(map,0); 107 108 for(y=0;y<MAP_HEIGHT;y++) { 109 for(x=0;x<MAP_WIDTH;x++) { 110 int r=rand()%4; 111 if(r==1) putpixel(map,x,y,1); 112 if(r==2) putpixel(map,x,y,2); 113 } 114 } 115} 116 117// display part of the tilemap, 118// starting at scrollx, scrolly as 119// upper left corner and filling 120// the parameter bitmap 'screen' 121// screen in most cases will be 122// a subbitmap of the screen or 123// of a doublebuffer 124 125void displaymap(BITMAP *screen) { 126 int screenx,screeny; 127 int mapx,mapy; 128 int screenleft,mapleft; 129 130 mapleft=scrollx/TILE_WIDTH; 131 mapy=scrolly/TILE_WIDTH; 132 screenleft=mapleft*TILE_WIDTH-scrollx; 133 screeny=mapy*TILE_WIDTH-scrolly; 134 135 while(screeny<screen->h) { 136 mapx=mapleft; 137 screenx=screenleft; 138 while(screenx<screen->w) { 139 140 {int t=getpixel(map,mapx,mapy); 141 if(t>=0) 142 blit(tile[t],screen,0,0,screenx,screeny,TILE_WIDTH,TILE_WIDTH); 143 } 144 145 mapx++; 146 screenx+=TILE_WIDTH; 147 } 148 mapy++; 149 screeny+=TILE_WIDTH; 150 } 151} 152 153// do all sort of things that need to run at 154// a constant time, like user input, 155// processing of objects... 156void process() { 157 int k; 158 int xm,ym; 159 160 while(keypressed()) { 161 k=readkey(); 162 } 163 164 if(key[KEY_LEFT]) scrollx-=4; 165 if(key[KEY_RIGHT]) scrollx+=4; 166 if(key[KEY_UP]) scrolly-=4; 167 if(key[KEY_DOWN]) scrolly+=4; 168 169 170 get_mouse_mickeys(&xm,&ym); 171 if(mouse_b) { 172 scrollx-=xm; 173 scrolly-=ym; 174 } 175 176 177 if (mouse_x >= WIN_R-1) scrollx+=8; 178 if (mouse_x <= WIN_L+1) scrollx-=8; 179 if (mouse_y >= SCREEN_H-1) scrolly+=8; 180 if (mouse_y <= WIN_T) scrolly-=8; 181 182 if(scrollx<0) scrollx=0; 183 if(scrolly<0) scrolly=0; 184 if(scrollx>MAP_WIDTH*TILE_WIDTH-(WIN_R-WIN_L+1)) 185 scrollx=MAP_WIDTH*TILE_WIDTH-(WIN_R-WIN_L+1); 186 if(scrolly>MAP_HEIGHT*TILE_WIDTH-(WIN_B-WIN_T+1)) 187 scrolly=MAP_HEIGHT*TILE_WIDTH-(WIN_B-WIN_T+1); 188 189} 190 191void grafix(BITMAP *myscreen) { 192 BITMAP *win; 193 win=create_sub_bitmap(myscreen, 194 WIN_L,WIN_T,WIN_R,WIN_B); 195 196 displaymap(win); 197 198 destroy_bitmap(win); 199 200 textprintf_ex(screen,font,0,360,makecol(255,255,255), makecol(0,0,0), "fps %2d ",fps); 201} 202 203 204// the main program 205int main() { 206 BITMAP *double_buffer; 207 208 allegro_init(); 209 install_keyboard(); 210 install_timer(); 211 install_int(gametimer,10); 212 install_mouse(); 213 214 set_color_depth(COLOR_BITS); 215 {int s=set_gfx_mode(GFX_AUTODETECT,640,480,0,0); 216 if(s) { 217 printf("Allegro couldnt set %d bit mode\n",COLOR_BITS); 218 printf("recompile and use another setting\n"); 219 printf("sorry\n"); 220 return 1; 221 } 222 } 223 double_buffer = create_video_bitmap(WIN_R-WIN_L, WIN_B-WIN_T); 224 clear(double_buffer); 225 226 loadtiles(); 227 loadmap(); 228 229 { 230 starttime=second_ago_gframetime=gametime; 231 pframes=gframes=second_ago_gframes=0; 232 while(!key[KEY_ESC]) { 233 234 if(pframes*100<(gametime-starttime)*FPS) { 235 process(); 236 pframes++; 237 } else { 238 239// don't pass screen in a real program here, but 240// use a doublebuffer or pageflipping or something 241// to avoid bad display quality 242 243 grafix(double_buffer); 244 vsync(); 245 show_mouse(double_buffer); 246 blit(double_buffer, screen, 0, 0, WIN_L, WIN_T, WIN_R-WIN_L, WIN_B-WIN_T); 247 show_mouse(screen); 248 gframes++; 249 } 250 251 // fps->how many grafix frames in last second ? 252 if(gametime-second_ago_gframetime>=100) { 253 second_ago_gframetime+=100; 254 fps=gframes-second_ago_gframes; 255 second_ago_gframes=gframes; 256 } 257 258 } 259 } 260 261 destroy_bitmap(double_buffer); 262 263 printf("email: eliaspschernig@geocities.com? LOL\n"); 264 return 0; 265} 266 267END_OF_MAIN();

Elias
Member #358
May 2000

Nooo, how embarassing :P It's not supposed to work with Allegro > 3.12 btw. because video bitmaps share memory with screen - you can't use both in the same code.

--
"Either help out or stop whining" - Evert

Neil Roy
Member #2,229
April 2002
avatar

Don't lecture to me, you're the one who wrote it! :P And it works fine. heheheh, should update it and make an Allegro 5 example (properly coded this time! ;D) ;D

It was a blast from the past, but pretty kewl little example I thought.

Mark Oates
Member #1,146
March 2001
avatar

{"name":"603456","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/1\/213205673d15a2b517f1057709ccc758.jpg","w":400,"h":373,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/1\/213205673d15a2b517f1057709ccc758"}603456

Elias
Member #358
May 2000

Neil Roy said:

heheheh, should update it and make an Allegro 5 example

Well, you left me no choice :)

#SelectExpand
1/* Simple Allegro 5 tilemap example from allegro.cc: 2 * 3 * http://www.allegro.cc/forums/thread/606482 4 * 5 * Also see here for more info: 6 * 7 * http://wiki.allegro.cc/index.php?title=Allegro_5_Tutorial 8 * 9 * Place fixed_font.tga and icon.tga from the Allegro 5 examples/data 10 * folder next to the .exe and there will be an FPS counter and an 11 * icon. 12 * 13 * Left mouse = Pan 14 * Right mouse = Rotozoom 15 * Esc = Quit 16 */ 17 18#include <stdio.h> 19#include <stdlib.h> 20#include <math.h> 21 22#include "allegro5/allegro.h" 23#include "allegro5/allegro_image.h" 24#include "allegro5/allegro_primitives.h" 25#include "allegro5/allegro_font.h" 26 27/* Our window. */ 28ALLEGRO_DISPLAY *display; 29/* Our tiles atlas. */ 30ALLEGRO_BITMAP *tiles; 31/* Our tilemap. */ 32int tile_map[100 * 100]; 33/* Keep track of pressed mouse button. */ 34int mouse; 35/* Camera parameters. */ 36float zoom = 1.0, rotate; 37float scroll_x, scroll_y; 38/* Our icon and font. */ 39ALLEGRO_BITMAP *icon; 40ALLEGRO_FONT *font; 41/* Simple FPS counter. */ 42int fps, fps_accum; 43double fps_time; 44 45/* Places a single tile into the tile atlas. 46 * Normally you would load the tiles from a file. 47 */ 48void tile_draw(int i, float x, float y, float w, float h) { 49 ALLEGRO_COLOR black = al_map_rgb(0, 0, 0); 50 ALLEGRO_COLOR yellow = al_map_rgb(255, 255, 0); 51 ALLEGRO_COLOR red = al_map_rgb(255, 0, 0); 52 switch (i) { 53 case 0: 54 al_draw_filled_rectangle(x, y, x + w, y + h, black); 55 break; 56 case 1: 57 al_draw_filled_rectangle(x, y, x + w, y + h, red); 58 al_draw_filled_circle(x + w * 0.5, y + h * 0.5, w * 0.475, 59 yellow); 60 break; 61 case 2: 62 al_draw_filled_rectangle(x, y, x + w, y + h, yellow); 63 al_draw_filled_triangle(x + w * 0.5, y + h * 0.125, 64 x + w * 0.125, y + h * 0.875, 65 x + w * 0.875, y + h * 0.875, red); 66 break; 67 case 3: 68 al_draw_filled_rectangle(x, y, x + w, y + h, black); 69 if (icon) 70 al_draw_scaled_bitmap(icon, 0, 0, 48, 48, 71 x, y, w, h, 0); 72 break; 73 } 74} 75 76/* Creates the tiles and a random 100x100 map. */ 77void tile_map_create(void) { 78 int i; 79 int x, y; 80 /* Create the tile atlas. */ 81 tiles = al_create_bitmap(1024, 1024); 82 al_set_target_bitmap(tiles); 83 al_clear_to_color(al_map_rgba(0, 0, 0, 0)); 84 for (i = 0; i < 4; i++) { 85 /* We draw the tiles a bit bigger (66x66 instead of 64x64) 86 * to account for the linear filtering. Normally just leaving 87 * the border transparent for sprites or repeating the border 88 * for tiling tiles should work well. 89 */ 90 tile_draw(i, i * 66, 0, 66, 66); 91 } 92 al_set_target_backbuffer(display); 93 94 /* Create the random map. */ 95 for (y = 0; y < 100; y++) { 96 for (x = 0; x < 100; x++) { 97 tile_map[x + y * 100] = rand() % 4; 98 } 99 } 100 101 /* Center of map. */ 102 scroll_x = 100 * 32 / 2; 103 scroll_y = 100 * 32 / 2; 104} 105 106/* Draws the complete map. */ 107void tile_map_draw(void) { 108 int x, y; 109 ALLEGRO_TRANSFORM transform; 110 float w, h; 111 112 w = al_get_display_width(display); 113 h = al_get_display_height(display); 114 115 /* Initialize transformation. */ 116 al_identity_transform(&transform); 117 /* Move to scroll position. */ 118 al_translate_transform(&transform, -scroll_x, -scroll_y); 119 /* Rotate and scale around the center first. */ 120 al_rotate_transform(&transform, rotate); 121 al_scale_transform(&transform, zoom, zoom); 122 /* Move scroll position to screen center. */ 123 al_translate_transform(&transform, w * 0.5, h * 0.5); 124 /* All subsequent drawing is transformed. */ 125 al_use_transform(&transform); 126 127 al_clear_to_color(al_map_rgb(0, 0, 0)); 128 129 al_hold_bitmap_drawing(1); 130 for (y = 0; y < 100; y++) { 131 for (x = 0; x < 100; x++) { 132 int i = tile_map[x + y * 100]; 133 float u = 1 + i * 66; 134 float v = 1; 135 al_draw_scaled_bitmap(tiles, u, v, 64, 64, 136 x * 32, y * 32, 32, 32, 0); 137 } 138 } 139 al_hold_bitmap_drawing(0); 140 141 al_identity_transform(&transform); 142 al_use_transform(&transform); 143} 144 145int main(void) { 146 ALLEGRO_TIMER *timer; 147 ALLEGRO_EVENT_QUEUE *queue; 148 bool redraw = true; 149 150 srand(time(NULL)); 151 152 /* Init Allegro 5 + addons. */ 153 al_init(); 154 al_init_image_addon(); 155 al_init_primitives_addon(); 156 al_init_font_addon(); 157 al_install_mouse(); 158 al_install_keyboard(); 159 160 /* Create our window. */ 161 al_set_new_display_flags(ALLEGRO_RESIZABLE); 162 display = al_create_display(640, 480); 163 al_set_window_title(display, "Allegro 5 Tilemap Example"); 164 165 /* The example will work without those, but there will be no 166 * FPS display and no icon. 167 */ 168 font = al_load_font("fixed_font.tga", 0, 0); 169 icon = al_load_bitmap("icon.tga"); 170 if (icon) 171 al_set_display_icon(display, icon); 172 173 al_set_new_bitmap_flags(ALLEGRO_MIN_LINEAR | ALLEGRO_MAG_LINEAR); 174 175 tile_map_create(); 176 177 timer = al_create_timer(1.0 / 60); 178 queue = al_create_event_queue(); 179 al_register_event_source(queue, al_get_keyboard_event_source()); 180 al_register_event_source(queue, al_get_mouse_event_source()); 181 al_register_event_source(queue, al_get_display_event_source(display)); 182 al_register_event_source(queue, al_get_timer_event_source(timer)); 183 al_start_timer(timer); 184 185 while (1) { 186 ALLEGRO_EVENT event; 187 al_wait_for_event(queue, &event); 188 189 if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) 190 break; 191 if (event.type == ALLEGRO_EVENT_KEY_DOWN) { 192 if (event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) 193 break; 194 } 195 if (event.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) { 196 mouse = event.mouse.button; 197 } 198 if (event.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) { 199 mouse = 0; 200 } 201 if (event.type == ALLEGRO_EVENT_MOUSE_AXES) { 202 /* Left button scrolls. */ 203 if (mouse == 1) { 204 float x = event.mouse.dx / zoom; 205 float y = event.mouse.dy / zoom; 206 scroll_x -= x * cos(rotate) + y * sin(rotate); 207 scroll_y -= y * cos(rotate) - x * sin(rotate); 208 } 209 /* Right button zooms/rotates. */ 210 if (mouse == 2) { 211 rotate += event.mouse.dx * 0.01; 212 zoom += event.mouse.dy * 0.01 * zoom; 213 } 214 zoom += event.mouse.dz * 0.1 * zoom; 215 if (zoom < 0.1) zoom = 0.1; 216 if (zoom > 10) zoom = 10; 217 } 218 if (event.type == ALLEGRO_EVENT_TIMER) 219 redraw = true; 220 if (event.type == ALLEGRO_EVENT_DISPLAY_RESIZE) { 221 al_acknowledge_resize(display); 222 redraw = true; 223 } 224 225 if (redraw && al_is_event_queue_empty(queue)) { 226 double t = al_get_time(); 227 tile_map_draw(); 228 if (font) { 229 al_draw_filled_rounded_rectangle(4, 4, 100, 30, 230 8, 8, al_map_rgba(0, 0, 0, 200)); 231 al_draw_textf(font, al_map_rgb(255, 255, 255), 232 54, 8, ALLEGRO_ALIGN_CENTRE, "FPS: %d", fps); 233 } 234 al_flip_display(); 235 fps_accum++; 236 if (t - fps_time >= 1) { 237 fps = fps_accum; 238 fps_accum = 0; 239 fps_time = t; 240 } 241 } 242 } 243 return 0; 244}

--
"Either help out or stop whining" - Evert

Neil Roy
Member #2,229
April 2002
avatar

Whoa, that's impressive. Love the mouse wheel zoom. :)

And coded properly this time! ;D

We'll see what you think of this new code in 12 years. ;)

Mark Oates
Member #1,146
March 2001
avatar

Neil Roy
Member #2,229
April 2002
avatar

This new example really shows off the new Allegro. No keyboard needed, completely mouse controlled, re-sizable, and closed with the mouse properly with some very easy to use code. Allegro has come a long way since 1999. :)

Good work.

Thomas Fjellstrom
Member #476
June 2000
avatar

A more complex example would only draw the tiles that are visible. But thats pretty decent.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Inquisiteur
Member #12,428
December 2010

Thanks very much for this example, helped me understand a few useful things.

I wonder however why the code compilation takes a few seconds (5/6 seconds before launch) for a such lightweight code ?

Is this because of the number of tiles used ?

Go to: