Problem calculating collision

Godot Version

4.3

Question

Hi

I’m am trying to do something I expected to be simple, but apparently I cannot find the error, so here I am asking for help.

I made a simple tilemap with water tiles and dirt tiles. When I move into the water the player is no longer on land and will begin to collide with the dirt tiles.
That part was really simple.

The next part is the capability to once more enter the land. To do so I expanded the 16x16 rectangle to 24x24 and translated it by 4. This should add a 4 pixel padding to the landmass that is not a part of the tilemap collider. Thus I should be able to press “space” when up against the landmass and stay on land.

For some reason part 2 does not work. I have found that not only does the shape not expand, but there seems to be a spot in the corner that it considers land as well…

Now even more confused I have the landarea working as intended, with the global location and yet another hidden “landmass” in the corner. The hidden “landmass” in the corner does seem to grow worse as I scale the rectangle shape, but nothing happens to the landmass.

I cannot seem to find any documentation on collision calculation. Does anyone have experience on this topic?

using Godot;
using System.Linq;

public partial class Phase : Node
{
	[Export] TileMapLayer tilemap;
	[Export] Node2D player;

	bool _isOnLand = true;
	Vector2 lastPlayerPosition;
	int counter = 0; // Limit frames processed if no movement or action, so logs are more informative
	readonly Vector2I[] SURROUNDING_NEIGHBOURS = new Vector2I[] { new(-1, -1), new(0, -1), new(1, -1), new(-1, 0), new(1, 0), new(-1, 1), new(0, 1), new(1, 1) };

	public override void _Process(double delta)
	{
		if (lastPlayerPosition == player.Position && counter > 10 && !Input.IsActionJustPressed("space")) return;
		if (lastPlayerPosition != player.Position || Input.IsActionJustPressed("space")) counter = 0;
		var isOnLand = UpdateOnLand(_isOnLand);
		tilemap.CollisionEnabled = !isOnLand;
		if (_isOnLand != isOnLand) GD.Print(isOnLand ? "On land" : "In water");
		_isOnLand = isOnLand;
		lastPlayerPosition = player.Position;
		counter++;
	}

	public bool UpdateOnLand(bool isOnLand)
	{
		var localPosition = tilemap.ToLocal(player.Position);
		var mapCoords = tilemap.LocalToMap(localPosition);

		if (Input.IsActionJustPressed("space"))
		{
			return true;
		}
		// Water tile is fourth
		else if (tilemap.GetCellAtlasCoords(mapCoords).X == 3)
		{
			var surroundingTiles = SURROUNDING_NEIGHBOURS.Where((x, i) =>
			{
				var tileCoord = x + mapCoords;
				var isInWater = tilemap.GetCellAtlasCoords(tileCoord).X == 3;
				if (isInWater)
				{
					var tileShape = new RectangleShape2D();
					tileShape.Size = new Vector2I(16, 16);
					var tileCoordGlobal = tilemap.ToGlobal(tilemap.MapToLocal(tileCoord));
					var collisionShape2D = player.GetChild<CollisionShape2D>(0);
					var tileTransform = new Transform2D(new Vector2I(1, 0), new Vector2I(0, 1), tileCoordGlobal);
					var isStillSomewhatOnLand = collisionShape2D.Shape.Collide(collisionShape2D.Transform, tileShape, tileTransform);
					return isStillSomewhatOnLand;
				}
				return false;
			}).ToList();
			if (!surroundingTiles.Any())
			{
				return false;
			}
		}

		return isOnLand;
	}
}

I see one of my mistakes, The global does not work, only the else if works.
That does explain a lot, then I just need to use the correct positions.

It seems I needed to use collisionShape2D.ToLocal() on the global position. Then I could modify the rectangle size for padding, though I ended with new Vector2I(14, 22) probably due to rotation and scaling of the collision shape