diff options
Diffstat (limited to 'Assets/Scripts')
| -rw-r--r-- | Assets/Scripts/MovingSphere.cs | 140 |
1 files changed, 125 insertions, 15 deletions
diff --git a/Assets/Scripts/MovingSphere.cs b/Assets/Scripts/MovingSphere.cs index f92b4ea..dfbd378 100644 --- a/Assets/Scripts/MovingSphere.cs +++ b/Assets/Scripts/MovingSphere.cs @@ -17,20 +17,45 @@ public class MovingSphere : MonoBehaviour [SerializeField, Range(0f, 10f)]
float jumpHeight = 2f;
+
[SerializeField, Range(0, 5)]
int maxAirJumps;
bool desiredJump;
int jumpPhase;
+
+ // dont snap to surface if going too fast
+ [SerializeField, Range(0f, 100f)]
+ float maxSnapSpeed = 100f;
+
+ [SerializeField, Min(0f)]
+ float raycastProbeDistance = 1f;
+ [SerializeField]
+ LayerMask probeMask = -1;
+ LayerMask stairsMask = -1;
+
+
+ int stepsSinceLastGrounded;
+ int stepsSinceLastJump;
+
+ Vector3 contactNormal;
+ Vector3 steepNormal;
+
int groundContactCount;
+ int steepContactCount;
bool OnGround => groundContactCount > 0;
+ bool OnSteep => steepContactCount > 0;
- Vector3 contactNormal;
// 1.0 means walls
// 0.0 means floors
[SerializeField, Range(0f, 1f)]
- float maxJumpAngle = 0.44f;
+ float maxSlopeAngle = 0.44f;
+ float minGroundDotProduct;
+
+ [SerializeField, Range(0f, 1f)]
+ float maxStairAngle = 0.6f;
+ float minStairsDotProduct;
Vector3 inputVelocity;
@@ -53,8 +78,11 @@ public class MovingSphere : MonoBehaviour inputVelocity = new Vector3(playerInput.x, 0f, playerInput.y) * maxSpeed;
Color purple = Color.red + Color.blue;
+ //renderer.material.SetColor(
+ // "_BaseColor", purple - (purple * (groundContactCount * 0.33f))
+ // );
renderer.material.SetColor(
- "_BaseColor", purple - (purple * (groundContactCount * 0.33f))
+ "_BaseColor", OnGround ? purple * 0.9f : purple * 0.1f
);
}
@@ -74,17 +102,27 @@ public class MovingSphere : MonoBehaviour }
void ClearState() {
- groundContactCount = 0;
- contactNormal = Vector3.zero;
+ groundContactCount = steepContactCount = 0;
+ contactNormal = steepNormal = Vector3.zero;
}
void UpdateState()
{
+ // if changed in editor, update these
+ minGroundDotProduct = Mathf.Cos(maxSlopeAngle * 90 * Mathf.Deg2Rad);
+ minStairsDotProduct = Mathf.Cos(maxStairAngle * 90 * Mathf.Deg2Rad);
+
+ stepsSinceLastGrounded += 1;
+ stepsSinceLastJump += 1;
velocity = body.velocity;
- if(OnGround)
+ if(OnGround || SnapToGround() || CheckSteepContacts())
{
- jumpPhase = 0;
+ stepsSinceLastGrounded = 0;
+
+ if(stepsSinceLastJump > 1)
+ jumpPhase = 0;
+
if(groundContactCount > 1)
contactNormal.Normalize();
}
@@ -94,15 +132,58 @@ public class MovingSphere : MonoBehaviour void Jump()
{
- if(OnGround || jumpPhase < maxAirJumps)
+ Vector3 jumpDirection;
+
+ if(OnGround)
+ jumpDirection = contactNormal;
+ else if(OnSteep)
{
- jumpPhase += 1;
- float jumpSpeed = Mathf.Sqrt(-2f * Physics.gravity.y * jumpHeight);
- float alignedSpeed = Vector3.Dot(velocity, contactNormal);
- if(alignedSpeed > 0f)
- jumpSpeed = Mathf.Max(jumpSpeed - alignedSpeed, 0f);
- velocity += contactNormal * jumpSpeed;
+ jumpDirection = steepNormal;
+ jumpPhase = 0;
}
+ else if((maxAirJumps > 0) && (jumpPhase <= maxAirJumps))
+ {
+ if(jumpPhase == 0)
+ jumpPhase = 1;
+ jumpDirection = contactNormal;
+ }
+ else
+ return;
+
+ stepsSinceLastJump = 0;
+ jumpPhase += 1;
+ float jumpSpeed = Mathf.Sqrt(-2f * Physics.gravity.y * jumpHeight);
+ jumpDirection = (jumpDirection + Vector3.up).normalized;
+ float alignedSpeed = Vector3.Dot(velocity, jumpDirection);
+ if(alignedSpeed > 0f)
+ jumpSpeed = Mathf.Max(jumpSpeed - alignedSpeed, 0f);
+ velocity += jumpDirection * jumpSpeed;
+ }
+
+ bool SnapToGround()
+ {
+ if(stepsSinceLastGrounded > 1 || stepsSinceLastJump <= 2)
+ return false;
+ float speed = velocity.magnitude;
+ if(speed > maxSnapSpeed)
+ return false;
+ if(!Physics.Raycast(
+ body.position,
+ Vector3.down,
+ out RaycastHit hit,
+ raycastProbeDistance,
+ probeMask
+ ))
+ return false;
+ if(hit.normal.y < GetMinDot(hit.collider.gameObject.layer))
+ return false;
+
+ groundContactCount = 1;
+ contactNormal = hit.normal;
+ float dot = Vector3.Dot(velocity, hit.normal);
+ if(dot > 0f)
+ velocity = (velocity - hit.normal * dot).normalized * speed;
+ return true;
}
void OnCollisionStay(Collision collision) {
@@ -114,14 +195,20 @@ public class MovingSphere : MonoBehaviour }
void EvaluateCollision(Collision collision) {
+ float minDot = GetMinDot(collision.gameObject.layer);
for(int i = 0; i < collision.contactCount; i++)
{
Vector3 normal = collision.GetContact(i).normal;
- if(normal.y >= (1f - maxJumpAngle))
+ if(normal.y >= minDot)
{
groundContactCount += 1;
contactNormal += normal;
}
+ else if(normal.y > -0.01f)
+ {
+ steepContactCount += 1;
+ steepNormal += normal;
+ }
}
}
@@ -156,4 +243,27 @@ public class MovingSphere : MonoBehaviour {
return vector - contactNormal * Vector3.Dot(vector, contactNormal);
}
+
+ float GetMinDot(int layer)
+ {
+ if ((stairsMask & (1 << layer)) > 0)
+ return minStairsDotProduct;
+ else
+ return minGroundDotProduct;
+ }
+
+ bool CheckSteepContacts()
+ {
+ if(steepContactCount > 1)
+ {
+ steepNormal.Normalize();
+ if(steepNormal.y >= minGroundDotProduct)
+ {
+ groundContactCount = 1;
+ contactNormal = steepNormal;
+ return true;
+ }
+ }
+ return false;
+ }
}
|
