/// <summary> /// /// </summary> public void ProcessMovement(ISurfControllable surfer, MovementConfig config, float deltaTime) { // cache instead of passing around parameters _surfer = surfer; _config = config; _deltaTime = deltaTime; if (!_surfer.moveData.underwater) { if (_surfer.moveData.velocity.y <= 0f) { jumping = false; } // apply gravity if (_surfer.groundObject == null) { _surfer.moveData.velocity.y -= (_surfer.moveData.gravityFactor * _config.gravity * _deltaTime); _surfer.moveData.velocity.y += _surfer.baseVelocity.y * _deltaTime; } // input velocity, check for ground CheckGrounded(); CalculateMovementVelocity(); } else { // Do underwater logic UnderwaterPhysics(); } float yVel = _surfer.moveData.velocity.y; _surfer.moveData.velocity.y = 0f; _surfer.moveData.velocity = Vector3.ClampMagnitude(_surfer.moveData.velocity, _config.maxVelocity); speed = _surfer.moveData.velocity.magnitude; _surfer.moveData.velocity.y = yVel; float maxDistPerFrame = 1f; Vector3 velocityThisFrame = _surfer.moveData.velocity * _deltaTime; float velocityDistLeft = velocityThisFrame.magnitude; float initialVel = velocityDistLeft; while (velocityDistLeft > 0f) { float amountThisLoop = Mathf.Min(maxDistPerFrame, velocityDistLeft); velocityDistLeft -= amountThisLoop; // increment origin Vector3 velThisLoop = velocityThisFrame * (amountThisLoop / initialVel); _surfer.moveData.origin += velThisLoop; // don't penetrate walls SurfPhysics.ResolveCollisions(_surfer.collider, ref _surfer.moveData.origin, ref _surfer.moveData.velocity, _surfer.moveData.rigidbodyPushForce); } _surfer.moveData.groundedTemp = _surfer.moveData.grounded; _surfer = null; }
///// Methods ///// /// <summary> /// /// </summary> public void ProcessMovement(ISurfControllable surfer, MovementConfig config, float deltaTime) { // cache instead of passing around parameters _surfer = surfer; _config = config; _deltaTime = deltaTime; // apply gravity if (_surfer.GroundObject == null) { _surfer.MoveData.Velocity.y -= (_surfer.MoveData.GravityFactor * _config.Gravity * _deltaTime); _surfer.MoveData.Velocity.y += _surfer.BaseVelocity.y * _deltaTime; } // input velocity, check for ground CheckGrounded(); CalculateMovementVelocity(); // increment origin _surfer.MoveData.Origin += _surfer.MoveData.Velocity * _deltaTime; // don't penetrate walls SurfPhysics.ResolveCollisions(_surfer.Collider, ref _surfer.MoveData.Origin, ref _surfer.MoveData.Velocity); _surfer = null; }
///// Methods ///// public static void ResolveCollisions(ISurfControllable controller, StrictCollisionAxis axis = StrictCollisionAxis.None) { var staticOrigin = controller.MoveData.Origin + new Vector3(0, controller.Collider.bounds.extents.y, 0); var numOverlaps = Physics.OverlapBoxNonAlloc(staticOrigin, controller.Collider.bounds.extents, _colliders, controller.Orientation, GroundLayerMask, QueryTriggerInteraction.Ignore); for (int i = 0; i < numOverlaps; i++) { if (Physics.ComputePenetration(controller.Collider, controller.MoveData.Origin, controller.Orientation, _colliders[i], _colliders[i].transform.position, _colliders[i].transform.rotation, out Vector3 direction, out float distance)) { // don't resolve if moving away from it if (Vector3.Dot(direction, controller.MoveData.Velocity.normalized) > 0) { continue; } var penetrationVec = direction * distance; var velocityVec = -Vector3.Project(controller.MoveData.Velocity, -direction); if (axis != StrictCollisionAxis.None) { var horizAdjustment = (Mathf.Abs(penetrationVec.x) + Mathf.Abs(penetrationVec.z)) > Mathf.Abs(penetrationVec.y); if ((axis == StrictCollisionAxis.Horizontal && horizAdjustment) || (axis == StrictCollisionAxis.Vertical && !horizAdjustment)) { controller.MoveData.Origin += penetrationVec; if (controller.MoveData.Surfing) { ClipVelocity(controller.MoveData.Velocity, direction, ref controller.MoveData.Velocity, 1.0f); } else { controller.MoveData.Velocity += velocityVec; } } continue; } controller.MoveData.Origin += penetrationVec; staticOrigin += penetrationVec; if (controller.MoveData.Surfing) { ClipVelocity(controller.MoveData.Velocity, direction, ref controller.MoveData.Velocity, distance); } else { controller.MoveData.Velocity += velocityVec; } } } }
public static void Reflect(ISurfControllable surfer, float deltaTime, Vector3 hitNormal) { var workingVel = surfer.MoveData.JustGrounded ? surfer.MoveData.PreGroundedVelocity : surfer.MoveData.Velocity; workingVel += surfer.MoveData.BaseVelocity; if (hitNormal.y <= SurfSlope || hitNormal.y >= 1) { return; } ClipVelocity(workingVel, hitNormal, ref workingVel, 1f); workingVel -= surfer.MoveData.BaseVelocity; surfer.MoveData.Velocity = workingVel; }
public void CalculateMovement(ISurfControllable surfer, MovementConfig config, float deltaTime) { // cache instead of passing around parameters _surfer = surfer; _config = config; _deltaTime = deltaTime; _surfer.MoveData.JustGrounded = false; _surfer.MoveData.JustJumped = false; if (surfer.MoveData.LimitedExecution) { CalculateSyncMovement(); } else { CheckParameters(); CalculateFullMovement(); } _surfer = null; _config = null; }
public void Crouch(ISurfControllable surfer, MovementConfig config, float deltaTime) { _surfer = surfer; _config = config; _deltaTime = deltaTime; if (_surfer == null) { return; } if (_surfer.collider == null) { return; } bool grounded = _surfer.groundObject != null; bool wantsToCrouch = _surfer.moveData.crouching; float crouchingHeight = Mathf.Clamp(_surfer.moveData.crouchingHeight, 0.01f, 1f); float heightDifference = _surfer.moveData.defaultHeight - _surfer.moveData.defaultHeight * crouchingHeight; if (grounded) { uncrouchDown = false; } // Crouching input if (grounded) { crouchLerp = Mathf.Lerp(crouchLerp, wantsToCrouch ? 1f : 0f, _deltaTime * _surfer.moveData.crouchingSpeed); } else if (!grounded && !wantsToCrouch && crouchLerp < 0.95f) { crouchLerp = 0f; } else if (!grounded && wantsToCrouch) { crouchLerp = 1f; } // Collider and position changing if (crouchLerp > 0.9f && !crouching) { // Begin crouching crouching = true; if (_surfer.collider.GetType() == typeof(BoxCollider)) { // Box collider BoxCollider boxCollider = (BoxCollider)_surfer.collider; boxCollider.size = new Vector3(boxCollider.size.x, _surfer.moveData.defaultHeight * crouchingHeight, boxCollider.size.z); } else if (_surfer.collider.GetType() == typeof(CapsuleCollider)) { // Capsule collider CapsuleCollider capsuleCollider = (CapsuleCollider)_surfer.collider; capsuleCollider.height = _surfer.moveData.defaultHeight * crouchingHeight; } // Move position and stuff _surfer.moveData.origin += heightDifference / 2 * (grounded ? Vector3.down : Vector3.up); foreach (Transform child in playerTransform) { if (child == _surfer.moveData.viewTransform) { continue; } child.localPosition = new Vector3(child.localPosition.x, child.localPosition.y * crouchingHeight, child.localPosition.z); } uncrouchDown = !grounded; } else if (crouching) { // Check if the player can uncrouch bool canUncrouch = true; if (_surfer.collider.GetType() == typeof(BoxCollider)) { // Box collider BoxCollider boxCollider = (BoxCollider)_surfer.collider; Vector3 halfExtents = boxCollider.size * 0.5f; Vector3 startPos = boxCollider.transform.position; Vector3 endPos = boxCollider.transform.position + (uncrouchDown ? Vector3.down : Vector3.up) * heightDifference; Trace trace = Tracer.TraceBox(startPos, endPos, halfExtents, boxCollider.contactOffset, SurfPhysics.groundLayerMask); if (trace.hitCollider != null) { canUncrouch = false; } } else if (_surfer.collider.GetType() == typeof(CapsuleCollider)) { // Capsule collider CapsuleCollider capsuleCollider = (CapsuleCollider)_surfer.collider; Vector3 point1 = capsuleCollider.center + Vector3.up * capsuleCollider.height * 0.5f; Vector3 point2 = capsuleCollider.center + Vector3.down * capsuleCollider.height * 0.5f; Vector3 startPos = capsuleCollider.transform.position; Vector3 endPos = capsuleCollider.transform.position + (uncrouchDown ? Vector3.down : Vector3.up) * heightDifference; Trace trace = Tracer.TraceCapsule(point1, point2, capsuleCollider.radius, startPos, endPos, capsuleCollider.contactOffset, SurfPhysics.groundLayerMask); if (trace.hitCollider != null) { canUncrouch = false; } } // Uncrouch if (canUncrouch && crouchLerp <= 0.9f) { crouching = false; if (_surfer.collider.GetType() == typeof(BoxCollider)) { // Box collider BoxCollider boxCollider = (BoxCollider)_surfer.collider; boxCollider.size = new Vector3(boxCollider.size.x, _surfer.moveData.defaultHeight, boxCollider.size.z); } else if (_surfer.collider.GetType() == typeof(CapsuleCollider)) { // Capsule collider CapsuleCollider capsuleCollider = (CapsuleCollider)_surfer.collider; capsuleCollider.height = _surfer.moveData.defaultHeight; } // Move position and stuff _surfer.moveData.origin += heightDifference / 2 * (uncrouchDown ? Vector3.down : Vector3.up); foreach (Transform child in playerTransform) { child.localPosition = new Vector3(child.localPosition.x, child.localPosition.y / crouchingHeight, child.localPosition.z); } } if (!canUncrouch) { crouchLerp = 1f; } } // Changing camera position if (!crouching) { _surfer.moveData.viewTransform.localPosition = Vector3.Lerp( _surfer.moveData.viewTransformDefaultLocalPos, _surfer.moveData.viewTransformDefaultLocalPos * crouchingHeight + Vector3.down * heightDifference * 0.5f, crouchLerp); } else { _surfer.moveData.viewTransform.localPosition = Vector3.Lerp( _surfer.moveData.viewTransformDefaultLocalPos - Vector3.down * heightDifference * 0.5f, _surfer.moveData.viewTransformDefaultLocalPos * crouchingHeight, crouchLerp); } }
public static bool StepOffset(Collider collider, Collider otherCollider, ref Vector3 origin, ref Vector3 velocity, float rigidbodyPushForce, float velocityMultiplier, float stepOffset, Vector3 direction, float distance, Vector3 forwardVelocity, ISurfControllable surfer) { // Return if step offset is 0 if (stepOffset <= 0f) { return(false); } // Get forward direction (return if we aren't moving/are only moving vertically) Vector3 forwardDirection = forwardVelocity.normalized; if (forwardDirection.sqrMagnitude == 0f) { return(false); } // Trace ground Trace groundTrace = Tracer.TraceCollider(collider, origin, origin + Vector3.down * 0.1f, groundLayerMask); if (groundTrace.hitCollider == null || Vector3.Angle(Vector3.up, groundTrace.planeNormal) > surfer.moveData.slopeLimit) { return(false); } // Trace wall Trace wallTrace = Tracer.TraceCollider(collider, origin, origin + velocity, groundLayerMask, 0.9f); if (wallTrace.hitCollider == null || Vector3.Angle(Vector3.up, wallTrace.planeNormal) <= surfer.moveData.slopeLimit) { return(false); } // Trace upwards (check for roof etc) float upDistance = stepOffset; Trace upTrace = Tracer.TraceCollider(collider, origin, origin + Vector3.up * stepOffset, groundLayerMask); if (upTrace.hitCollider != null) { upDistance = upTrace.distance; } // Don't bother doing the rest if we can't move up at all anyway if (upDistance <= 0f) { return(false); } Vector3 upOrigin = origin + Vector3.up * upDistance; // Trace forwards (check for walls etc) float forwardMagnitude = stepOffset; float forwardDistance = forwardMagnitude; Trace forwardTrace = Tracer.TraceCollider(collider, upOrigin, upOrigin + forwardDirection * Mathf.Max(0.2f, forwardMagnitude), groundLayerMask); if (forwardTrace.hitCollider != null) { forwardDistance = forwardTrace.distance; } // Don't bother doing the rest if we can't move forward anyway if (forwardDistance <= 0f) { return(false); } Vector3 upForwardOrigin = upOrigin + forwardDirection * forwardDistance; // Trace down (find ground) float downDistance = upDistance; Trace downTrace = Tracer.TraceCollider(collider, upForwardOrigin, upForwardOrigin + Vector3.down * upDistance, groundLayerMask); if (downTrace.hitCollider != null) { downDistance = downTrace.distance; } // Check step size/angle float verticalStep = Mathf.Clamp(upDistance - downDistance, 0f, stepOffset); float horizontalStep = forwardDistance; float stepAngle = Vector3.Angle(Vector3.forward, new Vector3(0f, verticalStep, horizontalStep)); if (stepAngle > surfer.moveData.slopeLimit) { return(false); } // Get new position Vector3 endOrigin = origin + Vector3.up * verticalStep; // Actually move if (origin != endOrigin && forwardDistance > 0f) { Debug.Log("Moved up step!"); origin = endOrigin + forwardDirection * forwardDistance * Time.deltaTime; return(true); } else { return(false); } }
///// Methods ///// /// <summary> /// /// </summary> /// <param name="collider"></param> /// <param name="origin"></param> /// <param name="velocity"></param> /// http://www.00jknight.com/blog/unity-character-controller public static void ResolveCollisions(Collider collider, ref Vector3 origin, ref Vector3 velocity, float rigidbodyPushForce, float velocityMultiplier = 1f, float stepOffset = 0f, ISurfControllable surfer = null) { // manual collision resolving int numOverlaps = 0; if (collider is CapsuleCollider) { var capc = collider as CapsuleCollider; Vector3 point1, point2; GetCapsulePoints(capc, origin, out point1, out point2); numOverlaps = Physics.OverlapCapsuleNonAlloc(point1, point2, capc.radius, _colliders, groundLayerMask, QueryTriggerInteraction.Ignore); } else if (collider is BoxCollider) { numOverlaps = Physics.OverlapBoxNonAlloc(origin, collider.bounds.extents, _colliders, Quaternion.identity, groundLayerMask, QueryTriggerInteraction.Ignore); } Vector3 forwardVelocity = Vector3.Scale(velocity, new Vector3(1f, 0f, 1f)); for (int i = 0; i < numOverlaps; i++) { Vector3 direction; float distance; if (Physics.ComputePenetration(collider, origin, Quaternion.identity, _colliders [i], _colliders [i].transform.position, _colliders [i].transform.rotation, out direction, out distance)) { // Step offset if (stepOffset > 0f && surfer != null && surfer.moveData.useStepOffset) { if (StepOffset(collider, _colliders [i], ref origin, ref velocity, rigidbodyPushForce, velocityMultiplier, stepOffset, direction, distance, forwardVelocity, surfer)) { return; } } // Handle collision direction.Normalize(); Vector3 penetrationVector = direction * distance; Vector3 velocityProjected = Vector3.Project(velocity, -direction); velocityProjected.y = 0; // don't touch y velocity, we need it to calculate fall damage elsewhere origin += penetrationVector; velocity -= velocityProjected * velocityMultiplier; Rigidbody rb = _colliders [i].GetComponentInParent <Rigidbody> (); if (rb != null && !rb.isKinematic) { rb.AddForceAtPosition(velocityProjected * velocityMultiplier * rigidbodyPushForce, origin, ForceMode.Impulse); } } } }
/// <summary> /// /// </summary> public void ProcessMovement(ISurfControllable surfer, MovementConfig config, float deltaTime) { // cache instead of passing around parameters _surfer = surfer; _config = config; _deltaTime = deltaTime; if (_surfer.moveData.laddersEnabled && !_surfer.moveData.climbingLadder) { // Look for ladders LadderCheck(new Vector3(1f, 0.95f, 1f), _surfer.moveData.velocity * Mathf.Clamp(Time.deltaTime * 2f, 0.025f, 0.25f)); } if (_surfer.moveData.laddersEnabled && _surfer.moveData.climbingLadder) { LadderPhysics(); } else if (!_surfer.moveData.underwater) { if (_surfer.moveData.velocity.y <= 0f) { jumping = false; } // apply gravity if (_surfer.groundObject == null) { _surfer.moveData.velocity.y -= (_surfer.moveData.gravityFactor * _config.gravity * _deltaTime); _surfer.moveData.velocity.y += _surfer.baseVelocity.y * _deltaTime; } // input velocity, check for ground CheckGrounded(); CalculateMovementVelocity(); } else { // Do underwater logic UnderwaterPhysics(); } float yVel = _surfer.moveData.velocity.y; _surfer.moveData.velocity.y = 0f; _surfer.moveData.velocity = Vector3.ClampMagnitude(_surfer.moveData.velocity, _config.maxVelocity); speed = _surfer.moveData.velocity.magnitude; _surfer.moveData.velocity.y = yVel; if (_surfer.moveData.velocity.sqrMagnitude == 0f) { // Do collisions while standing still SurfPhysics.ResolveCollisions(_surfer.collider, ref _surfer.moveData.origin, ref _surfer.moveData.velocity, _surfer.moveData.rigidbodyPushForce, _surfer.collider.transform.localPosition, 1f, _surfer.moveData.stepOffset, _surfer); } else { float maxDistPerFrame = 0.2f; Vector3 velocityThisFrame = _surfer.moveData.velocity * _deltaTime; float velocityDistLeft = velocityThisFrame.magnitude; float initialVel = velocityDistLeft; while (velocityDistLeft > 0f) { float amountThisLoop = Mathf.Min(maxDistPerFrame, velocityDistLeft); velocityDistLeft -= amountThisLoop; // increment origin Vector3 velThisLoop = velocityThisFrame * (amountThisLoop / initialVel); _surfer.moveData.origin += velThisLoop; // don't penetrate walls SurfPhysics.ResolveCollisions(_surfer.collider, ref _surfer.moveData.origin, ref _surfer.moveData.velocity, _surfer.moveData.rigidbodyPushForce, _surfer.collider.transform.localPosition, amountThisLoop / initialVel, _surfer.moveData.stepOffset, _surfer); } } _surfer.moveData.groundedTemp = _surfer.moveData.grounded; _surfer = null; }
public static void Reflect(ISurfControllable surfer, float deltaTime) { var workingVel = surfer.MoveData.JustGrounded ? surfer.MoveData.PreGroundedVelocity : surfer.MoveData.Velocity; workingVel += surfer.MoveData.BaseVelocity; var clipped = false; //if (contactOffset != 0) //{ // var longSide = Mathf.Sqrt(contactOffset * contactOffset + contactOffset * contactOffset); // distance += longSide; // extents *= (1f - contactOffset); //} //var center = surfer.MoveData.Origin + new Vector3(0, surfer.Collider.bounds.extents.y, 0); //var end = center + (Vector3.down * 0.2f) + (workingVel * deltaTime); //var dist = Vector3.Distance(center, end); //var dir = (end - center).normalized; var center = surfer.MoveData.Origin + new Vector3(0, surfer.Collider.bounds.extents.y + 0.1f, 0); var dist = 0.2f; var dir = (Vector3.down + (workingVel * deltaTime)).normalized; //var dir = (Vector3.down + (workingVel * deltaTime)).normalized; var hitCount = Physics.BoxCastNonAlloc(results: _hits, center: center, direction: dir, orientation: surfer.Orientation, maxDistance: dist, halfExtents: surfer.Collider.bounds.extents, layerMask: GroundLayerMask, queryTriggerInteraction: QueryTriggerInteraction.Ignore); for (int i = 0; i < hitCount; i++) { if (Tracer.HitIsShit(_hits[i]) || _hits[i].collider == null) { continue; } if (_hits[i].normal.y <= SurfSlope || _hits[i].normal.y >= 1) { continue; } var slopeDir = Vector3.Cross(Vector3.up, Vector3.Cross(Vector3.up, _hits[i].normal)); var dot = Vector3.Dot(workingVel.normalized, slopeDir); var goingAgainstSlope = dot > 0; if (!goingAgainstSlope) { continue; } ClipVelocity(workingVel, _hits[i].normal, ref workingVel, 1f); clipped = true; } workingVel -= surfer.MoveData.BaseVelocity; if (clipped) { surfer.MoveData.Velocity = workingVel; } //var oldVelocityMagnitude2d = new Vector2(surfer.MoveData.Velocity.x, surfer.MoveData.Velocity.z).magnitude; // //var newVelocityMagnitude2d = new Vector2(surfer.MoveData.Velocity.x, surfer.MoveData.Velocity.z).magnitude; //float fLateralStoppingAmount = oldVelocityMagnitude2d - newVelocityMagnitude2d; //Debug.Log(fLateralStoppingAmount); }