/// <summary> /// Checks if the character size fits at a specific location. /// </summary> public bool CheckBodySize(Vector3 size, Vector3 position, HitInfoFilter hitInfoFilter) { Vector3 bottom = characterActor.GetBottomCenter(position, size); Vector3 top = characterActor.GetTopCenter(position, size); float radius = size.x / 2f - CharacterConstants.SkinWidth; // GetBottomCenterToTopCenter.normalized ---> Up Vector3 castDisplacement = characterActor.GetBottomCenterToTopCenter(size) + characterActor.Up * CharacterConstants.SkinWidth; HitInfo hitInfo; physicsComponent.SphereCast( out hitInfo, bottom, radius, castDisplacement, hitInfoFilter ); bool overlap = hitInfo.hit; return(!overlap); }
/// <summary> /// Forces the character to be grounded (isGrounded = true) if possible. The detection distance includes the step down distance. /// </summary> public void ForceGrounded() { HitInfoFilter filter = new HitInfoFilter(PhysicsComponent.CollisionLayerMask, false, true); CollisionInfo collisionInfo; bool hit = characterCollisions.CheckForGround( out collisionInfo, Position, BodySize.y * 0.8f, // 80% of the height stepDownDistance, filter ); if (hit) { ProcessNewGround(collisionInfo.hitInfo.transform, collisionInfo); float slopeAngle = Vector3.Angle(Up, GetGroundSlopeNormal(collisionInfo)); if (slopeAngle <= slopeLimit) { // Save the ground collision info characterCollisionInfo.SetGroundInfo(collisionInfo, this); Position += collisionInfo.displacement; } } }
/// <summary> /// Checks if the new character size fits in place. If this check is valid then the real size of the character is changed. /// </summary> public bool SetBodySize(Vector2 size) { HitInfoFilter filter = new HitInfoFilter(PhysicsComponent.CollisionLayerMask, true, true); if (!characterCollisions.CheckBodySize(size, Position, filter)) { return(false); } targetBodySize = size; return(true); }
void UpdateEdgeInfo(ref CollisionInfo collisionInfo, Vector3 position, HitInfoFilter hitInfoFilter) { Vector3 center = characterActor.GetBottomCenter(position, characterActor.StepOffset); Vector3 castDirection = (collisionInfo.hitInfo.point - center).normalized; Vector3 castDisplacement = castDirection * CharacterConstants.EdgeRaysCastDistance; Vector3 upperHitPosition = center + characterActor.Up * CharacterConstants.EdgeRaysSeparation; Vector3 lowerHitPosition = center - characterActor.Up * CharacterConstants.EdgeRaysSeparation; HitInfo upperHitInfo; physicsComponent.Raycast( out upperHitInfo, upperHitPosition, castDisplacement, hitInfoFilter ); HitInfo lowerHitInfo; physicsComponent.Raycast( out lowerHitInfo, lowerHitPosition, castDisplacement, hitInfoFilter ); collisionInfo.edgeUpperNormal = upperHitInfo.normal; collisionInfo.edgeLowerNormal = lowerHitInfo.normal; collisionInfo.edgeUpperSlopeAngle = Vector3.Angle(collisionInfo.edgeUpperNormal, characterActor.Up); collisionInfo.edgeLowerSlopeAngle = Vector3.Angle(collisionInfo.edgeLowerNormal, characterActor.Up); collisionInfo.edgeAngle = Vector3.Angle(collisionInfo.edgeUpperNormal, collisionInfo.edgeLowerNormal); collisionInfo.isAnEdge = CustomUtilities.isBetween(collisionInfo.edgeAngle, CharacterConstants.MinEdgeAngle, CharacterConstants.MaxEdgeAngle, true); collisionInfo.isAStep = CustomUtilities.isBetween(collisionInfo.edgeAngle, CharacterConstants.MinStepAngle, CharacterConstants.MaxStepAngle, true); }
protected override void Awake() { base.Awake(); ledgeHitInfoFilter = new HitInfoFilter(layerMask, true, true); }
public void FireRaysArray(out RayArrayInfo rayArrayInfo, ref Vector3 position, float stepUpDistance, float stepDownDistance, HitInfoFilter hitInfoFilter) { int rings = 3; int rotationSubdivisions = 8; rayArrayInfo = new RayArrayInfo(); int hits = 0; float radius = characterActor.BodySize.x / 2f - CharacterConstants.SkinWidth; float skin = stepUpDistance + CharacterConstants.SkinWidth; float extraDistance = Mathf.Max(CharacterConstants.GroundCheckDistance, stepDownDistance); Vector3 castDisplacement = -characterActor.Up * (skin + extraDistance); HitInfo hitInfo; float deltaRotationAngle = 360f / rotationSubdivisions; float deltaRadius = radius / rings; Vector3 arrayOrigin = position + characterActor.Up * skin; for (int i = 0; i < rings; i++) { if (i == 0) { Vector3 origin = arrayOrigin; physicsComponent.Raycast( out hitInfo, origin, castDisplacement, hitInfoFilter ); if (hitInfo.hit) { hits++; rayArrayInfo.averageDistance += hitInfo.distance; rayArrayInfo.averageNormal += hitInfo.normal; // Debug.DrawLine( origin , hitInfo.point ); } } else { Vector3 ray = characterActor.Forward * (i * deltaRadius); for (int j = 0; j < rotationSubdivisions; j++) { ray = Quaternion.AngleAxis(j * deltaRotationAngle, characterActor.Up) * ray; Vector3 origin = arrayOrigin + ray; physicsComponent.Raycast( out hitInfo, origin, castDisplacement, hitInfoFilter ); if (hitInfo.hit) { hits++; rayArrayInfo.averageDistance += hitInfo.distance; rayArrayInfo.averageNormal += hitInfo.normal; // Debug.DrawLine( origin , hitInfo.point ); } } } } rayArrayInfo.averageDistance /= hits; rayArrayInfo.averageNormal.Normalize(); position -= characterActor.Up * (rayArrayInfo.averageDistance - (stepUpDistance + CharacterConstants.SkinWidth)); }
/// <summary> /// Checks vertically for the ground using a Raycast. /// </summary> public bool CheckForGroundRay(out CollisionInfo collisionInfo, Vector3 position, float stepOffset, float stepDownDistance, HitInfoFilter hitInfoFilter) { collisionInfo = new CollisionInfo(); float skin = stepOffset + CharacterConstants.SkinWidth + characterActor.BodySize.x; float extraDistance = Mathf.Max(CharacterConstants.GroundCheckDistance, stepDownDistance); Vector3 castDisplacement = -characterActor.Up * (skin + extraDistance); Vector3 origin = characterActor.GetBottomCenter(position, stepOffset); HitInfo hitInfo; int hits = physicsComponent.Raycast( out hitInfo, origin, castDisplacement, hitInfoFilter ); UpdateCollisionInfo(out collisionInfo, position, hitInfo, castDisplacement, skin, false, hitInfoFilter); return(collisionInfo.collision); }
void UpdateCollisionInfo(out CollisionInfo collisionInfo, Vector3 position, HitInfo hitInfo, Vector3 castDisplacement, float skin, bool calculateEdge = true, HitInfoFilter hitInfoFilter = new HitInfoFilter()) { collisionInfo = new CollisionInfo(); collisionInfo.collision = hitInfo.hit; if (collisionInfo.collision) { collisionInfo.displacement = castDisplacement.normalized * (hitInfo.distance - skin); collisionInfo.hitInfo = hitInfo; collisionInfo.contactSlopeAngle = Vector3.Angle(characterActor.Up, hitInfo.normal); if (calculateEdge) { UpdateEdgeInfo(ref collisionInfo, position, hitInfoFilter); } } else { collisionInfo.displacement = castDisplacement.normalized * (castDisplacement.magnitude - skin); } }
/// <summary> /// Checks if the character size fits in place. /// </summary> public bool CheckBodySize(Vector3 size, HitInfoFilter hitInfoFilter) { return(CheckBodySize(size, characterActor.Position, hitInfoFilter)); }
/// <summary> /// Checks if the character is currently overlapping with any obstacle from a given layermask. /// </summary> public bool CheckOverlapWithLayerMask(Vector3 footPosition, float bottomOffset, HitInfoFilter hitInfoFilter) { Vector3 bottom = characterActor.GetBottomCenter(footPosition, bottomOffset); Vector3 top = characterActor.GetTopCenter(footPosition); float radius = characterActor.BodySize.x / 2f - CharacterConstants.SkinWidth; bool overlap = physicsComponent.OverlapCapsule( bottom, top, radius, hitInfoFilter ); return(overlap); }
public bool CastBodyVertically(out CollisionInfo collisionInfo, Vector3 position, float verticalComponent, float backstepDistance, HitInfoFilter hitInfoFilter, bool ignoreOverlaps) { collisionInfo = new CollisionInfo(); float skin = backstepDistance + CharacterConstants.SkinWidth; Vector3 castDirection = verticalComponent > 0 ? characterActor.Up : -characterActor.Up; Vector3 center = verticalComponent > 0 ? characterActor.GetTopCenter(position) - castDirection * skin : characterActor.GetBottomCenter(position) - castDirection * skin; float castMagnitude = Mathf.Max(Mathf.Abs(verticalComponent) + skin, 0.02f); Vector3 castDisplacement = castDirection * castMagnitude; float minimumDistance = ignoreOverlaps ? backstepDistance : 0f; HitInfo hitInfo; int hits = physicsComponent.SphereCast( out hitInfo, center, characterActor.BodySize.x / 2f - CharacterConstants.SkinWidth, castDisplacement, hitInfoFilter ); UpdateCollisionInfo(out collisionInfo, position, hitInfo, castDisplacement, skin, true, hitInfoFilter); return(collisionInfo.collision); }
public bool CastBody(out CollisionInfo collisionInfo, Vector3 position, Vector3 displacement, float bottomOffset, HitInfoFilter hitInfoFilter) { collisionInfo = new CollisionInfo(); float backstepDistance = 0.1f; float skin = CharacterConstants.SkinWidth + backstepDistance; Vector3 bottom = characterActor.GetBottomCenter(position, bottomOffset); Vector3 top = characterActor.GetTopCenter(position); bottom -= displacement.normalized * backstepDistance; top -= displacement.normalized * backstepDistance; float radius = characterActor.BodySize.x / 2f - CharacterConstants.SkinWidth; Vector3 castDisplacement = displacement + displacement.normalized * skin; HitInfo hitInfo; int hits = 0; if (IsASphere) { hits = physicsComponent.SphereCast( out hitInfo, bottom, radius, castDisplacement, hitInfoFilter ); } else { hits = physicsComponent.CapsuleCast( out hitInfo, bottom, top, radius, castDisplacement, hitInfoFilter ); } UpdateCollisionInfo(out collisionInfo, position, hitInfo, castDisplacement, skin, true, hitInfoFilter); return(collisionInfo.collision); }
/// <summary> /// Checks for the ground. /// </summary> public bool CheckForStableGround(out CollisionInfo collisionInfo, Vector3 position, Vector3 direction, float stepOffset, HitInfoFilter hitInfoFilter) { collisionInfo = new CollisionInfo(); Vector3 origin = characterActor.GetBottomCenter(position); float radius = characterActor.BodySize.x / 2f - CharacterConstants.SkinWidth; float skin = CharacterConstants.SkinWidth; Vector3 castDisplacement = direction * (5f * CharacterConstants.GroundCheckDistance + skin + stepOffset); HitInfo hitInfo; int hits = physicsComponent.SphereCast( out hitInfo, origin, radius, castDisplacement, hitInfoFilter ); UpdateCollisionInfo(out collisionInfo, position, hitInfo, castDisplacement, skin, true, hitInfoFilter); return(collisionInfo.collision); }
void ProbeGround(ref Vector3 position, float dt) { Vector3 preProbeGroundPosition = position; float groundCheckDistance = edgeCompensation ? BodySize.x / 2f + CharacterConstants.GroundCheckDistance : CharacterConstants.GroundCheckDistance; Vector3 displacement = -Up *Mathf.Max(groundCheckDistance, stepDownDistance); HitInfoFilter filter = new HitInfoFilter(PhysicsComponent.CollisionLayerMask, false, true); CollisionInfo collisionInfo; bool hit = characterCollisions.CheckForGround( out collisionInfo, position, StepOffset, stepDownDistance, filter ); if (hit) { float slopeAngle = Vector3.Angle(Up, GetGroundSlopeNormal(collisionInfo)); if (slopeAngle <= slopeLimit) { // Stable hit --------------------------------------------------- ProcessNewGround(collisionInfo.hitInfo.transform, collisionInfo); // Save the ground collision info characterCollisionInfo.SetGroundInfo(collisionInfo, this); // Calculate the final position position += collisionInfo.displacement; if (edgeCompensation && IsAStableEdge(collisionInfo)) { // calculate the edge compensation and apply that to the final position Vector3 compensation = Vector3.Project((collisionInfo.hitInfo.point - position), Up); position += compensation; } stableProbeGroundVelocity = (position - preProbeGroundPosition) / dt; } else { // Unstable Hit // If the unstable ground is far enough then force not grounded float castSkinDistance = StepOffset + 2f * CharacterConstants.SkinWidth; if (collisionInfo.hitInfo.distance > castSkinDistance) { ForceNotGrounded(); return; } if (preventBadSteps) { // If the unstable ground is close enough then do a new collide and slide if (WasGrounded) { position = Position; Vector3 unstableDisplacement = CustomUtilities.ProjectVectorOnPlane( Velocity * dt, GroundStableNormal, Up ); CollideAndSlide(ref position, unstableDisplacement, true); } } characterCollisions.CheckForGroundRay( out collisionInfo, position, StepOffset, stepDownDistance, filter ); ProcessNewGround(collisionInfo.hitInfo.transform, collisionInfo); characterCollisionInfo.SetGroundInfo(collisionInfo, this); Debug.DrawRay(collisionInfo.hitInfo.point, collisionInfo.hitInfo.normal); stableProbeGroundVelocity = (position - preProbeGroundPosition) / dt; } } else { ForceNotGrounded(); } }
public void CollideAndSlide(ref Vector3 position, Vector3 displacement, bool useFullBody) { Vector3 groundPlaneNormal = GroundStableNormal; Vector3 slidingPlaneNormal = Vector3.zero; HitInfoFilter filter = new HitInfoFilter( PhysicsComponent.CollisionLayerMask, false, true ); int iteration = 0; while (iteration < CharacterConstants.MaxSlideIterations) { iteration++; CollisionInfo collisionInfo; bool hit = characterCollisions.CastBody( out collisionInfo, position, displacement, useFullBody ? 0f : StepOffset, filter ); if (hit) { //--- if (canPushDynamicRigidbodies) { if (collisionInfo.hitInfo.IsRigidbody) { if (!collisionInfo.hitInfo.IsKinematicRigidbody) { bool canPushThisObject = CustomUtilities.BelongsToLayerMask(collisionInfo.hitInfo.transform.gameObject.layer, pushableRigidbodyLayerMask); if (canPushThisObject) { // Use the entire displacement and stop the collide and slide position += displacement; break; } } } } if (slideOnWalls) { position += collisionInfo.displacement; displacement -= collisionInfo.displacement; // Get the new slide plane bool blocked = UpdateSlidingPlanes( collisionInfo, ref slidingPlaneNormal, ref groundPlaneNormal, ref displacement ); } else { if (!WallCollision) { position += collisionInfo.displacement; } break; } } else { position += displacement; break; } } }
public void CollideAndSlideUnstable(ref Vector3 position, Vector3 displacement) { HitInfoFilter filter = new HitInfoFilter( PhysicsComponent.CollisionLayerMask, false, true ); int iteration = 0; while (iteration < CharacterConstants.MaxSlideIterations || displacement == Vector3.zero) { iteration++; CollisionInfo collisionInfo; bool hit = characterCollisions.CastBody( out collisionInfo, position, displacement, 0f, filter ); if (hit) { position += collisionInfo.displacement; if (canPushDynamicRigidbodies) { if (collisionInfo.hitInfo.IsRigidbody) { if (!collisionInfo.hitInfo.IsKinematicRigidbody) { Vector3 remainingVelocity = displacement - collisionInfo.displacement; Vector3 force = CharacterBody.Mass * (remainingVelocity / Time.deltaTime); collisionInfo.hitInfo.rigidbody3D.AddForceAtPosition(force, position); } } } displacement -= collisionInfo.displacement; displacement = Vector3.ProjectOnPlane(displacement, collisionInfo.hitInfo.normal); } else { position += displacement; break; } } if (!alwaysNotGrounded && forceNotGroundedFrames == 0) { CollisionInfo groundCheckCollisionInfo; characterCollisions.CheckForGround( out groundCheckCollisionInfo, position, stepUpDistance, CharacterConstants.GroundPredictionDistance, filter ); if (groundCheckCollisionInfo.collision) { PredictedGround = groundCheckCollisionInfo.hitInfo.transform.gameObject; PredictedGroundDistance = groundCheckCollisionInfo.displacement.magnitude; bool validGround = PredictedGroundDistance <= CharacterConstants.GroundCheckDistance; if (validGround) { ProcessNewGround(groundCheckCollisionInfo.hitInfo.transform, groundCheckCollisionInfo); characterCollisionInfo.SetGroundInfo(groundCheckCollisionInfo, this); } else { characterCollisionInfo.ResetGroundInfo(); } } else { PredictedGround = null; PredictedGroundDistance = 0f; characterCollisionInfo.ResetGroundInfo(); } } }