From f4323d161a589b076b0ccd64e2a1cffcd09b0681 Mon Sep 17 00:00:00 2001 From: realtradam Date: Fri, 9 Dec 2022 21:44:21 -0500 Subject: movement mechanics more complete --- Assets/Scripts/MovingSphere.cs | 140 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 125 insertions(+), 15 deletions(-) (limited to 'Assets/Scripts') 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; + } } -- cgit v1.2.3