Skip to content

Instantly share code, notes, and snippets.

@dyguests
Last active February 9, 2022 14:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dyguests/64777abc80824b9842abef9a09d3f4bb to your computer and use it in GitHub Desktop.
Save dyguests/64777abc80824b9842abef9a09d3f4bb to your computer and use it in GitHub Desktop.
GroundHelper, PhysicsObject, grounded checker.
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Utils
{
public class GroundHelper
{
private const float minMoveDistance = 0.001f;
private const float shellRadius = 0.01f;
private Rigidbody2D rb;
private Config config;
private IGroundedStateListener groundedStateListener;
// ------------------ runtime variants ------------------
private bool grounded;
private Vector2 groundNormal;
private Vector2 velocity;
private ContactFilter2D contactFilter;
private RaycastHit2D[] hitBuffer = new RaycastHit2D[16];
private List<RaycastHit2D> hitBufferList = new List<RaycastHit2D>(16);
public GroundHelper(Rigidbody2D rb, Config config, IGroundedStateListener groundedStateListener)
{
this.rb = rb;
this.config = config;
this.groundedStateListener = groundedStateListener;
}
/// <summary>
/// this method should be called in host MonoBehaviour.FixedUpdate.
/// </summary>
public void FixedUpdate()
{
var lastGrounded = grounded;
grounded = false;
velocity = rb.velocity + Physics2D.gravity * Time.fixedDeltaTime;
var deltaPosition = velocity * Time.deltaTime;
var moveAlongGround = new Vector2(groundNormal.y, -groundNormal.x);
var move = moveAlongGround * deltaPosition.x;
CheckMove(move, false);
move = Vector2.up * deltaPosition.y;
CheckMove(move, true);
// grounded changed callback.
if (lastGrounded != grounded)
{
groundedStateListener.OnGroundedChanged(grounded);
}
}
private void CheckMove(Vector2 move, bool yMovement)
{
float distance = move.magnitude;
if (distance > minMoveDistance)
{
int count = rb.Cast(move, contactFilter, hitBuffer, distance + shellRadius);
hitBufferList.Clear();
for (int i = 0; i < count; i++)
{
hitBufferList.Add(hitBuffer[i]);
}
for (int i = 0; i < hitBufferList.Count; i++)
{
Vector2 currentNormal = hitBufferList[i].normal;
if (currentNormal.y > config.minGroundNormalY)
{
grounded = true;
if (yMovement)
{
groundNormal = currentNormal;
currentNormal.x = 0;
}
}
float projection = Vector2.Dot(velocity, currentNormal);
if (projection < 0)
{
velocity = velocity - projection * currentNormal;
}
// float modifiedDistance = hitBufferList[i].distance - shellRadius;
// distance = modifiedDistance < distance ? modifiedDistance : distance;
}
}
}
[Serializable]
public class Config
{
[SerializeField] internal float minGroundNormalY = .65f;
}
public interface IGroundedStateListener
{
void OnGroundedChanged(bool grounded);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment