/// <summary> /// Handles horizontal movement on the XZ plane. /// </summary> /// <param name="horizontalVelocity"></param> /// <param name="entity"></param> /// <param name="currPos"></param> /// <param name="currRot"></param> /// <param name="controller"></param> /// <param name="collider"></param> /// <param name="collisionWorld"></param> private void HandleHorizontalMovement( ref float3 horizontalVelocity, ref Entity entity, ref float3 currPos, ref quaternion currRot, ref CharacterControllerData controller, ref PhysicsCollider collider, ref CollisionWorld collisionWorld) { if (MathUtils.IsZero(horizontalVelocity)) { return; } float3 targetPos = currPos + horizontalVelocity; NativeList <ColliderCastHit> horizontalCollisions = PhysicsUtils.ColliderCastAll(collider, currPos, targetPos, ref collisionWorld, entity, Allocator.Temp); PhysicsUtils.TrimByFilter(ref horizontalCollisions, ColliderData, PhysicsCollisionFilters.DynamicWithPhysical); if (horizontalCollisions.Length > 0) { // We either have to step or slide as something is in our way. float3 step = new float3(0.0f, controller.maxStep, 0.0f); PhysicsUtils.ColliderCast(out ColliderCastHit nearestStepHit, collider, targetPos + step, targetPos, ref collisionWorld, entity, PhysicsCollisionFilters.DynamicWithPhysical, null, ColliderData, Allocator.Temp); if (!MathUtils.IsZero(nearestStepHit.Fraction)) { // We can step up. targetPos += (step * (1.0f - nearestStepHit.Fraction)); horizontalVelocity = targetPos - currPos; } else { // We can not step up, so slide. NativeList <DistanceHit> horizontalDistances = PhysicsUtils.ColliderDistanceAll(collider, 1.0f, new RigidTransform() { pos = currPos + horizontalVelocity, rot = currRot }, ref collisionWorld, entity, Allocator.Temp); PhysicsUtils.TrimByFilter(ref horizontalDistances, ColliderData, PhysicsCollisionFilters.DynamicWithPhysical); for (int i = 0; i < horizontalDistances.Length; ++i) { if (horizontalDistances[i].Distance >= 0.0f) { continue; } horizontalVelocity += (horizontalDistances[i].SurfaceNormal * -horizontalDistances[i].Distance); } horizontalDistances.Dispose(); } } horizontalCollisions.Dispose(); }
/// <summary> /// Handles vertical movement from gravity and jumping. /// </summary> /// <param name="entity"></param> /// <param name="currPos"></param> /// <param name="currRot"></param> /// <param name="controller"></param> /// <param name="collider"></param> /// <param name="collisionWorld"></param> private void HandleVerticalMovement( ref float3 verticalVelocity, ref Entity entity, ref float3 currPos, ref quaternion currRot, ref CharacterControllerData controller, ref PhysicsCollider collider, ref CollisionWorld collisionWorld, float DeltaTime) { controller.verticalVelocity = verticalVelocity; if (MathUtils.IsZero(verticalVelocity)) { return; } verticalVelocity *= DeltaTime; NativeList <ColliderCastHit> verticalCollisions = PhysicsUtils.ColliderCastAll(collider, currPos, currPos + verticalVelocity, ref collisionWorld, entity, Allocator.Temp); PhysicsUtils.TrimByFilter(ref verticalCollisions, ColliderData, PhysicsCollisionFilters.DynamicWithPhysical); if (verticalCollisions.Length > 0) { RigidTransform transform = new RigidTransform() { pos = currPos + verticalVelocity, rot = currRot }; if (PhysicsUtils.ColliderDistance(out DistanceHit verticalPenetration, collider, 1.0f, transform, ref collisionWorld, entity, PhysicsCollisionFilters.DynamicWithPhysical, null, ColliderData, Allocator.Temp)) { if (verticalPenetration.Distance < -0.01f) { verticalVelocity += (verticalPenetration.SurfaceNormal * verticalPenetration.Distance); if (PhysicsUtils.ColliderCast(out ColliderCastHit adjustedHit, collider, currPos, currPos + verticalVelocity, ref collisionWorld, entity, PhysicsCollisionFilters.DynamicWithPhysical, null, ColliderData, Allocator.Temp)) { verticalVelocity *= adjustedHit.Fraction; } } } } verticalVelocity = MathUtils.ZeroOut(verticalVelocity, 0.0001f); verticalCollisions.Dispose(); }
/// <summary> /// Performs a collision correction at the specified position. /// </summary> /// <param name="entity"></param> /// <param name="currPos"></param> /// <param name="currRot"></param> /// <param name="controller"></param> /// <param name="collider"></param> /// <param name="collisionWorld"></param> private void CorrectForCollision(ref Entity entity, ref float3 currPos, ref quaternion currRot, ref CharacterControllerData controller, ref PhysicsCollider collider, ref CollisionWorld collisionWorld) { RigidTransform transform = new RigidTransform() { pos = currPos, rot = currRot }; // Use a subset sphere within our collider to test against. // We do not use the collider itself as some intersection (such as on ramps) is ok. var offset = -math.normalize(controller.gravity) * 0.1f; var sampleCollider = new PhysicsCollider() { Value = SphereCollider.Create(new SphereGeometry() { Center = currPos + offset, Radius = 0.1f }) }; if (PhysicsUtils.ColliderDistance(out DistanceHit smallestHit, sampleCollider, 0.1f, transform, ref collisionWorld, entity, PhysicsCollisionFilters.DynamicWithPhysical, null, ColliderData, Allocator.Temp)) { if (smallestHit.Distance < 0.0f) { currPos += math.abs(smallestHit.Distance) * smallestHit.SurfaceNormal; } } }
/// <summary> /// Determines if the character is resting on a surface. /// </summary> /// <param name="entity"></param> /// <param name="currPos"></param> /// <param name="epsilon"></param> /// <param name="collider"></param> /// <param name="collisionWorld"></param> /// <returns></returns> private unsafe static void DetermineIfGrounded(Entity entity, ref float3 currPos, ref float3 epsilon, ref CharacterControllerData controller, ref PhysicsCollider collider, ref CollisionWorld collisionWorld) { var aabb = collider.ColliderPtr->CalculateAabb(); float mod = 0.15f; float3 samplePos = currPos + new float3(0.0f, aabb.Min.y, 0.0f); float3 gravity = math.normalize(controller.gravity); float3 offset = (gravity * 0.1f); float3 posLeft = samplePos - new float3(aabb.Extents.x * mod, 0.0f, 0.0f); float3 posRight = samplePos + new float3(aabb.Extents.x * mod, 0.0f, 0.0f); float3 posForward = samplePos + new float3(0.0f, 0.0f, aabb.Extents.z * mod); float3 posBackward = samplePos - new float3(0.0f, 0.0f, aabb.Extents.z * mod); controller.isGrounded = PhysicsUtils.Raycast(out RaycastHit centerHit, samplePos, samplePos + offset, ref collisionWorld, entity, PhysicsCollisionFilters.DynamicWithPhysical, Allocator.Temp) || PhysicsUtils.Raycast(out RaycastHit leftHit, posLeft, posLeft + offset, ref collisionWorld, entity, PhysicsCollisionFilters.DynamicWithPhysical, Allocator.Temp) || PhysicsUtils.Raycast(out RaycastHit rightHit, posRight, posRight + offset, ref collisionWorld, entity, PhysicsCollisionFilters.DynamicWithPhysical, Allocator.Temp) || PhysicsUtils.Raycast(out RaycastHit forwardHit, posForward, posForward + offset, ref collisionWorld, entity, PhysicsCollisionFilters.DynamicWithPhysical, Allocator.Temp) || PhysicsUtils.Raycast(out RaycastHit backwardHit, posBackward, posBackward + offset, ref collisionWorld, entity, PhysicsCollisionFilters.DynamicWithPhysical, Allocator.Temp); }