using System.Collections.Generic;
using UnityEngine;
using VContainer;
public class PlayerDetection : MonoBehaviour
{
[Header("Links")]
private CapsuleCollider controller;
[Header("Settings, Raycast")]
[SerializeField] LayerMask groundMask;
[SerializeField] float skinWidth = 0.2f;
[SerializeField] float roofDistance = 1f;
[SerializeField] float minAirTime = 0.1f;
[SerializeField] float minLandingVelocity = 2;
[Header("Settings, Slope")]
[SerializeField] LayerMask stairs;
[SerializeField] float slopeAngle = 45;
[SerializeField] float maxSlopeAngle = 89;
public RaycastHit GroundHit {get; private set;}
private float airTime = 0;
private float peakFallVelocity = 0;
public bool JustLanded {get; private set;}
public bool IsGrounded {get; private set;}
public bool IsSurfing {get; private set;}
public bool TochedRoofInFrame {get; private set;}
private Vector3 wallHit;
private List<Vector3> HitNormalsInFrame = new List<Vector3>();
[Inject]
public void Construct(PlayerView view)
{
controller = view.Capsule;
}
public void PreTick(PlayerContext ctx)
{
GroundCheck(ctx);
}
public void PostTick(PlayerContext ctx)
{
foreach (var v in HitNormalsInFrame)
{
if (ctx.IsSurfing && Vector3.Dot(v, ctx.SlideNormal) > 0.8f) continue;
if(Vector3.Dot(ctx.velocity, v) < 0)
{
ctx.velocity = QuakeMathf.ClipVelocity(ctx.velocity, v);
}
}
if(TochedRoofInFrame&& ctx.velocity.y > 0)
{
ctx.velocity.y = 0;
}
TochedRoofInFrame = false;
HitNormalsInFrame.Clear();
}
public bool RoofDetection()
{
float capsuleBottom = controller.center.y - (controller.height*0.5f) + controller.radius;
float capsuleTop = controller.center.y + (controller.height*0.5f) + controller.radius;
Vector3 bottom = transform.position + new Vector3(controller.center.x,capsuleBottom, controller.center.z);
Vector3 top = transform.position + new Vector3(controller.center.x, capsuleTop, controller.center.z);
return Physics.CheckCapsule(bottom,top, controller.radius*0.9f, groundMask);
}
private void GroundCheck(PlayerContext ctx)
{
float bottomOffset = controller.center.y - (controller.height * 0.5f) + controller.radius;
Vector3 origin = transform.position + new Vector3(controller.center.x, bottomOffset, controller.center.z);
float radius = controller.radius*0.9f;
float currentDist = IsGrounded&&!ctx.JumpRequest? skinWidth + 0.2f : skinWidth;
RaycastHit hit;
bool grounded = Physics.SphereCast(
origin,
radius,
Vector3.down,
out hit,
currentDist,
groundMask
);
GroundHit = hit;
bool wasIsGrounded = IsGrounded;
if (!grounded)
{
airTime += Time.deltaTime;
if (ctx.velocity.y < 0)
peakFallVelocity = Mathf.Min(peakFallVelocity, ctx.velocity.y);
}
if (grounded)
{
float angle = Vector3.Angle(hit.normal, Vector3.up);
IsSurfing = angle > slopeAngle && angle <= maxSlopeAngle;
IsGrounded = !IsSurfing;
}
else
{
IsSurfing = false;
IsGrounded = false;
}
JustLanded = !wasIsGrounded && IsGrounded
&& airTime >= minAirTime
&& Mathf.Abs(peakFallVelocity) >= minLandingVelocity;
if (IsGrounded || IsSurfing)
{
airTime = 0f;
peakFallVelocity = 0f;
}
ctx.IsGrounded = IsGrounded;
ctx.IsSurfing = IsSurfing;
ctx.WallNormal = wallHit;
ctx.SlideNormal = grounded ? hit.normal : Vector3.up;
}
void OnCollisionStay(Collision collision)
{
for (int i = 0; i < collision.contactCount; i++)
{
var c = collision.GetContact(i);
if(c.normal.y < -0.3f)
{
TochedRoofInFrame = true;
continue;
}
if (Mathf.Abs(c.normal.y) > 0.05f) continue;
bool exists = false;
for (int j = 0; j < HitNormalsInFrame.Count; j++)
{
if (Vector3.Dot(HitNormalsInFrame[j], c.normal) > 0.85f)
{
exists = true;
break;
}
}
if (!exists)
HitNormalsInFrame.Add(c.normal);
}
if (collision.contactCount > 0)
wallHit = collision.GetContact(0).normal;
}
}
Hello everyone
I trying make best character controller MYSELF NOT USE A CONTROLLER FROM STORE
So well. I have a issue with slopes angle because i use simple sphere cast bottom my character
This raycast checks all edges as surf surfaces, for example a stairs just a simple probuilder stairs have a problems with them because their edges angles like 80 and surfing turn on