225 lines
11 KiB
C#
225 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.CompilerServices;
|
|
using Unity.Burst;
|
|
using Unity.Collections;
|
|
using Unity.Entities;
|
|
using Unity.CharacterController;
|
|
using Unity.Jobs;
|
|
using Unity.Mathematics;
|
|
using Unity.Physics;
|
|
using Unity.Physics.Authoring;
|
|
using Unity.Physics.Extensions;
|
|
using Unity.Physics.Systems;
|
|
using Unity.Transforms;
|
|
using UnityEngine;
|
|
|
|
public struct ThirdPersonCharacterUpdateContext
|
|
{
|
|
// Here, you may add additional global data for your character updates, such as ComponentLookups, Singletons, NativeCollections, etc...
|
|
// The data you add here will be accessible in your character updates and all of your character "callbacks".
|
|
|
|
public void OnSystemCreate(ref SystemState state)
|
|
{
|
|
// Get lookups
|
|
}
|
|
|
|
public void OnSystemUpdate(ref SystemState state)
|
|
{
|
|
// Update lookups
|
|
}
|
|
}
|
|
|
|
public readonly partial struct ThirdPersonCharacterAspect : IAspect, IKinematicCharacterProcessor<ThirdPersonCharacterUpdateContext>
|
|
{
|
|
public readonly KinematicCharacterAspect CharacterAspect;
|
|
public readonly RefRW<ThirdPersonCharacterComponent> CharacterComponent;
|
|
public readonly RefRW<ThirdPersonCharacterControl> CharacterControl;
|
|
|
|
public void PhysicsUpdate(ref ThirdPersonCharacterUpdateContext context, ref KinematicCharacterUpdateContext baseContext)
|
|
{
|
|
ref ThirdPersonCharacterComponent characterComponent = ref CharacterComponent.ValueRW;
|
|
ref KinematicCharacterBody characterBody = ref CharacterAspect.CharacterBody.ValueRW;
|
|
ref float3 characterPosition = ref CharacterAspect.LocalTransform.ValueRW.Position;
|
|
|
|
// First phase of default character update
|
|
CharacterAspect.Update_Initialize(in this, ref context, ref baseContext, ref characterBody, baseContext.Time.DeltaTime);
|
|
CharacterAspect.Update_ParentMovement(in this, ref context, ref baseContext, ref characterBody, ref characterPosition, characterBody.WasGroundedBeforeCharacterUpdate);
|
|
CharacterAspect.Update_Grounding(in this, ref context, ref baseContext, ref characterBody, ref characterPosition);
|
|
|
|
// Update desired character velocity after grounding was detected, but before doing additional processing that depends on velocity
|
|
HandleVelocityControl(ref context, ref baseContext);
|
|
|
|
// Second phase of default character update
|
|
CharacterAspect.Update_PreventGroundingFromFutureSlopeChange(in this, ref context, ref baseContext, ref characterBody, in characterComponent.StepAndSlopeHandling);
|
|
CharacterAspect.Update_GroundPushing(in this, ref context, ref baseContext, characterComponent.Gravity);
|
|
CharacterAspect.Update_MovementAndDecollisions(in this, ref context, ref baseContext, ref characterBody, ref characterPosition);
|
|
CharacterAspect.Update_MovingPlatformDetection(ref baseContext, ref characterBody);
|
|
CharacterAspect.Update_ParentMomentum(ref baseContext, ref characterBody);
|
|
CharacterAspect.Update_ProcessStatefulCharacterHits();
|
|
}
|
|
|
|
private void HandleVelocityControl(ref ThirdPersonCharacterUpdateContext context, ref KinematicCharacterUpdateContext baseContext)
|
|
{
|
|
float deltaTime = baseContext.Time.DeltaTime;
|
|
ref KinematicCharacterBody characterBody = ref CharacterAspect.CharacterBody.ValueRW;
|
|
ref ThirdPersonCharacterComponent characterComponent = ref CharacterComponent.ValueRW;
|
|
ref ThirdPersonCharacterControl characterControl = ref CharacterControl.ValueRW;
|
|
|
|
// Rotate move input and velocity to take into account parent rotation
|
|
if(characterBody.ParentEntity != Entity.Null)
|
|
{
|
|
characterControl.MoveVector = math.rotate(characterBody.RotationFromParent, characterControl.MoveVector);
|
|
characterBody.RelativeVelocity = math.rotate(characterBody.RotationFromParent, characterBody.RelativeVelocity);
|
|
}
|
|
|
|
if (characterBody.IsGrounded)
|
|
{
|
|
// Move on ground
|
|
float3 targetVelocity = characterControl.MoveVector * characterComponent.GroundMaxSpeed;
|
|
CharacterControlUtilities.StandardGroundMove_Interpolated(ref characterBody.RelativeVelocity, targetVelocity, characterComponent.GroundedMovementSharpness, deltaTime, characterBody.GroundingUp, characterBody.GroundHit.Normal);
|
|
|
|
// Jump
|
|
if (characterControl.Jump)
|
|
{
|
|
CharacterControlUtilities.StandardJump(ref characterBody, characterBody.GroundingUp * characterComponent.JumpSpeed, true, characterBody.GroundingUp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Move in air
|
|
float3 airAcceleration = characterControl.MoveVector * characterComponent.AirAcceleration;
|
|
if (math.lengthsq(airAcceleration) > 0f)
|
|
{
|
|
float3 tmpVelocity = characterBody.RelativeVelocity;
|
|
CharacterControlUtilities.StandardAirMove(ref characterBody.RelativeVelocity, airAcceleration, characterComponent.AirMaxSpeed, characterBody.GroundingUp, deltaTime, false);
|
|
|
|
// Cancel air acceleration from input if we would hit a non-grounded surface (prevents air-climbing slopes at high air accelerations)
|
|
if (characterComponent.PreventAirAccelerationAgainstUngroundedHits && CharacterAspect.MovementWouldHitNonGroundedObstruction(in this, ref context, ref baseContext, characterBody.RelativeVelocity * deltaTime, out ColliderCastHit hit))
|
|
{
|
|
characterBody.RelativeVelocity = tmpVelocity;
|
|
}
|
|
}
|
|
|
|
// Gravity
|
|
CharacterControlUtilities.AccelerateVelocity(ref characterBody.RelativeVelocity, characterComponent.Gravity, deltaTime);
|
|
|
|
// Drag
|
|
CharacterControlUtilities.ApplyDragToVelocity(ref characterBody.RelativeVelocity, deltaTime, characterComponent.AirDrag);
|
|
}
|
|
}
|
|
|
|
public void VariableUpdate(ref ThirdPersonCharacterUpdateContext context, ref KinematicCharacterUpdateContext baseContext)
|
|
{
|
|
ref KinematicCharacterBody characterBody = ref CharacterAspect.CharacterBody.ValueRW;
|
|
ref ThirdPersonCharacterComponent characterComponent = ref CharacterComponent.ValueRW;
|
|
ref ThirdPersonCharacterControl characterControl = ref CharacterControl.ValueRW;
|
|
ref quaternion characterRotation = ref CharacterAspect.LocalTransform.ValueRW.Rotation;
|
|
|
|
// Add rotation from parent body to the character rotation
|
|
// (this is for allowing a rotating moving platform to rotate your character as well, and handle interpolation properly)
|
|
KinematicCharacterUtilities.AddVariableRateRotationFromFixedRateRotation(ref characterRotation, characterBody.RotationFromParent, baseContext.Time.DeltaTime, characterBody.LastPhysicsUpdateDeltaTime);
|
|
|
|
// Rotate towards move direction
|
|
if (math.lengthsq(characterControl.MoveVector) > 0f)
|
|
{
|
|
CharacterControlUtilities.SlerpRotationTowardsDirectionAroundUp(ref characterRotation, baseContext.Time.DeltaTime, math.normalizesafe(characterControl.MoveVector), MathUtilities.GetUpFromRotation(characterRotation), characterComponent.RotationSharpness);
|
|
}
|
|
}
|
|
|
|
#region Character Processor Callbacks
|
|
public void UpdateGroundingUp(
|
|
ref ThirdPersonCharacterUpdateContext context,
|
|
ref KinematicCharacterUpdateContext baseContext)
|
|
{
|
|
ref KinematicCharacterBody characterBody = ref CharacterAspect.CharacterBody.ValueRW;
|
|
|
|
CharacterAspect.Default_UpdateGroundingUp(ref characterBody);
|
|
}
|
|
|
|
public bool CanCollideWithHit(
|
|
ref ThirdPersonCharacterUpdateContext context,
|
|
ref KinematicCharacterUpdateContext baseContext,
|
|
in BasicHit hit)
|
|
{
|
|
return PhysicsUtilities.IsCollidable(hit.Material);
|
|
}
|
|
|
|
public bool IsGroundedOnHit(
|
|
ref ThirdPersonCharacterUpdateContext context,
|
|
ref KinematicCharacterUpdateContext baseContext,
|
|
in BasicHit hit,
|
|
int groundingEvaluationType)
|
|
{
|
|
ThirdPersonCharacterComponent characterComponent = CharacterComponent.ValueRO;
|
|
|
|
return CharacterAspect.Default_IsGroundedOnHit(
|
|
in this,
|
|
ref context,
|
|
ref baseContext,
|
|
in hit,
|
|
in characterComponent.StepAndSlopeHandling,
|
|
groundingEvaluationType);
|
|
}
|
|
|
|
public void OnMovementHit(
|
|
ref ThirdPersonCharacterUpdateContext context,
|
|
ref KinematicCharacterUpdateContext baseContext,
|
|
ref KinematicCharacterHit hit,
|
|
ref float3 remainingMovementDirection,
|
|
ref float remainingMovementLength,
|
|
float3 originalVelocityDirection,
|
|
float hitDistance)
|
|
{
|
|
ref KinematicCharacterBody characterBody = ref CharacterAspect.CharacterBody.ValueRW;
|
|
ref float3 characterPosition = ref CharacterAspect.LocalTransform.ValueRW.Position;
|
|
ThirdPersonCharacterComponent characterComponent = CharacterComponent.ValueRO;
|
|
|
|
CharacterAspect.Default_OnMovementHit(
|
|
in this,
|
|
ref context,
|
|
ref baseContext,
|
|
ref characterBody,
|
|
ref characterPosition,
|
|
ref hit,
|
|
ref remainingMovementDirection,
|
|
ref remainingMovementLength,
|
|
originalVelocityDirection,
|
|
hitDistance,
|
|
characterComponent.StepAndSlopeHandling.StepHandling,
|
|
characterComponent.StepAndSlopeHandling.MaxStepHeight,
|
|
characterComponent.StepAndSlopeHandling.CharacterWidthForStepGroundingCheck);
|
|
}
|
|
|
|
public void OverrideDynamicHitMasses(
|
|
ref ThirdPersonCharacterUpdateContext context,
|
|
ref KinematicCharacterUpdateContext baseContext,
|
|
ref PhysicsMass characterMass,
|
|
ref PhysicsMass otherMass,
|
|
BasicHit hit)
|
|
{
|
|
// Custom mass overrides
|
|
}
|
|
|
|
public void ProjectVelocityOnHits(
|
|
ref ThirdPersonCharacterUpdateContext context,
|
|
ref KinematicCharacterUpdateContext baseContext,
|
|
ref float3 velocity,
|
|
ref bool characterIsGrounded,
|
|
ref BasicHit characterGroundHit,
|
|
in DynamicBuffer<KinematicVelocityProjectionHit> velocityProjectionHits,
|
|
float3 originalVelocityDirection)
|
|
{
|
|
ThirdPersonCharacterComponent characterComponent = CharacterComponent.ValueRO;
|
|
|
|
CharacterAspect.Default_ProjectVelocityOnHits(
|
|
ref velocity,
|
|
ref characterIsGrounded,
|
|
ref characterGroundHit,
|
|
in velocityProjectionHits,
|
|
originalVelocityDirection,
|
|
characterComponent.StepAndSlopeHandling.ConstrainVelocityToGroundPlane);
|
|
}
|
|
#endregion
|
|
}
|