|
Isometric tile map with height? (think simcity) |
ITAmember
Member #10,095
August 2008
|
So here is my current code 1#include <allegro.h>
2
3#define BLACK makecol(0,0,0)
4
5#define WHITE makecol(255,255,255)
6
7#define TILE_MAP_SIZE 255
8
9#define TILE_X 64
10
11#define TILE_Y 32
12
13#define MAP_Y (TILE_MAP_SIZE * TILE_Y)
14
15#define MAP_X (TILE_MAP_SIZE * TILE_X)
16
17#define ORIGIN_X (MAP_X / 2 - TILE_X / 2)
18
19#define ORIGIN_Y 0
20
21#define SCREEN_X 800
22
23#define SCREEN_Y 600
24
25
26
27BITMAP *iso_tile;
28
29BITMAP *buffer;
30
31
32
33int main(void)
34
35{
36
37 // Framerate counter varibles
38
39 int counter = 0;
40
41 int start = 0;
42
43 int ticks = 0;
44
45 int framerate = 0;
46
47
48
49 // map position varibles
50
51 int x=7000, y=3500;
52
53
54
55 // tile drawing boundrys for the buffer
56
57 int top, bottom, right, left;
58
59
60
61 // init
62
63 allegro_init();
64
65 set_color_depth(32);
66
67 if(set_gfx_mode(GFX_AUTODETECT_WINDOWED, SCREEN_X, SCREEN_Y, 0, 0))
68
69 {
70
71 allegro_message(allegro_error);
72
73 return 1;
74
75 }
76
77 iso_tile = load_bitmap("iso_tile.bmp", NULL);
78
79 if(!iso_tile)
80
81 {
82
83 allegro_message("Could not load iso_tile.bmp, aborting");
84
85 return 1;
86
87 }
88
89 buffer = create_bitmap(SCREEN_X, SCREEN_Y);
90
91 if(!buffer)
92
93 {
94
95 allegro_message("Could not create the buffer, what is wrong with your system???");
96
97 return 1;
98
99 }
100
101 rectfill(buffer, 0, 0, 640, 480, BLACK);
102
103 install_keyboard();
104
105 install_mouse();
106
107
108
109 // Main loop
110
111 while(!key[KEY_ESC])
112
113 {
114
115 if(key[KEY_RIGHT])
116
117 {
118
119 x += 2;
120
121 }
122
123 if(key[KEY_LEFT])
124
125 {
126
127 x -= 2;
128
129 if(x < 0)
130
131 {
132
133 x = 0;
134
135 }
136
137 }
138
139 if(key[KEY_UP])
140
141 {
142
143 y -= 2;
144
145 if(y < 0)
146
147 {
148
149 y = 0;
150
151 }
152
153 }
154
155 if(key[KEY_DOWN])
156
157 {
158
159 y += 2;
160
161 }
162
163
164
165 // get the tile drawing boundrys for the buffer
166
167 // helpfull URL http://www.goldenstudios.or.id/forum/archive/index.php/thread-319.html
168
169 top = ((y / TILE_Y) - ((x + SCREEN_X) - (TILE_MAP_SIZE * TILE_X / 2)) / TILE_X);
170
171 if(top < 0)
172
173 {
174
175 top = 0;
176
177 }
178
179 else if(top > TILE_MAP_SIZE)
180
181 {
182
183 top = TILE_MAP_SIZE;
184
185 }
186
187 left = ((y / TILE_Y) + (x - (TILE_MAP_SIZE * TILE_X / 2)) / TILE_X);
188
189 if(left < 0)
190
191 {
192
193 left = 0;
194
195 }
196
197 else if(left > TILE_MAP_SIZE)
198
199 {
200
201 left = TILE_MAP_SIZE;
202
203 }
204
205 bottom = (((y + SCREEN_Y) / TILE_Y) - (x - (TILE_MAP_SIZE * TILE_X / 2)) / TILE_X);
206
207 if(bottom < 0)
208
209 {
210
211 bottom = 0;
212
213 }
214
215 else if(bottom > TILE_MAP_SIZE)
216
217 {
218
219 bottom = TILE_MAP_SIZE;
220
221 }
222
223 right = (((y + SCREEN_Y) / TILE_Y) + ((x + SCREEN_X) - (TILE_MAP_SIZE * TILE_X / 2)) / TILE_X);
224
225 if(right < 0)
226
227 {
228
229 right = 0;
230
231 }
232
233 else if(right > TILE_MAP_SIZE)
234
235 {
236
237 right = TILE_MAP_SIZE;
238
239 }
240
241 // Draw the map
242
243 for(int draw_x = left; draw_x < right + 1; draw_x++)
244
245 {
246
247 for(int draw_y = top; draw_y < bottom + 1; draw_y++)
248
249 {
250
251 draw_sprite(buffer, iso_tile, ((ORIGIN_X - draw_y * 32) + draw_x * 32) - x,
252
253 ((draw_y + draw_x) * 16) - y);
254
255 }
256
257 }
258
259
260
261 // Framerate counter
262
263 ticks++;
264
265 if (clock() > start + 1000)
266
267 {
268
269 counter++;
270
271 start = clock();
272
273 framerate = ticks;
274
275 ticks = 0;
276
277 }
278
279
280
281 // write the framerate to the buffer
282
283 textprintf_ex(buffer, font, 1, 1, WHITE, -1, "FPS = %i", framerate);
284
285
286
287 // write the map position values
288
289 textprintf_ex(buffer, font, 1, 11, WHITE, -1, "x = %i, y = %i", x, y);
290
291
292
293 // write the tile drawing boundrys to the buffer
294
295 textprintf_ex(buffer, font, 1, 21, WHITE, -1, "top = %i, bottom = %i", top, bottom);
296
297 textprintf_ex(buffer, font, 1, 31, WHITE, -1, "left = %i, right = %i", left, right);
298
299
300
301 draw_sprite(screen, buffer, 0, 0);
302
303 }
304
305
306
307 // Clean up
308
309 destroy_bitmap(buffer);
310
311 destroy_bitmap(iso_tile);
312
313 allegro_exit();
314
315 return 0;
316
317}
318
319END_OF_MAIN()
Here is the iso_tile.bmp. It is 64/32 pixels but I plan to switch to 32/16 pixels for the tiles. My first question is is there anything that I'm really messing up? Like forgeting to do something, making something easy really hard, etc. This code works just fine but is on the slow side, only 130 FPS on a 2.0GHz P4 and I'm targeting a 500MHz AMD K6. Not the best. Right now the code tries to draw the tiles that the screen shows, it acctualy draws a large diamond around the screen with large triangles above and below the screen that cannot be seen. But, this is besides the point as I'm going to have to redo that code. What I want is an isometric tiled map that will show tiles of different elevations, think simcity/rollercoaster tycoon/sim isle, the list goes on. There are 4 problems that I see. 1. Do I store the elevation for corners or the actual tiles? I don't want somebody just to write up a function that does that and post it, I want to try to figure it out as much as I can. All I'm really looking for is advice, formulas, and feedback on my code. Thanks in advance |
Kris Asick
Member #1,424
July 2001
|
ONLY 130 FPS at 32 bit colour, non-accelerated, 800x600 resolution? How much higher do you need it? BTW: 32-bit colour on anything less than 1 GHz probably isn't going to run very fast if you do full screen buffering. You may want to think about scaling down to 16-bit or even 8-bit if possible. --- Kris Asick (Gemini) |
Schyfis
Member #9,752
May 2008
|
30-60 FPS is enough for most games. ________________________________________________________________________________________________________ |
Kris Asick
Member #1,424
July 2001
|
...OK, I was half asleep when I made my reply, so I should've added some advice on the questions you asked too. But I would just like to point out that while I was still working on a 1 GHz machine, doing full screen double buffering at 32-bit colour and 640x480 resolution on my system, with very little going on since this was an editor, was only reaching about 30~40 FPS. 32-bit colour is simply VERY demanding for a low-end system and no hardware acceleration. If you want it to run at that colour depth on a 500 MHz system, don't expect anything higher than 20 FPS unless you skip on doing buffering. To answer your other questions though... 1. It depends. If you're doing the terrain like in Populous or SimCity, it would be better to store the elevation points in its own grid. If you're doing the terrain like in Rollercoaster Tycoon, then yes, each tile should track its own corners. Remembering too that no matter which approach you take, you need to be ready graphically for each of the 16 possible combinations. 2. Store it. Usually, the rule of thumb is: The more memory you use, the less time you need to waste calculating everything. 3. This is perhaps the trickiest part of any isometric engine. The easy way out of course is to just draw a square section, like most of the Populous games do. (Populous 2 has a full-screen mode on the PC.) However, when drawing to the edges of the screen, you have to take the height of objects into account. Really tall objects can just pop up if you don't take the downwards distance into consideration. It will also really help if you make yourself a function that translates a pixel on the screen to a particular tile. (You also want to design the function to calculate based on a selectable height level.) 4. Then again... It's just a matter of math really. You know your dimensions and the math you perform to make stuff appear on-screen, so you just have to do the math backwards really. Or, if that's too much of a stretch, here's another idea. For each rendering pass, have a single point or list of points you need to get tile specifics from, then, when rendering a tile, check to see if any of those points are within the bounding area of that tile. If so, compare the point using a getpixel() operation to the bitmap of the tile itself, and if the point is on it, that's the tile... unless a tile of higher height comes along during the rendering pass and overrides this, since tiles closer to the foreground will always be drawn last. --- Kris Asick (Gemini) |
ITAmember
Member #10,095
August 2008
|
Here is a screen shot of what I'm looking for. (note that I did not make that, I got it off a google image search) {"name":"2669338451_791e9d3afb.jpg","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/b\/d\/bd1cae3eb41d0c1bd0927669d8c5f5f4.jpg","w":500,"h":292,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/b\/d\/bd1cae3eb41d0c1bd0927669d8c5f5f4"} Thanks for the help, the first two questions are cleared up now. I wasn't aware how much the 32 bit color slowed down the game, I guess I'm going to switch to 16 bit and see how that runs. From what you said it sounds like the double buffering slows down the game but for some reason I get an extra ~30 FPS with it. Regarding the 3rd question you said something about drawing a square section, I would love to do that but I can't figure that out either. I'll just put some more thought into figureing out how to draw this, but if anyone has more advice that would be great also. As for the 4th question I found this URL http://www.gamedev.net/reference/articles/article2026.asp that uses something called mouse mapping. If someone would give some tips on efficent ways to implement this that would be great, of course that assumes mouse mapping is the best way to do this. It looks slow to me and I'm wondering if there are alternatives. BTW I'm going to use the tilesheet from the above URL until I get around to making my own. Thanks in advance. |
ImLeftFooted
Member #3,935
October 2003
|
ITAmember said: From what you said it sounds like the double buffering slows down the game but for some reason I get an extra ~30 FPS with it. Are you doing any kind of transparentness or tinting effects? Quote: As for the 4th question I found this URL http://www.gamedev.net/reference/articles/article2026.asp that uses something called mouse mapping. If someone would give some tips on efficent ways to implement this that would be great, of course that assumes mouse mapping is the best way to do this. It looks slow to me and I'm wondering if there are alternatives. As I understand this can get very complicated. |
ITAmember
Member #10,095
August 2008
|
Dustin Dettmer said: Are you doing any kind of transparentness or tinting effects? Nope, just what I have in the code. I think it has something to do with Windows locking the bitmaps as you draw. Quote: As I understand this can get very complicated. That's what I got out of it too. I guess I'm going to spend a long time just trying to get the isometric engine working. The logical place to start would be drawing the map, I'm going to start with a 10x10 and see what I can do. Wish me luck. The biggest problem I see is figuring out what part of the map to draw each frame. If anyone has any ideas it would be great. |
Kris Asick
Member #1,424
July 2001
|
ITAmember said: Nope, just what I have in the code. I think it has something to do with Windows locking the bitmaps as you draw. Windows only locks video bitmaps before drawing to them. Regular memory bitmaps are fine. I'm not sure about system bitmaps... Besides, if that were true, the framerate would be even lower. --- Kris Asick (Gemini) |
ITAmember
Member #10,095
August 2008
|
Switching to 16 bit color in fullscreen boosted the framerate to 230 on the P4. Since this is going to be a stratagy game a blazing high framerate is not a big deal, 30 would be just fine. (on the 550MHz machine) |
Oscar Giner
Member #2,207
April 2002
|
You can use 16 bit graphics with dithering. Unless there's a lot of smoooth gradients going on, you'll hardly notice any quality difference. [edit] -- |
ITAmember
Member #10,095
August 2008
|
Umm, what? To be honest I have no idea what you mean. |
Tobias Dammers
Member #2,604
August 2002
|
ITAmember said: This code works just fine but is on the slow side, only 130 FPS You need to find the limiting factor. If your drawing code runs slow, you need to optimize the drawing algorithm. If the blitting/flipping is an obstacle, consider trying a different frame buffering algorithm (brute-force blit, page-flip, triple buffering). If your program spends a lot of time waiting for vsync, then chances are you won't need any optimizing at all, since the vsync() delay depends on the physical refresh rate of the video hardware, not the CPU or RAM. Since you want to run the thing on a 500 MHz machine, consider going 8 bpp. Quote: 3. What is an efficient way to only draw the visible tiles in a huge map? Convert the screen corner coordinates to tile-space; add some tolerance to all 4 edges (one tile should be enough for left, top, and right; for the bottom part, you need to take the maximum tile height into account). Iterate over the resulting sub-set of your tilemap, painting tiles back-to-front. Quote: 4. How do I detect which tile a given pixel is in? (very easy with a "flat" isometric map, not sure how to do it when the tile elevation offsets the tile position)
One way is to disregard heights altogether, and make sure the UI clearly highlights the selected tile. --- |
ITAmember
Member #10,095
August 2008
|
Yeah! I can draw the tile maps with height! {"name":"598354","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/9\/6\/9678f0c53d1b88c6ddbaa4c1049a2623.png","w":799,"h":600,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/9\/6\/9678f0c53d1b88c6ddbaa4c1049a2623"} EDIT: Given this set of mouse map images: {"name":"598355","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/c\/c\/cc8a6e5291e48aa8b408f539860b1e80.png","w":1280,"h":64,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/c\/c\/cc8a6e5291e48aa8b408f539860b1e80"} If I pick a point on one of those tiles and use getpixel to retreive a pixel how do I actauly detect which color is which? Don't I have to use the hexdecimal thing? How do I find out which hexdecimal each color stands for? |
Tobias Dammers
Member #2,604
August 2002
|
Colors as returned by getpixel are just integers. Since the actual color on the mouse map doesn't matter, you can just fill the mouse map areas with sequential numbers, and when reading them back, check for those same numbers. Or, if you use a mouse map that you read from a file, fire up an image editor, find the RGB values for the pixels, and then in allegro, feed them to makecol() to calculate the correct color that would match the one in the mouse map. --- |
ITAmember
Member #10,095
August 2008
|
So I have to make an array with all of those values? Is there an easy way to automate that without having to type in 2048 numbers for each tile? What if I convert the mouse map bitmap into an indexed bitmap? |
Tobias Dammers
Member #2,604
August 2002
|
2048? The mouse map uses no more than 7 colors, so by all means, yes, use indexed colors. Use the first 7 colors (indices 0 through 6), and compare the return value from getpixel against these directly. --- |
ITAmember
Member #10,095
August 2008
|
If I had to type all the data into an array I would have to type 2048 numbers for each tile. (32 * 64 = 2048) I indexed the mouse map and I'm going to try it out. EDIT: I assume that I have to set the color depth to 8 bits before the load and reset it back to 32 after the load? (I'm working in 32 while I develop, the final version will have a lower color depth) |
Tobias Dammers
Member #2,604
August 2002
|
ITAmember said: I assume that I have to set the color depth to 8 bits before the load and reset it back to 32 after the load? (I'm working in 32 while I develop, the final version will have a lower color depth) You can just do set_color_conversion(COLORCONV_NONE) before loading an 8bpp image. Make sure to set it back to COLORCONV_TOTAL or something similar if you need to load other bitmaps that you intend to actually display. --- |
ITAmember
Member #10,095
August 2008
|
Ok I have this function: 1void get_tile_from_point(BITMAP *mouse_map_sheet, tile *map_array, int point_x, int point_y, int *iso_x, int *iso_y)
2{
3 // get the rough tile x and y values without accounting for height or the odd numberd tiles
4 int origin = TILE_MAP_SIZE / 2 - 1;
5 int grid_x = (point_x - TILE_X / 2) / TILE_X;
6 int grid_y = point_y / (TILE_X / 2);
7 *iso_y = grid_y - (grid_x - origin);
8 *iso_x = grid_y + (grid_x - origin);
9
10 // store the fine x and y values
11 int fine_x = (point_x - TILE_X / 2) % TILE_X;
12 int fine_y = (point_y % (TILE_X / 2)) + (TILE_X / 4);
13
14 while(1)
15 {
16 if(*iso_x < 0 || *iso_x > (TILE_MAP_SIZE - 1) || *iso_y < 0 || *iso_y > (TILE_MAP_SIZE - 1))
17 {
18 *iso_x = -1;
19 *iso_y = -1;
20 return;
21 }
22 switch(mouse_map_color_check(mouse_map_sheet, *map_array[*iso_x][*iso_y].tile_type, fine_x,
23 fine_y + *map_array[*iso_x][*iso_y].elevation * PPUE))
24 {
25 case MM_CENTER:
26 {
27 return;
28 }
29 case MM_NW:
30 {
31 *iso_x -= 1;
32 fine_x += TILE_X / 2;
33 fine_y += TILE_Y / 4;
34 break;
35 }
36 case MM_N:
37 {
38 *iso_x -= 1;
39 *iso_y -= 1;
40 fine_y += TILE_Y / 2;
41 break;
42 }
43 case MM_NE:
44 {
45 *iso_y -= 1;
46 fine_x -= TILE_X / 2;
47 fine_y += TILE_Y / 4;
48 break;
49 }
50 case MM_SW:
51 {
52 *iso_y += 1;
53 fine_x += TILE_X / 2;
54 fine_y -= TILE_Y / 4;
55 break;
56 }
57 case MM_S:
58 {
59 *iso_x += 1;
60 *iso_y += 1;
61 fine_y -= TILE_Y / 2;
62 break;
63 }
64 case MM_SE:
65 {
66 *iso_x += 1;
67 fine_x -= TILE_X / 2;
68 fine_y -= TILE_Y / 4;
69 break;
70 }
71 default:
72 {
73 *iso_x = -1;
74 *iso_y = -1;
75 return;
76 }
77 }
78 }
79}
That should work just fine, except for the fact it won't compile. Three of the parimeters are passed by pointer, "tile *map_array", "int *iso_x", and "int *iso_y". iso_x and iso_y are passed by pointer so that I can "return" both of them without doing the same calculations. map_array is the problem. The array is declared in the main function. Here is the struct for the "tile" type: typedef struct { int tile_type; int elevation; } tile; That is in an included header file and more data will be added later. Anyway when I compile here is the error: no match for 'operator[]' in '*((+(((unsigned int)(*iso_x)) * 8u)) + map_array)[*iso_y]' Those two lines are in the switch statement. Any ideas how to fix this? |
amber
Member #6,783
January 2006
|
You need to pass it the right type. You're trying to pass it a tile*, which can represent a single value or a one-dimensional array. It seems like you're trying to pass it a two-dimensional array-- or possibly a two-dimensional array of pointers. I'm actually not sure what you're trying to do. I'd be able to help more if I saw the call to the function, and the declaration of whatever variable you're trying to pass as the tile *map_array parameter. |
ITAmember
Member #10,095
August 2008
|
Declaration is tile map_array[TILE_MAP_SIZE][TILE_MAP_SIZE]; Where TILE_MAP_SIZE is 10. The function call is EDIT 1: Changing void get_tile_from_point(BITMAP *mouse_map_sheet, tile *map_array, int point_x, int point_y, int *iso_x, int *iso_y) to void get_tile_from_point(BITMAP *mouse_map_sheet, tile *map_array[][TILE_MAP_SIZE], int point_x, int point_y, int *iso_x, int *iso_y) Took care of those errors but now it says "tile_type" and "elevation" have not been declared in this statement switch(mouse_map_color_check(mouse_map_sheet, *map_array[*iso_x][*iso_y].tile_type, fine_x, fine_y + *map_array[*iso_x][*iso_y].elevation * PPUE)) Any ideas? I'll keep googling but I doubt I'll find anything. EDIT 2: Changing switch(mouse_map_color_check(mouse_map_sheet, *map_array[*iso_x][*iso_y].tile_type, fine_x, fine_y + *map_array[*iso_x][*iso_y].elevation * PPUE)) to switch(mouse_map_color_check(mouse_map_sheet, *map_array[*iso_x][*iso_y]->tile_type, fine_x, fine_y + *map_array[*iso_x][*iso_y]->elevation * PPUE)) (note the "->"s instead of the "."s) gave me this compile error: invalid type argument of `unary *' One for each of the arrows. EDIT 3: Changing switch(mouse_map_color_check(mouse_map_sheet, *map_array[*iso_x][*iso_y]->tile_type, fine_x, fine_y + *map_array[*iso_x][*iso_y]->elevation * PPUE)) to switch(mouse_map_color_check(mouse_map_sheet, map_array[*iso_x][*iso_y]->tile_type, fine_x, fine_y + map_array[*iso_x][*iso_y]->elevation * PPUE)) (removal of the "*"s from infront of the map_array) Did it! EDIT 4: (crap this is a lot ) Now the function calling line is giving me problems get_tile_from_point(mouse_maps, map_array, mouse_x, mouse_y, &iso_x, &iso_y); gives me 'get_tile_from_point' : cannot convert parameter 2 from 'tile [10][10]' to 'tile *' EDIT 5: (damm compiler's makeing me go in circles ) Ok function declaration void get_tile_from_point(BITMAP *mouse_map_sheet, tile map_array[][TILE_MAP_SIZE], int point_x, int point_y, int *iso_x, int *iso_y) switch statement switch(mouse_map_color_check(mouse_map_sheet, map_array[*iso_x][*iso_y].tile_type, fine_x, fine_y + map_array[*iso_x][*iso_y].elevation * PPUE)) function call get_tile_from_point(mouse_maps, map_array, mouse_x, mouse_y, &iso_x, &iso_y); And after updating the function prototype in the header file (which I forgot to do all those times ) fixed it. Here's the screenshot (sorry about the cursur, I had to draw my own because the PrtScn button doesn't capture it) {"name":"598371","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/c\/c\/ccc45036652699089d18e5392bf96088.png","w":800,"h":600,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/c\/c\/ccc45036652699089d18e5392bf96088"} |
|