Unity 2D Raycast Non-Slope Mover

using System.Collections.Generic; using System.Linq; using UnityEngine; namespace Game.Player.Character.Movers.Custom { [RequireComponent(typeof(BoxCollider2D))] public class CharacterMover2D : MonoBehaviour { [SerializeField] private int _maxVerticalRays = 3; [SerializeField] private int _maxHorizontalRays = 3; [SerializeField] private float _skinWidth = 0.1f; [SerializeField] private LayerMask _collisionMask; public CollisionData Collisions; private BoxCollider2D _collider; private ColliderStats _colliderStats; private void Awake() { _collider = GetComponent<BoxCollider2D>(); _colliderStats = CalculateColliderStats(); } public void Move(Vector2 velocity) { MoveHorizontal(velocity.x); MoveVertical(velocity.y); } private void MoveHorizontal(float velX) { Collisions.Left = false; Collisions.Right = false; if (Mathf.Abs(velX) < 0.0001f) return; float xDist = Mathf.Abs(velX); int xDir = (int) Mathf.Sign(velX); RayHitData hits = CastHorizontalRays(xDist, xDir); if (hits.Nearest.HasValue) { transform.position = transform.position + new Vector3((hits.Nearest.Value.distance - _skinWidth) * xDir, 0, 0); } else transform.position = transform.position + new Vector3(xDist * xDir, 0, 0); } private void MoveVertical(float velY) { Collisions.Below = false; Collisions.Above = false; float yDist = Mathf.Abs(velY) + _skinWidth; int yDir = (int) Mathf.Sign(velY); RayHitData hits = CastVerticalRays(yDist, yDir); if (hits.Nearest.HasValue) { transform.position = transform.position + new Vector3(0, (hits.Nearest.Value.distance - _skinWidth) * yDir, 0); } else { transform.position = transform.position + new Vector3(0, yDist * yDir, 0); } } private RayHitData CastVerticalRays(float yDist, int yDir) { float raySegmentSep = _collider.size.x / (_maxVerticalRays - 1); float colliderLeft = transform.position.x + _colliderStats.LeftDistance; float colliderBottom = transform.position.y + _colliderStats.BottomDistance; float colliderTop = transform.position.y + _colliderStats.TopDistance; float colliderYOrigin = (yDir == 1) ? colliderTop + _skinWidth : colliderBottom - _skinWidth; var hits = new List<RaycastHit2D>(); for (int i = 0; i < _maxVerticalRays; i++) { Vector2 rayOrigin = new Vector2(colliderLeft + (i * raySegmentSep), colliderYOrigin); RaycastHit2D hitInfo = Physics2D.Raycast(rayOrigin, Vector2.up * yDir, yDist, _collisionMask); Debug.DrawLine(rayOrigin, rayOrigin + Vector2.up * (yDist * yDir), Color.red, 0.16f); if (hitInfo.collider != null) { hits.Add(hitInfo); } } RaycastHit2D? nearest = null; if (hits.Count > 0) { foreach (RaycastHit2D hit in hits) { if (!nearest.HasValue || hit.distance < nearest.Value.distance) nearest = hit; } if (yDir == 1) Collisions.Above = true; else if (yDir == -1) Collisions.Below = true; } return new RayHitData { Hits = hits, Nearest = nearest }; } private RayHitData CastHorizontalRays(float xDist, int xDir) { float raySegmentSep = _collider.size.x / (_maxHorizontalRays - 1); float colliderRight = transform.position.x + _colliderStats.RightDistance; float colliderLeft = transform.position.x + _colliderStats.LeftDistance; float colliderTop = transform.position.y + _colliderStats.TopDistance; float colliderXOrigin = (xDir == 1) ? colliderRight + _skinWidth : colliderLeft - _skinWidth; var hits = new List<RaycastHit2D>(); for (int i = 0; i < _maxVerticalRays; i++) { Vector2 rayOrigin = new Vector2(colliderXOrigin, colliderTop - (i * raySegmentSep)); RaycastHit2D hitInfo = Physics2D.Raycast(rayOrigin, Vector2.right * xDir, xDist, _collisionMask); Debug.DrawLine(rayOrigin, rayOrigin + Vector2.right * (xDist * xDir), Color.red, 0.16f); if (hitInfo.collider != null) { hits.Add(hitInfo); } } RaycastHit2D? nearest = null; if (hits.Count > 0) { foreach (RaycastHit2D hit in hits) { if (!nearest.HasValue || hit.distance < nearest.Value.distance) nearest = hit; } if (xDir == 1) Collisions.Right = true; else if (xDir == -1) Collisions.Left = true; } return new RayHitData { Hits = hits, Nearest = nearest }; } /// <summary> /// This should be called anytime the colliders' size changes. /// </summary> private ColliderStats CalculateColliderStats() { ColliderStats newStats; newStats.RightDistance = _collider.bounds.size.x / 2; newStats.LeftDistance = -newStats.RightDistance; newStats.TopDistance = _collider.bounds.size.y / 2; newStats.BottomDistance = -newStats.TopDistance; return newStats; } } internal struct RayHitData { public RaycastHit2D? Nearest; public List<RaycastHit2D> Hits; } public struct CollisionData { public bool Right; public bool Left; public bool Below; public bool Above; } public struct ColliderStats { public float RightDistance; public float LeftDistance; public float TopDistance; public float BottomDistance; } }
A 2D raycast free mover that doesnt handle slopes. Could be buggy, who knows.

Be the first to comment

You can use [html][/html], [css][/css], [php][/php] and more to embed the code. Urls are automatically hyperlinked. Line breaks and paragraphs are automatically generated.