How to: tiles in 3dgs

Posted By: Joozey

How to: tiles in 3dgs - 07/25/11 17:38

Hey,

Though I just as well place it here. I'm running against problems with 3dgs when trying to show 80*60 little bitmap tiles on the screen. I want to make a little game like diggers/minecraft/dwarffortress in 2D; dig blocks and search for treasures. So I made a tileset with 8x8 tiles, and using some noise functions to create a 60*80 tiles geological landscape.



I first tried to use panels for each tile, but that soon proved to be very fps-inefficient. Then I used one panel and 80*60 windows on it. That worked significantly better, but 14 fps is not all that great. Using BMP instead of TGA saved me another 3 fps, but what else could I do to improve the framerate to at least 30? Should I approach this way differently? Any ideas?

Thanks,
Joozey
Posted By: Superku

Re: How to: tiles in 3dgs - 07/25/11 18:05

Use an array (or a bitmap, doesn't matter) and use draw_quad to draw bmaps on the specific positions, that works pretty well.
You can declare a BMAP array and draw the bitmaps f.i. as follows, should be self-explanatory:

Code:
BMAP* bmap_tile[MAX_TILES];

for(i = 0; i < SIZE_X; i++) {
	for(j = 0; j < SIZE_Y; j++) {
		
		if(array[i][j] > 0) draw_quad(bmap_tile[array[i][j]-1],vector(i*64,j*64,0),NULL,NULL,NULL,NULL,100,0);
		
	}
	
}


Posted By: ratchet

Re: How to: tiles in 3dgs - 07/25/11 18:31

For any 2D, polygons are better , a method used in other engones for 2D like Unity also !

What will be your new ideas in this game laugh ?
Caus making exactly same ideas gameplays, feels a litlle useles, or can be good to learn !

Posted By: Redeemer

Re: How to: tiles in 3dgs - 07/25/11 18:34

Superku has the right idea but there are a few improvements that could be made. First of all the way he's doing it will cause the terrain to draw itself sideways, and the tiles will be spaced out far too much!

Code:
BMAP* bmap_tile[MAX_TILES];
int x, y;

for(y=0; y<SIZE_Y; y++) {
	for(x=0; x<SIZE_X; x++) {
		if(tilemap[y][x]) draw_quad(bmap_tile[tilemap[y][x]],vector(x<<3,y<<3,0),NULL,NULL,NULL,NULL,100,0);
	}
}



I like to keep as a general rule of thumb that whenever possible, use bit shifts to perform multiplication instead of straight multiplication. This is possible whenever you are multiplying a variable by a number that is a power of 2.

Also you should put the "y" for loop before the "x" for loop to get better caching performance.
Posted By: Superku

Re: How to: tiles in 3dgs - 07/25/11 18:42

Quote:
First of all the way he's doing it will cause the terrain to draw itself sideways, and the tiles will be spaced out far too much!

Could you explain this, if possible in different words?
I think it's pretty obvious that "64" stands for the tile size in my example, and I don't get what you mean with "the terrain to draw itself sideways".
Of course there are more things to consider, f.i. you only draw the part of the array that is potentially visible (in a scrolling game).

Quote:
Also you should put the "y" for loop before the "x" for loop to get better caching performance.

What kind of difference is that supposed to make?
Posted By: Stromausfall

Re: How to: tiles in 3dgs - 07/25/11 19:23

i had a similar problem and used a shader, it worked pretty neat - but i don't really have a clue whether this is a 'good' approach or not (my knowledge about shaders is actually non existent)...the method i used is the following(roughly) :

- create an entity with a skin of the size your landscape needs to have, for example : 80 * 60 pixels
- then use pixel_to_bmap to write a pixel with a color , for example (255, 0, 0) ! this pixel represents a tile ! and (255, 0, 0) is the type of the tile !
- then you only need a shader that reads the pixel value (thus for example 255, 0, 0) and then paints a bmap that represents the tile (like the one you now use as panel element) at the position where the pixel with the value is read...

Posted By: Redeemer

Re: How to: tiles in 3dgs - 07/25/11 19:49

Quote:
Quote:
First of all the way he's doing it will cause the terrain to draw itself sideways, and the tiles will be spaced out far too much!
Could you explain this, if possible in different words?
I think it's pretty obvious that "64" stands for the tile size in my example, and I don't get what you mean with "the terrain to draw itself sideways".

Joozey stated in his first post that he was working with 8x8 tiles, so I naturally assumed that your example would use that size. Also, you were using the "y" variable to reference the tilemap column and the "x" variable to reference the tilemap row, and then you were drawing the tilemap with draw_quad with the "x" parameter as the column and the "y" parameter of the row! Basically, you confused the variables halfway through, which would cause the data to be drawn sideways. I've done it before lots of times.

Quote:
Quote:
Also you should put the "y" for loop before the "x" for loop to get better caching performance.

What kind of difference is that supposed to make?

2D arrays in C are stored in memory with each row of data stored in linear succession. Therefore if you put the for loop for "x" before "y" then you will start referencing row 1, column 1, and with each cycle you will go down one row until you reach column 2. This actually throws the CPU off, because it anticipates you to access the columns in succession, not the rows.

Basically the tilemap might visually look like this:

1, 2, 3, 4,
5, 6, 7, 8,
A, B, C, D

But it is stored in memory like this:

1, 2, 3, 4, 5, 6, 7, 8, A, B, C, D,

And if you access them the way you were doing, they are referenced in this order:

1, 5, A, 2, 6, B, 3, 7, C, 4, 8, D

Which completely throws off the CPU!
Posted By: Superku

Re: How to: tiles in 3dgs - 07/25/11 20:18

Quote:
Also, you were using the "y" variable to reference the tilemap column and the "x" variable to reference the tilemap row, and then you were drawing the tilemap with draw_quad with the "x" parameter as the column and the "y" parameter of the row! Basically, you confused the variables halfway through, which would cause the data to be drawn sideways. I've done it before lots of times.

I did not confuse them, you've just assumed that the first index variable indicates a row. I know that it's against the convention (of matrix notation) and now I know how those arrays are saved internally, thanks to you, but it doesn't matter, my approach works fine as well. I've done it before.
Posted By: Redeemer

Re: How to: tiles in 3dgs - 07/25/11 20:34

On second thought, you're probably right on that point. I tend to confuse myself on those grounds and assume that the data is being stored by the matrix convention, because that's how I always did it.
Posted By: Joozey

Re: How to: tiles in 3dgs - 07/25/11 20:43

Thanks Superku, had not thought of using that.

I'll have to rebuild the noise function, and store it in an array as quads need to be drawn every frame. The bitmap drawn by the quads is determined by a noise function consisting of several formulas. I can not recalculate those each frame, as that would be again very slow. When moving the screen I'll have to update the array with a column or row of new noise values from the new position then.
Posted By: lostclimate

Re: How to: tiles in 3dgs - 07/25/11 21:44

i didnt read the last like 4 posts, but what about making some clumped prefabs.... like if an area has a square of like 9 of the same tiles, make 1 tile the size of 9 with 9 small tiles in it. the math shouldnt be too hard i dont think.
Posted By: JibbSmart

Re: How to: tiles in 3dgs - 07/25/11 22:12

How about one relatively low-res bmap (obviously at something less than 32 bits per pixel) and use a shader to render the tiles over each pixel?

Jibb
Posted By: Joozey

Re: How to: tiles in 3dgs - 07/25/11 22:48

In the end draw_quad still appears to slorp down to 15 fps. And I now have the disadvantage not to be able to draw panels in front of the level.

I guess clumped prefabs also requires a lot of calculating to determine where the clumps are, and again when a tile in the prefab is destroyed. But since there are large spots of ground that are all the same, may be a solution. It's just a downside that the fps goes down when you dig more pathways in the screen... unless the pathways are bitmaps in front of the tiles... hm...

Not sure about a shader, never fancied them as they tend to make my laptop cry (no matter how simple they are), and only used premade shaders thus far. I have no idea where to start writing a shader that puts tiles on pixels of a lower scaled bitmap generated from a noise function. I guess it's like using an array and bmap_blit, but somewhat faster?
Posted By: Slin

Re: How to: tiles in 3dgs - 07/25/11 22:57

The best approach would probably bmap_blit... Your problem is the high amount of draw calls, as each quad is rendered seperately and the parameters and stuff are probably all seperately updated. And while the bmap_blit itself wonīt be extremely fast, it shouldnīt cause any problems as long as donīt change all tiles each frame. The resulting bitmap can than be rendered in just one draw call...
Another alternative not directly possible with gamestudio would be geometry shaders. The possible solution which is similar would be to prepare a mesh with the maximum number of quads you need and do some vertex manipulation on it to set positions and texcoords in an atlas texture. The last thing would probably be the fastest solution with gamestudio, but bmap_blit should be a bit easier to realize and perform good enough.
Posted By: Joozey

Re: How to: tiles in 3dgs - 07/25/11 23:03

^ and when moving around I could simply bmap_blit that image, minus the row or column that I move from, and only update that part. Let's see how that performs.
Posted By: Redeemer

Re: How to: tiles in 3dgs - 07/25/11 23:28

If you had access to video memory, I could give you some extremely fast tile map drawing code I once wrote. It would give you at least 60 FPS as well as scrolling if you wanted it, and you wouldn't have to mess with updating only a few tiles per frame...
Posted By: JibbSmart

Re: How to: tiles in 3dgs - 07/26/11 00:42

But shaders solve everything frown Ever. Forever.

Jibb
Posted By: Joey

Re: How to: tiles in 3dgs - 07/26/11 11:40

the geometry shader approach also came to my mind. but you can strip that down to a pure vertex shader approach. just prepare the mesh for all quads and then do a texture lookup to check which texture should be drawn; changing blocks then works via pixel_to_bmap.
Posted By: Hummel

Re: How to: tiles in 3dgs - 07/26/11 12:15

I did a test rendering using simple pixel shaders some time ago.
This way you can fill your whole screen with multible layers of tiles at very high frame rates.
I see no reason not to use shaders even if you create something old-schoolish.
Posted By: bart_the_13th

Re: How to: tiles in 3dgs - 07/26/11 12:40

I do see it as an overkill wink
Slin's method of using bimap blitting looks more suitable I guess. Just use the blitting and erasing only when needed(even if you are going to do it on multiple tile, do it once at a time, spread the load), and there should be no slowdown.
Posted By: Slin

Re: How to: tiles in 3dgs - 07/26/11 12:43

NOOO, Shaders are bad!!!111111111
They donīt work on hardware older than 10 years!!!!!!!!!!!! Oh and shadermodel 3 is the devil himself as it wonīt work on hardware older than 7 years!!!1111111111

Did I mention that my 2 years old iPhone canīt render anything without shaders? laugh
Posted By: Redeemer

Re: How to: tiles in 3dgs - 07/26/11 13:04

All you young 'uns and your fancy shaders... What kind of programmers are you? wink
Posted By: bart_the_13th

Re: How to: tiles in 3dgs - 07/26/11 14:34


Originally Posted By: Redeemer
All you young 'uns and your fancy shaders... What kind of programmers are you? wink

Shader programmer? grin
Posted By: Joey

Re: How to: tiles in 3dgs - 07/26/11 15:02

have you tried using particles? they should render fast enough.
Posted By: Joozey

Re: How to: tiles in 3dgs - 07/26/11 17:14

haven't tried particles, but currently drawing a low resolution bitmap of the level that is visible (80x60 pixels), and scaled up 8 times with a panel. This works fast. To add detail I should apply a shader then I suppose. Was this the way proposed with the shader?
Posted By: JibbSmart

Re: How to: tiles in 3dgs - 07/26/11 17:29

That is what I had in mind laugh Except I don't think panels can have shaders. I was thinking a quad, or doing it all in a pp-shader that takes into account level offset.

Jibb
Posted By: Joey

Re: How to: tiles in 3dgs - 07/26/11 20:15

not exactly what I meant, but should work just as well. Do a point-sampled texture lookup for your base texture, and then, depending on the color of the pixel, fill the texel with a detailed tile. In principle that's something like

Code:
unsigned int4 base = tex2D(baseSampler, texCoord.xy);
switch (base.x) {
case 1:
    float3 out = tex2D(tile1Sampler, fmod(texCoord.xy*TILE_SIZE, TILE_SIZE);
    break;
...
}



Joey
Posted By: JibbSmart

Re: How to: tiles in 3dgs - 07/26/11 20:21

If the tiled textures are going to be quite small (like shown), I would pack them into one texture, and use the base texture as an offset within the tiled texture.

I think that'd be more efficient.

Jibb
Posted By: Hummel

Re: How to: tiles in 3dgs - 07/26/11 21:43

Yep, with other words: using an index map to sample the tile texture from a texture atlas.
Posted By: Joozey

Re: How to: tiles in 3dgs - 07/26/11 23:07

What about just scaling the base color bitmap on screen, and apply a detail-bitmap on it with the shader? Just a black tga with transparent parts that form the details on the (plain colored) block.

Currently playing too much with the noise-landscape generator tongue.

Edit: I went for the cheap approach: put up a window element in a panel, apply one detail-tile tga bitmap to it, and resize the window to full screen. The detailed tile is automatically tiled over the screen. Insta-tiles. This also makes cubes from the sky though, not sure if I like that. Will try the shader approach too soon. Thanks for the shader snippet and all suggestions laugh.
Posted By: jenGs

Re: How to: tiles in 3dgs - 07/27/11 22:50

Hello,
I know you have got allready a lot of suggestions about the tileproblem, but I did some testing and have a whole different sollution for you.

Just execute this code and see.
Click onto the rectangle to switch a tile.

Code:
///////////////////////////////
#include <acknex.h>
#include <default.c>
#include <stdio.h>

///////////////////////////////

void createFile();
void createAdvanced();
void clickAround();

BMAP *sand = "b1.tga";
BMAP *gras = "b2.tga";

STRING *line = "";

ENTITY *wall;

void main()
{
	mouse_mode = 4;
	mouse_pointer = 2;
	mouse_sync = 1;
	video_switch(10, 32, 2);
	wait(1);
	level_load("");
	
	createAdvanced();
	
	camera.pan = 90;
	camera.y = -512;
	camera->flags |= ISOMETRIC;
	camera.arc = 20;
	
	clickAround();
}

void clickAround()
{
	VECTOR to;
	
	while(1)
	{
		if(mouse_left)
		{
			vec_set(to, mouse_dir3d);
			vec_scale(to, 1000);
			vec_add(to,mouse_pos3d);
			c_trace(mouse_pos3d, to, USE_POLYGON | SCAN_TEXTURE);
			if(hit.entity != NULL)
			{
				ENTITY *w = hit.entity;
				var s = hit.triangle;
				if(s % 2 != 0) { s++; }
				s /= 2;
				int v1 = ((s - 1) * 4) + 1;
				CONTACT *c = ent_getvertex(w, NULL, v1);
				c.v = NULL;
				c.x += 8;
				ent_setvertex(w, c, v1);
				
				c = ent_getvertex(w, NULL, v1 + 1);
				c.v = NULL;
				c.x -= 8;
				ent_setvertex(w, c, v1 + 1);
				
				c = ent_getvertex(w, NULL, v1 + 2);
				c.v = NULL;
				c.x += 8;
				ent_setvertex(w, c, v1 + 2);
				
				c = ent_getvertex(w, NULL, v1 + 3);
				c.v = NULL;
				c.x -= 8;
				ent_setvertex(w, c, v1 + 3);
			}
			while(mouse_left) { wait(1); }
		}
		wait(1);
	}
}

void createAdvanced()
{
	int px, py;
	int x, y;
	double rx, ry;
	int i = 0;
	STRING *f = str_create("");
	int mesh = 0;
	
	BMAP *map01 = bmap_createblack(320, 320, 32);
	
	for(px = 0; px < 1; px++)
	{
		for(py = 0; py < 1; py++)
		{
			str_printf(f, "mesh%.0f.obj", (double)mesh);
			
			FILE *file = fopen(f.chars, "wb");
			
			for(y = 0; y < 20; y++)
			{
				for(x = 0; x < 20; x++)
				{
					rx = x * 8;
					ry = -y * 8;
					str_printf(line, "v %.0f %.0f 0\n", rx, ry);
					fputs(line.chars, file);
					str_printf(line, "v %.0f %.0f 0\n", rx + 8, ry);
					fputs(line.chars, file);
					str_printf(line, "v %.0f %.0f 0\n", rx, ry - 8);
					fputs(line.chars, file);
					str_printf(line, "v %.0f %.0f 0\n", rx + 8, ry - 8);
					fputs(line.chars, file);
				}
			}
			
			fputs("\n", file);
			
			for(y = 0; y < 20; y++)
			{
				for(x = 0; x < 20; x++)
				{
					str_printf(line, "vt %.3f %.3f\n", (double)x / 20.0, (double)y / 20.0);
					fputs(line.chars, file);
					str_printf(line, "vt %.3f %.3f\n", ((double)x + 1) / 20.0, (double)y / 20.0);
					fputs(line.chars, file);
					str_printf(line, "vt %.3f %.3f\n", (double)x / 20.0, ((double)y + 1) / 20.0);
					fputs(line.chars, file);
					str_printf(line, "vt %.3f %.3f\n", ((double)x + 1) / 20.0, ((double)y +1) / 20.0);
					fputs(line.chars, file);
					
					bmap_blit(map01, sand, vector(x * 16, y * 16, 0), NULL);
				}
			}
			
			fputs("\n", file);
			
			for(i = 0; i < 400; i++)
			{
				double v1 = i * 4 + 1;
				double v2 = v1 + 1;
				double v3 = v1 + 2;
				double v4 = v1 + 3;
				str_printf(line, "g plane%.0f\n", (double)i);
				fputs(line.chars, file);
				str_printf(line, "f %.0f/%.0f %.0f/%.0f %.0f/%.0f\n", v3, v3, v2, v2, v1, v1);
				fputs(line.chars, file);
				str_printf(line, "f %.0f/%.0f %.0f/%.0f %.0f/%.0f\n", v3, v3, v4, v4, v2, v2);
				fputs(line.chars, file);
			}
			
			fclose(file);
			
			wall = ent_create(f, vector(px * 160, 0, py * 160), NULL);
			
			ent_setskin(wall, map01, 1);
			mesh++;
		}
	}
	
	str_remove(f);
}



Some problems: You can't clone an entity with ent_clone created from an *.obj mesh, because stupid c_trace goes nuts if you do so.
So you have to create a mesh for every chunk of tiles.
Workaround: delete the obj meshes before quitting the programm.
Texturing is a pain in the ass, because ent_create doesn't load the textures of the mtl library connected to the properly. MED does this without problems, but at runtime there is a neat out of memory error. so you have to create a bigger texture for every chunk and blit the tiletexture onto it, then use ent_setskin.

But, I've got maximaml framerates out of this, even with a lot of chunks.

There is a lot of room for improvement. Perhaps it is possible to create the meshes in a virtual filesystem, without bothering the harddrive.
With a lot of chunks, the loading time is very long. I've tested 60 chunks and it took 20 secs to create them.

Edit: Of course you could create a directX mesh. But everytime I've did that and attached it to an entity the engine messed up the clipping the hull the visibility and everything. Of course it could have also been me who messed up the vertice normals, order ...

Posted By: MTD

Re: How to: tiles in 3dgs - 09/03/11 11:07

If somebody is interested:
http://www.matudagames.com/GameDev/Miner/miner2000.zip

To move char: Left & Right arrows on keyboard
Want to make a "Jump": Space
Key for "Dig": X

+ mega source code inside wink
Posted By: Aku_Aku

Re: How to: tiles in 3dgs - 09/03/11 12:00

I just executed your code and got two error messages.
Something is wrong with your code.
Maybe you didn't declared the tgas.
Please don't post useless code to the forum, thanks in advance.
Posted By: MTD

Re: How to: tiles in 3dgs - 09/03/11 12:53

Originally Posted By: Aku_Aku
I just executed your code and got two error messages.
Something is wrong with your code.
Maybe you didn't declared the tgas.
Please don't post useless code to the forum, thanks in advance.

???

If You run only the code - then you need to add 2 visual files (the cubes) by Your own taste.
(UPLOADED NEW PACK) - should be fine now
Posted By: bart_the_13th

Re: How to: tiles in 3dgs - 09/03/11 13:19

Originally Posted By: Aku_Aku
I just executed your code and got two error messages.
Something is wrong with your code.
Maybe you didn't declared the tgas.
Please don't post useless code to the forum, thanks in advance.

There's no code posted in this forum that's useless. Even if it has errors, you shouldn't attack the poster who's voluntarily post their own code.
Just my 2 cents
Posted By: Aku_Aku

Re: How to: tiles in 3dgs - 09/03/11 13:30

It wasn't attack, it was just an ask, and i used the word please.
I think you misunderstood me.
Posted By: bart_the_13th

Re: How to: tiles in 3dgs - 09/03/11 14:46

Ok, maybe using the word 'attack' is a bit too much, sorry wink
Posted By: YellowAfterlife

Re: How to: tiles in 3dgs - 10/02/11 00:24

Screenshot:

Code (56 lines):
Click to reveal..
Code:
#include <acknex.h>
#include <default.c>
#define tw 8
#define th 8
#define sw 80
#define sh 60
BMAP* screen; // buffer image
COLOR screencolor; // buffer background colour
int map[sw][sh]; // map
BMAP* tiles[4]; // tile type images
int tile_vis[4] = { 0, 1, 1, 1 }; // tile type visibility flags
int tile_color[4] = { 0x000000, 0xFFFFFF, 0xAAAAAA, 0x777777 }; // tile type colours
void res_init()
{
	int i, j, k;
	COLOR c;
	for (i = 0; i < 4; i++) // create color images
	{
		tiles[i] = bmap_createblack(tw, th, 32);
		k = tile_color[i];
		c.red = (k >> 16) & 0xFF;
		c.green = (k >> 8) & 0xFF;
		c.blue = k & 0xFF;
		bmap_fill(tiles[i], c, 100);
	}
	for (i = 0; i < sw; i++) // generate some terrain
	{
		k = random(sh >> 2) >> 0; for (j = 0; j < k; j++) map[i][j] = 0;
		k += random(sh >> 2) >> 0; for (; j < k; j++) map[i][j] = 3;
		k += random(sh >> 2) >> 0; for (; j < k; j++) map[i][j] = 2;
		for (; j < sh; j++) map[i][j] = 1;
	}
	screen = bmap_createblack(tw * sw, th * sh, 24);
	vec_set(screencolor, vector(40, 40, 40));
}
void main()
{
	wait(1); // must-wait - some bitmap functions cant be used at first frame
	res_init(); // initialize things
	while (1) // main loop.
	{
		bmap_fill(screen, screencolor, 1); // clear screen with colour
		int i, j, k; // counters and temp. var
		VECTOR v; // temp vector
		for (j = 0; j < sh; j++)
		for (i = 0; i < sw; i++)
		{
			k = map[i][j]; // obtain tile index at coordinates
			if (!tile_vis[k]) continue;
			v.x = i * tw; v.y = j * th; // move vector
			bmap_blit(screen, tiles[k], v, NULL); // draw tile image to buffer
		}
		draw_quad(screen, vector(320, 0, 0), 0, 0, 0, 0, 100, 0); // draw buffer to engine window
		wait(1); // if you'll put 2 there, you'll be able to see the horror of screen flickering :)
	}
}


This is a very basic implementation of 'buffer', to which separate tile images are copied and which is drawn to screen afterwards. Would be nice to know, at which speed this runs, compared to original test files.
Obviously, this could be optimized a lot more, adding tile 'caching', handling game camera, etc.
In addition, a rule to remember: the smaller tiles are, the higher are chances that game will lag.

Otherwise, making 2d games with 3d GameStudio is quite realistic, and just takes method knowledge to do.
© 2024 lite-C Forums