I’m working on a ladder climbing mechanic, and I want the player to always snap to the middle of the ladder. I have a script attached to the ladder’s Tilemap to handle this behavior:
using UnityEngine;
using UnityEngine.Tilemaps;
public class ExampleClass : MonoBehaviour
{
private Tilemap tilemap;
[SerializeField]private PlayerMovement mov;
bool _isLadderPositionSet = false; //Make sure to set player postion of collided ladder tile position only once
void Start()
{
tilemap = transform.GetComponent<Tilemap>();
}
void OnTriggerEnter2D(Collider2D other)
{
if(other.gameObject.tag == "Player" && mov.IsClimbing)
{
Vector3 cellPosition = GetPostionOfCollidedTile(other);
Debug.Log("ladder postion: " + cellPosition);
other.gameObject.transform.position = new Vector3(cellPosition.x + 0.5f, cellPosition.y + 0.5f, cellPosition.z);
}
}
void OnTriggerStay2D(Collider2D other)
{
if(!_isLadderPositionSet && other.gameObject.tag == "Player" && mov.IsClimbing)
{
_isLadderPositionSet = true;
Vector3 cellPosition = GetPostionOfCollidedTile(other);
Debug.Log("ladder postion: " + cellPosition);
other.gameObject.transform.position = new Vector3(cellPosition.x + 0.5f, other.gameObject.transform.position.y, other.gameObject.transform.position.z);
}
if(_isLadderPositionSet && other.gameObject.tag == "Player" && !mov.IsClimbing)
{
_isLadderPositionSet = false;
}
}
void OnTriggerExit2D(Collider2D other)
{
if(_isLadderPositionSet && other.gameObject.tag == "Player")
{
_isLadderPositionSet = false;
}
}
Vector3 GetPostionOfCollidedTile(Collider2D other)
{
for (int n = tilemap.cellBounds.xMin; n < tilemap.cellBounds.xMax; n++)
{
for (int p = tilemap.cellBounds.yMin; p < tilemap.cellBounds.yMax; p++)
{
Vector3Int localPlace = new Vector3Int(n, p, (int)tilemap.transform.position.y);
Bounds tileBounds = new Bounds(tilemap.CellToWorld(localPlace) + tilemap.cellSize / 2, tilemap.cellSize);
if (other.bounds.Intersects(tileBounds))
{
Vector3 place = tilemap.CellToWorld(localPlace);
return place;
}
}
}
return Vector3.zero;
}
}
It mostly works, and the player successfully snaps to the middle of the ladder. However, I’ve encountered a bug: when the player climbs the ladder from the left side, he is thrown off the ladder, and I can’t figure out why this is happening.
Below is a fragment of the related climbing code in the PlayerMovement
script:
public bool IsClimbing { get; private set; }
private bool _onLadder;
void Update()
{
//On ladder check
if(Physics2D.OverlapBox(_groundCheckPoint.position, _groundCheckSize, 0, _climbingLayer))
{
_onLadder = true;
}
else
{
_onLadder = false;
IsClimbing = false;
}
if(IsClimbing)
{
Climb();
}
if(_moveInput.y != 0 && _onLadder)
{
Climb();
}
}
private void Climb()
{
IsClimbing = true;
LastOnGroundTime = Data.coyoteTime;
Debug.Log("Calculating climb velocity..");
Debug.Log("Y:" + _moveInput.y);
Vector2 climbVelocity = new Vector2(RB.velocity.x, _moveInput.y * Data.climbSpeed);
RB.velocity = climbVelocity;
if(LastPressedJumpTime > 0)
{
IsClimbing = false;
return;
}
}
This is the player’s collider, and the one below it is used to check for ground and ladder layers:
Thanks in advance!