Tile collision control with SDL2

Pages: 12
So I am building a game with SDL and in need of some help. I've built a tilemap that's a 2D array and a draw function that draws the map with simple sprites. E.g., 0 = grass, 1 = water and 2 = cobblestone, 3 = dirt, etc. However, I am having issues trying to keep the player within certain tiles. For example, I do not want the player to be able to walk on tile's marked with 1 (that being water). But I do want the player to be able to walk on grass.

For example:

1
2
3
4
5
6
int map[4][4]{
{1, 1, 0, 0},
{1, 1, 0, 0},
{1, 1, 0, 0},
{1, 1, 0, 0},
};


The array above indicates that the player should only be able to walk on the right side(all the 0's), and not on the left side.

I've figured out how I can do a collision control on the main window in SDL2 itself. E.g.:

1
2
3
if (Playertexr.x < 0) {
    Playertexr.x = 0;
}


But I am kinda cluessless to how I can check if the player is on a spesific tile. Let alone, how I should prevent the player from walking on the tile? Because, checking if the player is outside the visible x-axis is fair enough. Preventing the player from going off the x-axis is also fair enough, because you'll just place the players position x = 0.

But how should one proceed with spesific tiles on the map and not just the window's borders?
Last edited on
Get the player's location.
Identify the tile that is at the same location.
That tile is the tile the player is on.
First, use 1D array (better performances). In your exemple :
 
int map[16];

And use the formula x+y*W (where x,y are your coordonnates and W the width of the map, so 4 here).

Second, to solve your problem, you have two possibilities :

1. Make a move() function where you check the futur coordonnates of the payer and "cancel" the movement in case of invalid position.

2. You check the position of your player AFTER the move. If it's an invalid position, you teleport him on the closest valid tile.
Note : this solution can bring some bug with a small array (like yours) but is well working on a bigger array (where a river is not only a single row of '1' tiles).

If it(s a real time game (with smooth moves) I recommend the second option, but if it's a turn per turn game, use the first.
Last edited on
> First, use 1D array (better performances).
Can you prove that in all cases?

It's one of those 1980's pseudo tricks which long ago ceased to have any practical use.


Well, I'm really curious to know the truth about it.

After some researches I found this post where we can see a perf degradation with 2D arrays : https://software.intel.com/en-us/forums/intel-fortran-compiler-for-linux-and-mac-os-x/topic/270751

However it's logic for me that the time to access to the x,y element of a 1D and 2D array should be the same.

Someone said on S.O that the difference is due, for small arrays, to the separation of each subarray into the heap because the processor 64bytes is reading block by block, but on splitted subarray it can't use its full capacity.

Yet I'm sure of two things. It's a better thing to learn manipulating 2D array mapped as 1D array, and this also use less memory (x[A][B] y[A*B] -> x use A memory slots in addition to y).
Last edited on
It's a better thing to learn manipulating 2D array mapped as 1D array,

I disagree. Why is that better? The code is harder to understand. That's a bad thing. Programmer time is expensive.


this also use less memory

I disagree.
1
2
int char[25]; // this occupies 25 consecutive bytes
int char[5][5] // this occupies 25 consecutive bytes 



Last edited on
Er,
1. That's Fortran and not C or C++, so different rules apply.

2. Given A[X][Y], for Fortran, it's A[0][0] and A[1][0] which are adjacent in memory. For C and C++, it's A[0][0] and A[0][1] which are adjacent in memory.

So given some kind of loop like this.
1
2
3
4
5
for ( int x = 0 ; x < X ; x++ ) {
    for ( int y = 0 ; y < Y ; y++ ) {
        A[x][y] = 0;
    }
}

In C and C++, this progresses nicely through incremental addresses and almost all accesses land in the cache.
For Fortran, almost every access is a cache-miss.

I'm sure if you wrote a naive column major loop in C++ on a 2D array, it would suck just as bad.

> and this also use less memory (x[A][B] y[A*B] -> x use A memory slots in addition to y).
Now that's just utter BS.
1
2
3
4
5
6
7
8
9
10
11
12
$ cat baz.cpp
#include <iostream>

int main()
{
  int a[10][20];
  int b[10*20];
  std::cout << sizeof(a) << " " << sizeof(b) << std::endl;
  return 0;
}
$ ./a.out 
800 800
I believe that the myth of 2D arrays consuming more memory than 1D arrays mapped to two dimensions is based on misreading or misunderstanding how it does apply to a different scenario, where the 2D array is implemented as a 1D array of pointers, each holding a 1D array, forming a 2D grid.

THAT does consume more memory, and it usually chops up the layout so it causes more cache misses.

Given A[X][Y], for Fortran, it's A[0][0] and A[1][0] which are adjacent in memory .... For Fortran, almost every access is a cache-miss.

(a) By default, Fortran arrays are one- not zero-based. Unlike C++, which is forever restricted to 0-based, you can, however, declare Fortran arrays to start from whatever index you like
(b) Why is that a problem? If my array corresponds to x horizontal, y vertically upward (as on graph paper) then it's fairly normal (in scientific work) to scan across row by row. No cache misses there.

1
2
3
4
5
for ( int x = 0 ; x < X ; x++ ) {
    for ( int y = 0 ; y < Y ; y++ ) {
        A[x][y] = 0;
    }
}

Well, in Fortran (any type of array) or Python (numpy arrays) you would just write
A = 0
for an entire array operation, leaving the compiler to optimise it - and the array A could have one, two or any number of dimensions. The only container allowing you to do that in C++ is a valarray, and that is confined to be one-dimensional.


I've experienced three cases where it is better to use 1-d arrays (but none apply to the problem here):
- parallel processing, because the buffer used to transfer data between processors is inherently 1-d;
- domain decomposition with multiple blocks ... of different sizes; this is well-nigh impossible to index in 2- or 3-d;
- where one has to dynamically resize in higher dimensions. 3-dimensional arrays really don't work well in vector<>.
Last edited on
@Repeater, yeah that's what I am trying to do. Just not quite sure how I get there yet!

@zaap thanks for the good info. But I gotta admit that I'm still a bit cluess the collision detection. Where I am struggling with how I can check the different points on the tile map. I made a new one with a 1D array btw:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class Tile {
public:
	Tile();
	void LoadMap(int arr[16]);
	void drawMap();

	int lvl1[32] = {
		0, 1, 0, 0, 0, 0, 0, 0, 
		0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0,
	};

private:
	SDL_Rect src, dest;

	SDL_Texture* grass;
        SDL_Texture* water;

	int map[16];
};
Tile::Tile() {
	grass = IMG_LoadTexture(Game::renderer, "img/grass.png");
        // ...

	LoadMap(lvl1);

	src.x = src.y = 0;
	src.w = dest.w = 32;
	src.h = dest.h = 32;

	dest.x = dest.y = 0;
}

void Tile::LoadMap(int arr[16]) {
	for (int tile = 0; tile < 16; tile++) {
		map[tile] = arr[tile];
	}
}

void Tile::drawMap() {
	int type = 0;

	for (int tile = 0; tile < 16; tile++) {
		type = map[tile];

		dest.x = tile * 32;

		switch (type) {
		case 0:
			SDL_RenderCopy(Game::renderer, grass, &src, &dest);
			break;
                //...
		default:
			break;
		}
	}
}


The player is also this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class Game {
public:
	Game() { }
	~Game() { }
	void handleEvents();
	void clean();
	void createPlayer();
	void render();
	void initialize(const char* title, const int width, const int height);
	bool isRunning();

	static SDL_Renderer *renderer;
private:
	bool runningCheck = true;
	SDL_Window *window;

	SDL_Texture *player;
	SDL_Rect Playertexr;
};
void Game::createPlayer() {

	int w; int h;
	player = IMG_LoadTexture(renderer, "img/player.png");
	SDL_QueryTexture(player, NULL, NULL, &w, &h);

	Playertexr.x = 0;
	Playertexr.y = 0;
	Playertexr.w = w;
	Playertexr.h = h;

}
void Game::handleEvents() {
	SDL_Event event;
	SDL_PollEvent(&event);
	int x; int y;
	SDL_GetMouseState(&x, &y);

	switch (event.type) {
	     // ...
        }

	if (Playertexr.x < 0) {
		Playertexr.x = 0;
	}
	if ((Playertexr.x + Playertexr.w) > 800) {
		Playertexr.x = (800 - Playertexr.w);
	}
	if (Playertexr.y < 0) {
		Playertexr.y = 0;
	}
	if ((Playertexr.y + Playertexr.h) > 640) {
		Playertexr.y = (640 - Playertexr.h);
	}

	std::cout << Playertexr.x << ", " << Playertexr.y << std::endl;

	//Check for collisions etc...
}


Now, what I just posted is quite a lot of code. Even though I tried to simplify as much as I could. I prefer to try to stick to pseudocode for the ideas. But this time I am pretty clueless. So if no one minds checking over, and maybe help me out here. It'd be highly appreciated!
Last edited on
Ok now I understand the problem you have.

Here's a function to know in which tile is the player.
(I do it with a 2D array according to all the above argumentation).
1
2
3
4
5
6
7
8
/**
H - the height of a sprite
W - the width of a sprite
*/

Tile getPos(Tile map[4][4], Player p){
    return map[p.x/W][p.y/H];
}



Could you possibly specify some more @Zaap? I went back to the 2D array btw
Last edited on
Your player moves on a 640x800 map, but tiles informations are located into your 4x4 array.

What you need to do is a projection of your 4x4 array on your big array (or the opposite).

To do that, I just converted the position of your player on the 640x800 grid to his position on the 4x4 grid, assuming 4xW (width of a sprite size) cover the entire map.

Ex for the X-axe :
1
2
3
4
5
6
7
Small array : S[4];
Big array : B[8];
#define W 2

B[0] && B[1] --> S[0] // 0/2=0 and 1/2 (integer)=0
B[2] && B[3] --> S[1] // 2/2=1 and 3/2 (integer)=0
etc...
Last edited on
@Zaap, I think I get what you're saying?

I kinda got a collision control working. But that's between two SDL_Rect's. This way:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
bool Game::checkCollision(const SDL_Rect &a, const SDL_Rect &b) {
	int aLeft = a.x;
	int aRight = a.x + a.w;
	int aTop = a.y;
	int aBottom = a.y + a.h;

	int bLeft = b.x;
	int bRight = b.x + b.w;
	int bTop = b.y;
	int bBottom = b.y + b.h;

	if (aLeft > bRight) {
		return false;
	}
	if (aRight < bLeft) {
		return false;
	}
	if (aTop > bBottom) {
		return false;
	}
	if (aBottom < bTop) {
		return false;
	}

	return true;
}


Which works fine with:
1
2
3
4
if (checkCollision(player, enemy)) {
	player.x = 0;
	player.y = 0;
}


However, how would I apply this to the tilemap?
1
2
3
4
bool checkValidTile(player p, int map[4][4]){
    if(map[p.x/W][p.y/H]==0) return false;
    return true;
}
I have somes issues with trying to implement this tbh.

This is what I am trying to do with the info you've given:

1
2
3
4
5
6
7
Map obj;

if (checkValidTile(Playertexr, obj.lvl1)) {
	Playertexr.x = 128 / 2;
	Playertexr.y = 900 / 2;
}


1
2
3
4
5
6
7
8
bool checkValidTile(const SDL_Rect player, int map[20][25]) {
	if (map[player.x / player.w][player.y / player.h] == 0) {
		return false;
	}
	else {
		return true;
	}
}


with the player defined as:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SDL_Texture *player;
SDL_Rect Playertexr;

void Game::createPlayer() {

	int w; int h;
	player = IMG_LoadTexture(renderer, "img/player.png");
	SDL_QueryTexture(player, NULL, NULL, &w, &h);

	Playertexr.x = 128 / 2;
	Playertexr.y = 900 / 2;
	Playertexr.w = w * 2;
	Playertexr.h = h * 2;

}


And the Map class (the ones for tiles):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Map {
public:
	Map();
	~Map();
	void LoadMap(int arr[20][25]);
	void drawMap();
	
	int map[20][25];
	
	int lvl1[20][25] = {
	{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0},
	{0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	};

private:
	SDL_Rect src, dest;

	SDL_Texture* dirt;
	SDL_Texture* grass;
	SDL_Texture* water;
};


This looks ok, so what's the issue ?
I got some weird readings that didn't make much sense. Imagine this map:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int map[20][25] == {
	{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0},
	{0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	};


Then I would get a collision detection in this area (ish):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int map[20][25] == {
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	};
Can you tell me if player and tile sprites have the same size ?
They are not the same size, I think the player is of 40x60 and the 32x32
Pages: 12