Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Collision with Tilemap Layer
#1
Heyo! I'm curious about how you can make it so you can check for collision between a sprite and a layer. Like if you got a collision layer or something from Tiled and you wanna test against it so your character can't pass through walls and that stuff.
I wish you could just mov the obstacle from the destination.
Reply
#2
Hi!
Of course, you have to use the function TLN_GetLayerTile() on the layer you want to test. You pass layer index, the x and y pixel position you want to test, and a pointer to a TLN_TileInfo struct. This struct will be filled with the tile at that position, its type, flags, offet inside the tile, color of the pixel at that position... this is what you need to implement sprite vs layer collision.

Let me know!
Reply
#3
(04-11-2020, 04:37 PM)megamarc Wrote: Hi!
Of course, you have to use the function TLN_GetLayerTile() on the layer you want to test. You pass layer index, the x and y pixel position you want to test, and a pointer to a TLN_TileInfo struct. This struct will be filled with the tile at that position, its type, flags, offet inside the tile, color of the pixel at that position... this is what you need to implement sprite vs layer collision.

Let me know!

Does it load info like, say, if my tile has a boolean custom property that says if it's collidable or not? I'd find that VERY good.
Other than that I am confused about what data from TileInfo is best suited for letting me know the tile is collidable. Checking against ID is too hardcoded of a method.

[Image: unknown.png]
I wish you could just mov the obstacle from the destination.
Reply
#4
A boolean "Collidable" would be impractical, as there are many types of collisions: is the block solid walkable floor? One-way platform? A ladder?  Sliding slope? Dangerous spikes? All of them are collidable but must behave differently.

