I’ve been banging my head against this for a while and have finally decided i could do with some input from somebody far smarter than myself!
I’m working on a 2D platform game and i have a condition using raycasts to allow you to bang your head on a low ceiling. I’m using a TileMapLayer for my level.
The problem came up when i tried adding one way platforms in as the raycast detects the collision as if it were any other kind of tile.
I deactivated my head bump code just to make sure the one way tile was working without it and it does indeed work fine so i figured all i had to do then was when the raycast collides with a tile, check if it’s polygon was set to “One Way” and it it was, dont bump your head!
However i simply cannot figure out how to get that data!
So to put it simply, how can i detect if a raycast2D is colliding with a tile in a TileMapLayer that has it’s collision polygon set to “One Way” ?
if raycast.is_colliding():
var collider = raycast.get_collider()
if collider.one_way == true:
print("One way only!")
But it just returns “Invalid access to property or key ‘one_way’ on a base object of type ‘TileMapLayer’.”
My original attempts sent me down the route of, finding this in the docs:
So i tried getting the raycast collider, finding the collision point, getting the tile co-ordinates so i could do get_cell_tile_data(tile_coords) (and stored it in a variable named cell_data)
Then i followed the docs to here:
And after some reading i simply tried:
cell_data.is_collision_polygon_one_way(0, 0) as i read as there is only one collision polygon per tile, using the layer 0 and the polygon index 0 would be good.
But it always returns false. So i was not really convinced 0, 0 was right! But i can’t seem to find anything concrete on retrieving the polygon index or whether what i was trying was right.
func _physics_process(delta):
print(check_one_way())
func check_one_way():
if up_ray_cast.is_colliding():
var collider = up_ray_cast.get_collider()
var cell = collider.local_to_map(up_ray_cast.get_collision_point())
var data = collider.get_cell_tile_data(cell)
if data:
return data.get_custom_data("one_way")
else:
return 0
Hey!
Thank you very much for taking the time to do that
That does indeed work but it’s more of a work around than what i was trying to achieve. I was actually already using the same technique (but facing down) to assign different surface types to tiles like sand or ice to affect the friction, max speed and so on.
So what i mean specifically is, at this point in the TileSet editor:
You can assign the tile as one way so it actually affects the way the tile collides with the player.
There is this in the documentation:
That looka to be how i can tell if this property is active or not but… i guess i just don’t understand how it works because if i modify your code to this:
if up_ray_cast.is_colliding():
var collider = up_ray_cast.get_collider()
var cell = collider.local_to_map(up_ray_cast.get_collision_point())
var data = collider.get_cell_tile_data(cell)
if data.is_collision_polygon_one_way(0, 0):
print("One way!!")
else:
print("No...")
It always returns No…, which makes me think i’m either missing something or this is bugged? (probaby the former!)
That is weird because the code you sent to me works
if up_ray_cast.is_colliding():
var collider = up_ray_cast.get_collider()
var cell = collider.local_to_map(up_ray_cast.get_collision_point())
var data = collider.get_cell_tile_data(cell)
if data.is_collision_polygon_one_way(0, 0):
print("Yes")
else:
print("No")
That is really weird indeed. I’ve even dumbed down what is going on in my game, lengthened the raycast etc to test this case but it always returns No for me.
All i can think of is, maybe something went weird when converting this project from 4.2.2 to 4.3 (but the TileMap to TileMapLayers was really the only thing i needed to do and it was painless). I’ll fire up a new, clean project and test in there.
Ok so, i’m more confused than i was before now haha but i have at least identified where this is falling over for me.
Hopefully it’s just something simple i’m not understanding.
I decided to roll back and test the first method you suggested @artinthecoder, as i knew that already worked for my surface type check. But now even THAT was not working.
And then it struck me! For whatever reason, if my raycast is in a downwards direction it works. If my raycast is in an upwards direction it doesn’t! Like it doesn’t even get the collider.
To illustrate the issue i did this:
if ray_cast_2d.is_colliding():
var collider = ray_cast_2d.get_collider()
var cell = collider.local_to_map(ray_cast_2d.get_collision_point())
var data = collider.get_cell_tile_data(cell)
if data:
print(data.get_custom_data("one_way"))
else:
print("Nope")
That way if i’m on a tileset it should always print true or false because it’s getting data. If i set up the raycast on the bottom of my player:
And run about on this tileset i get a steady stream of false or true depending on what tiles i’m on. Perfect! … for the ground.
Now if i simply flip the raycast and put it on the top of my player
It always returns “Nope”, so it isn’t even retrieving the get_cell_tile_data(cell)
Sorry for the flurry of posts, but i have found out what is going on!
I tinkered with the code to try and get as much info about the raycast collisions as i could to see if there were any differences i could spot between a collision with the exact same tile from above versus below:
if ray_cast_2d.is_colliding():
var collider = ray_cast_2d.get_collider()
print("Collider:", collider)
print("Collision Point:", ray_cast_2d.get_collision_point())
var cell = collider.local_to_map(ray_cast_2d.get_collision_point())
print("Cell:", cell)
var data = collider.get_cell_tile_data(cell)
if data:
print("Custom Data:", data.get_custom_data("one_way"))
else:
print("Nope")
else:
print("Raycast not colliding")
And right away it became clear!
When colliding with the raycast facing downwards:
I think i can kind of see why there would be this difference, and i can only assume this would also be true in left versus right detection too.
So fixing this is as simple as adding cell.y -= 1
Which, i have to admit feels a little dirty for whatever reason! But it works.
Is it just a case of having to correct the way it rounds this: var cell = collider.local_to_map(ray_cast_2d.get_collision_point())
to take into account if the raycast collision is upward or i assume, to the left?
if up_ray_cast.is_colliding():
var collider = up_ray_cast.get_collider()
var cell = collider.local_to_map(up_ray_cast.get_collision_point())
var data = collider.get_cell_tile_data(cell)
if data.is_collision_polygon_one_way(0, 0):
print("Yes")
else:
print("No")
But there was an additional issue preventing it from working where, depending on the direction of the raycast, the value returned in: var cell = collider.local_to_map(up_ray_cast.get_collision_point())
was incorrect as it was returning the cell below the cell we actually wanted to retrieve the data for when raycasting to test a tile above the player.
Simply adding cell.y -= 1
After that line fixes the problem.
Thank you for your help @artinthecoder it’s very much appreciated