|
[A4] Old tile smooth scrolling code by Elias Pschernig from 1999 |
Neil Roy
Member #2,229
April 2002
|
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). 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 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. -- |
Neil Roy
Member #2,229
April 2002
|
Don't lecture to me, you're the one who wrote it! And it works fine. heheheh, should update it and make an Allegro 5 example (properly coded this time! ) It was a blast from the past, but pretty kewl little example I thought. --- |
Mark Oates
Member #1,146
March 2001
|
{"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"} -- |
Elias
Member #358
May 2000
|
Neil Roy said: heheheh, should update it and make an Allegro 5 example Well, you left me no choice 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}
-- |
Neil Roy
Member #2,229
April 2002
|
Whoa, that's impressive. Love the mouse wheel zoom. And coded properly this time! We'll see what you think of this new code in 12 years. --- |
Mark Oates
Member #1,146
March 2001
|
kewl stuff -- |
Neil Roy
Member #2,229
April 2002
|
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
|
A more complex example would only draw the tiles that are visible. But thats pretty decent. -- |
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 ? |
|