Currently up to version 2.4.x, Tilengine reads a custom property called "type" in the tileset. It is numeric in type, and is the value returned in TLN_TileInfo::type structure member (http://www.tilengine.org/doc/struct_t_l_..._info.html). The example project SuperMarioClone uses this system to determine the type of tile, if you open the file smw_foreground.tsx you will see it.

Since I implemented this feature, the Tiled editor has had the standard property Type added to the tilleset. In a future update that I am preparing, it will also bring this property to get the type, although I will leave the current custom property for backwards compatibility. You can even see this parameter unused in your screenshot, just below ID.

There are other gameplay implementations that use a layer exclusively for collisions, in which there are as many different tiles as there are collidable block types, and it uses the tile ID as a type. It is a bit redundant, and although it can be done it is preferable to work directly with the "type" property in the main tileset:
https://ansimuz.itch.io/sunnyland-forest


Attached Files Thumbnail(s)
   
Reply
#5
(04-13-2020, 02:59 PM)megamarc Wrote: A boolean "Collidable" would be impractical, as there are many types of collisions: is the block solid walkable floor? One-way platform? A ladder?  Sliding slope? Dangerous spikes? All of them are collidable but must behave differently.

Currently up to version 2.4.x, Tilengine reads a custom property called "type" in the tileset. It is numeric in type, and is the value returned in TLN_TileInfo::type structure member (http://www.tilengine.org/doc/struct_t_l_..._info.html). The example project SuperMarioClone uses this system to determine the type of tile, if you open the file smw_foreground.tsx you will see it.

Since I implemented this feature, the Tiled editor has had the standard property Type added to the tilleset. In a future update that I am preparing, it will also bring this property to get the type, although I will leave the current custom property for backwards compatibility. You can even see this parameter unused in your screenshot, just below ID.

There are other gameplay implementations that use a layer exclusively for collisions, in which there are as many different tiles as there are collidable block types, and it uses the tile ID as a type. It is a bit redundant, and although it can be done it is preferable to work directly with the "type" property in the main tileset:
https://ansimuz.itch.io/sunnyland-forest

I have managed to get it working now, but my implementation seems wonky at best. I can't seem to hit well geometry and in some instances getting collisions close to the 0,0 coordinate can result in a segfault! Here is my code as it currently is. I don't know how I can correctly test with tiles... I have been making use of the fixed point stuff too which resulted in a few moments I forgot to convert to int hah. I should pay more attention!


Attached Files
.zip   rpg.zip (Size: 8.74 KB / Downloads: 3)
I wish you could just mov the obstacle from the destination.
Reply
#6
Hi,
You're experiencing two unrelated problems:

1. Seems that TLN_GetTileInfo() is not correctly protected against passing negative values as world coordinates, that's why sometimes "segfaults" when walking left and up. I should protect it, menawhile you can do it in your application, checking against negative values and if so, adding the size of the negative dimension. You can get them at runtime with TLN_GetLayerWidth() and TLN_GetLayerHeight().

2. Bad detection: you forgot to initialize city->initx and city->inity to 0 at startup, they have undefined values you're setting up the starting position of the layer with these unknown values. That's why what you see is not what you expect, you're checking in one place but displaying another. Setting them to 0 and then calling TLN_SetLayerPosition() will fix it.
Reply
#7
(04-14-2020, 03:36 AM)megamarc Wrote: Hi,
You're experiencing two unrelated problems:

1. Seems that TLN_GetTileInfo() is not correctly protected against passing negative values as world coordinates, that's why sometimes "segfaults" when walking left and up. I should protect it, menawhile you can do it in your application, checking against negative values and if so, adding the size of the negative dimension. You can get them at runtime with TLN_GetLayerWidth() and TLN_GetLayerHeight().

2. Bad detection: you forgot to initialize city->initx and city->inity to 0 at startup, they have undefined values you're setting up the starting position of the layer with these unknown values. That's why what you see is not what you expect, you're checking in one place but displaying another. Setting them to 0 and then calling TLN_SetLayerPosition() will fix it.

I have fixed the issues but the detection is still bad. The player can go into the left side of the wall while the right it is correct, and just generally clip. I should look into better tackling of the problem but currently this is what stands. I would love to know how to do a bounding box sort of thing so it's way more intuitive to have a place where you can't collide with. Generally I am doing a "world moves, not player" sort of thing which is easier to program but less intuitive to account for in these scenarios. Lemme know!

[Image: unknown.png]

[Image: unknown.png]

[Image: unknown.png]
I wish you could just mov the obstacle from the destination.
Reply
#8
That's because you're just checking collision in a single point in your character, that is not representative of its shape. To properly manage it, you should work with an array of collision points around the edge of your character, and when you move it, check all the points that are on the edge of the walking direction (if you move to the right, check all collision points on the right side of the character).

How many points and at which locations? That depends on your gameplay structure, but as a general rule, they should be separated at most the size of the tiles in the collision tileset. In your project, city tiles are 16 pixels per side, and beeb is 32, so you have to use between 2 and 3 points per side. For example [0,16,31], or [8,24]. The second set leaves a small border of 8 pixels that in some cases may feel more natural than clipping to the outer edge. In 3/4 top-down perspective -like classic Final Fantasy games-, the upper collision edge should be a bit above feet, not on the head. As I said, it depends on gameplay being created. But consider an array of points per edge.

This article may help you. It's about platformers, but the concepts about motion and collision are quite general:
http://higherorderfun.com/blog/2012/05/2...atformers/
Reply
#9
(04-14-2020, 02:45 PM)megamarc Wrote: That's because you're just checking collision in a single point in your character, that is not representative of its shape. To properly manage it, you should work with an array of collision points around the edge of your character, and when you move it, check all the points that are on the edge of the walking direction (if you move to the right, check all collision points on the right side of the character).

How many points and at which locations? That depends on your gameplay structure, but as a general rule, they should be separated at most the size of the tiles in the collision tileset. In your project, city tiles are 16 pixels per side, and beeb is 32, so you have to use between 2 and 3 points per side. For example [0,16,31], or [8,24]. The second set leaves a small border of 8 pixels that in some cases may feel more natural than clipping to the outer edge. In 3/4 top-down perspective -like classic Final Fantasy games-, the upper collision edge should be a bit above feet, not on the head. As I said, it depends on gameplay being created. But consider an array of points per edge.

This article may help you. It's about platformers, but the concepts about motion and collision are quite general:
http://higherorderfun.com/blog/2012/05/2...atformers/

I have tried now implementing the method you suggested, however I still am facing the same issues even when doing checks per side of the character. I even intentionally added a offset to make it detect the bottom part of Beeb rather than the full character. Do you know what could be the issue with the checks? I am sending the zip with the code. I'm also curious about configuring layers to not repeat when going out of bounds; Rather show a solid color or repeat a small pattern such as seen in the SNES. Have a good time and be safe!


Attached Files
.zip   rpg.zip (Size: 8.87 KB / Downloads: 2)
I wish you could just mov the obstacle from the destination.
Reply
#10
Some general advices:
  • when the intention of a function is to return a true/false value, define it as bool and return an explicit expression that evaluates to bool. test_direction() and detect_collision_area() have unclear intention, they return the numeric type of the tile but are treated as boolean.
  • logic of test_direction() is wrong. Implement it in a way that returns true only when all calls to detect_collision_area() return false, any positive in detect_collision_area() must return false. In layman terms: only allow motion if all testpoints are not colliding.
  • test_direction() has another problem, it only tests the corners of the character that are 32 pixels away, but tile size is 16 pixels. There are holes inside the character. Collision points must not be farther than 16 pixels. Insert an extra collision point in between.
Not repeating layers is not supported in tilengine, they always repeat. The effect you want can be easily implemented creating a thick solid border of empty tiles around your main map, and doing bound checking when trying to move.

Be safe you too!
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)