Skip to content

Instantly share code, notes, and snippets.

@devindazzle
Created August 13, 2014 16:29
Show Gist options
  • Save devindazzle/f3b55028fc49f434365d to your computer and use it in GitHub Desktop.
Save devindazzle/f3b55028fc49f434365d to your computer and use it in GitHub Desktop.
CharacterController2D
using System;
using System.Linq;
using UnityEngine;
[RequireComponent(typeof(BoxCollider2D))]
public class CharacterController2D : MonoBehaviour
{
#region Fields and Properties
[Range(.001f, 0.3f)]
public float SkinWidth = .02f;
public LayerMask PlatformMask = 0;
[SerializeField]
private LayerMask OneWayPlatformMask = 0;
public ControllerParameters2D Parameters { get { return _overrideParameters ?? DefaultParameters; } }
public ControllerParameters2D DefaultParameters;
private ControllerParameters2D _overrideParameters;
public bool HandleCollisions { get; set; }
public ControllerState2D State { get; private set; }
[SerializeField]
[Range(2, 20)]
private int TotalHorizontalRays = 4;
[SerializeField]
[Range(2, 20)]
private int TotalVerticalRays = 3;
private Transform _transform;
private BoxCollider2D _boxCollider;
private float
_verticalDistanceBetweenRays,
_horizontalDistanceBetweenRays;
private Vector3
_rayCastTopLeft,
_rayCastBottomRight,
_rayCastBottomLeft;
public bool CanJump
{
get
{
if (Parameters.JumpRestrictions == ControllerParameters2D.JumpBehavior.CanJumpAnywhere)
return _jumpIn <= 0;
if (Parameters.JumpRestrictions == ControllerParameters2D.JumpBehavior.CanJumpOnGround)
return State.IsGrounded;
return false;
}
}
private float _jumpIn;
public Vector2 Velocity { get { return _velocity; } }
private Vector2 _velocity;
#endregion
public void Awake()
{
HandleCollisions = true;
State = new ControllerState2D();
// Add one-way platforms to our normal platform mask so that we can land on them from above
PlatformMask |= OneWayPlatformMask;
// Cache some of the components
_transform = transform;
_boxCollider = GetComponent<BoxCollider2D>();
// Calculate the distance between rays
RecalculateDistanceBetweenRays();
}
public void AddForce(Vector2 force)
{
_velocity += force;
}
public void SetForce(Vector2 force)
{
_velocity = force;
}
public void SetHorizontalForce(float x)
{
_velocity.x = x;
}
public void SetVerticalForce(float y)
{
_velocity.y = y;
}
public void Jump()
{
}
public void LateUpdate()
{
// Jump restrictions
_jumpIn -= Time.deltaTime;
// Move the character
Move(Velocity * Time.deltaTime);
}
private void Move(Vector2 deltaMovement)
{
// Determine if the character was grounded last frame
var wasGrounded = State.IsCollidingBelow;
State.Reset();
if (HandleCollisions)
{
HandlePlatforms();
CalculateRayOrigins();
// Move horizontally
if (Mathf.Abs(deltaMovement.x) > .001f)
MoveHorizontally(ref deltaMovement);
}
// Move the character
_transform.Translate(deltaMovement, Space.World);
if (Time.deltaTime > 0f)
_velocity = deltaMovement/Time.deltaTime;
// Ensure the character is never moving faster the the maximum allowed speed
_velocity.x = Mathf.Min(_velocity.x, Parameters.MaxVelocity.x);
_velocity.y = Mathf.Min(_velocity.y, Parameters.MaxVelocity.y);
// Determine if character became grounded (landed) on this frame
State.BecameGroundedThisFrame = (!wasGrounded && State.IsCollidingBelow);
}
private void HandlePlatforms()
{
}
private void RecalculateDistanceBetweenRays()
{
var colliderWidth = _boxCollider.size.x * Mathf.Abs(_transform.localScale.x) - (2 * SkinWidth);
_horizontalDistanceBetweenRays = colliderWidth / (TotalVerticalRays - 1);
var colliderHeight = _boxCollider.size.y * Mathf.Abs(_transform.localScale.y) - (2 * SkinWidth);
_verticalDistanceBetweenRays = colliderHeight / (TotalHorizontalRays - 1);
}
private void CalculateRayOrigins()
{
var size = new Vector2(_boxCollider.size.x * Mathf.Abs(_transform.localScale.x), _boxCollider.size.y * Mathf.Abs(_transform.localScale.y)) / 2;
var center = new Vector2(_boxCollider.center.x * _transform.localScale.x, _boxCollider.center.y * _transform.localScale.y);
_rayCastTopLeft = _transform.position + new Vector3(center.x - size.x + SkinWidth, center.y + size.y - SkinWidth);
_rayCastBottomRight = _transform.position + new Vector3(center.x + size.x - SkinWidth, center.y - size.y + SkinWidth);
_rayCastBottomLeft = _transform.position + new Vector3(center.x - size.x + SkinWidth, center.y - size.y + SkinWidth);
}
private void MoveHorizontally(ref Vector2 deltaMovement)
{
var isGoingRight = deltaMovement.x > 0;
var rayDistance = Mathf.Abs(deltaMovement.x) + SkinWidth;
var rayDirection = isGoingRight ? Vector2.right : -Vector2.right;
var rayOrigin = isGoingRight ? _rayCastBottomRight : _rayCastBottomLeft;
// Debug.Log("isGoingRight: " + isGoingRight + " Distance: " + rayDistance + " Dírection: " + rayDirection + " Origin: " + rayOrigin);
for (var i = 0; i < TotalHorizontalRays; i++)
{
var rayVector = new Vector2(rayOrigin.x, rayOrigin.y + (i * _verticalDistanceBetweenRays));
Debug.DrawRay(rayVector, rayDirection * rayDistance, Color.red);
var rayCastHit = Physics2D.Raycast(rayVector, rayDirection, rayDistance, PlatformMask);
if (!rayCastHit)
continue;
// if (i == 0 && HandleHorizontalSlope(ref deltaMovement, Vector2.Angle(rayCastHit.normal, Vector2.up), isGoingRight))
// break;
deltaMovement.x = rayCastHit.point.x - rayVector.x;
rayDistance = Mathf.Abs(deltaMovement.x);
if (isGoingRight)
{
deltaMovement.x -= SkinWidth;
State.IsCollidingRight = true;
}
else
{
deltaMovement.x += SkinWidth;
State.IsCollidingLeft = true;
}
if (rayDistance < SkinWidth + .0001f)
break;
}
}
private void MoveVertically(ref Vector2 deltaMovement)
{
}
public void OnTriggerEnter2D(Collider2D other)
{
var parameters = other.gameObject.GetComponent<ControllerPhysicsVolume2D>();
if (parameters == null)
return;
_overrideParameters = parameters.Parameters;
}
public void OnTriggerExit2D(Collider2D other)
{
var parameters = other.gameObject.GetComponent<ControllerPhysicsVolume2D>();
if (parameters == null)
return;
_overrideParameters = null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment