This commit is contained in:
Philippe St-Amand 2023-01-10 13:46:07 -05:00
Родитель 2bf2a4d905
Коммит ace434248f
46 изменённых файлов: 657 добавлений и 218 удалений

Просмотреть файл

@ -201,11 +201,11 @@ MonoBehaviour:
Tag05: 0
Tag06: 0
Tag07: 0
InterpolatePosition: 0
InterpolatePosition: 1
InterpolateRotation: 0
EvaluateGrounding: 1
SnapToGround: 1
GroundSnappingDistance: 0.3
GroundSnappingDistance: 0.5
EnhancedGroundPrecision: 0
MaxGroundedSlopeAngle: 60
DetectMovementCollisions: 1
@ -236,6 +236,7 @@ MonoBehaviour:
StepHandling: 1
MaxStepHeight: 0.5
ExtraStepChecksDistance: 0.1
CharacterWidthForStepGroundingCheck: 1
PreventGroundingWhenMovingTowardsNoGrounding: 1
HasMaxDownwardSlopeChangeAngle: 0
MaxDownwardSlopeChangeAngle: 90

Просмотреть файл

@ -12,6 +12,7 @@ using Unity.Physics.Authoring;
using Unity.Physics.Extensions;
using Unity.Physics.Systems;
using Unity.Transforms;
using UnityEngine;
using Material = Unity.Physics.Material;
using RaycastHit = Unity.Physics.RaycastHit;
@ -333,18 +334,51 @@ namespace Rival
[Serializable]
public struct BasicStepAndSlopeHandlingParameters
{
/// <summary>
/// Whether or not step handling logic is enabled
/// </summary>
[UnityEngine.Header("Step Handling")]
[UnityEngine.Tooltip("Whether or not step handling logic is enabled")]
public bool StepHandling;
/// <summary>
/// Max height that the character can step on
/// </summary>
[UnityEngine.Tooltip("Max height that the character can step on")]
public float MaxStepHeight;
/// <summary>
/// Horizontal offset distance of extra downwards raycasts used to detect grounding around a step
/// </summary>
[UnityEngine.Tooltip("Horizontal offset distance of extra downwards raycasts used to detect grounding around a step")]
public float ExtraStepChecksDistance;
/// <summary>
/// Character width used to determine grounding for steps. For a capsule this should be 2x capsule radius, and for a box it should be maximum box width. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height.
/// </summary>
[UnityEngine.Tooltip("Character width used to determine grounding for steps. For a capsule this should be 2x capsule radius, and for a box it should be maximum box width. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height.")]
public float CharacterWidthForStepGroundingCheck;
/// <summary>
/// Whether or not to cancel grounding when the character is moving off a ledge. This prevents the character from "snapping" onto the ledge as it moves off of it
/// </summary>
[UnityEngine.Header("Slope Changes")]
[UnityEngine.Tooltip("Whether or not to cancel grounding when the character is moving off a ledge. This prevents the character from \"snapping\" onto the ledge as it moves off of it")]
public bool PreventGroundingWhenMovingTowardsNoGrounding;
/// <summary>
/// Whether or not the character has a max slope change that it can stay grounded on
/// </summary>
[UnityEngine.Tooltip("Whether or not the character has a max slope change that it can stay grounded on")]
public bool HasMaxDownwardSlopeChangeAngle;
/// <summary>
/// Max slope change that the character can stay grounded on
/// </summary>
[UnityEngine.Tooltip("Max slope change that the character can stay grounded on")]
[UnityEngine.Range(0f, 180f)]
public float MaxDownwardSlopeChangeAngle;
/// <summary>
/// Whether or not to constrain the character velocity to ground plane when it hits a non-grounded slope
/// </summary>
[UnityEngine.Header("Misc")]
[UnityEngine.Tooltip("Whether or not to constrain the character velocity to ground plane when it hits a non-grounded slope")]
public bool ConstrainVelocityToGroundPlane;
public static BasicStepAndSlopeHandlingParameters GetDefault()
@ -354,6 +388,7 @@ namespace Rival
StepHandling = false,
MaxStepHeight = 0.5f,
ExtraStepChecksDistance = 0.1f,
CharacterWidthForStepGroundingCheck = 1f,
PreventGroundingWhenMovingTowardsNoGrounding = true,
HasMaxDownwardSlopeChangeAngle = false,
@ -1448,7 +1483,6 @@ namespace Rival
characterBody.IsGrounded,
characterBody.RelativeVelocity,
isGroundedOnMovementHit);
VelocityProjectionHits.Add(new KinematicVelocityProjectionHit(currentCharacterHit));
processor.OnMovementHit(
ref context,
@ -1918,6 +1952,7 @@ namespace Rival
float maxStepHeight,
float extraStepChecksDistance) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
{
KinematicCharacterBody characterBody = CharacterBody.ValueRO;
KinematicCharacterProperties characterProperties = CharacterProperties.ValueRO;
@ -2045,7 +2080,7 @@ namespace Rival
return false;
}
/// <summary>
/// Handles the stepping-up-a-step logic during character movement iterations
/// </summary>
@ -2060,6 +2095,7 @@ namespace Rival
/// <param name="hitDistance"></param>
/// <param name="stepHandling"></param>
/// <param name="maxStepHeight"></param>
/// <param name="characterWidthForStepGroundingCheck"> Character width used to determine grounding for steps. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height. </param>
/// <param name="hasSteppedUp"></param>
/// <typeparam name="T"> The type of the struct implementing <see cref="IKinematicCharacterProcessor{C}"/> </typeparam>
/// <typeparam name="C"> The type of the user-created context struct </typeparam>
@ -2075,6 +2111,7 @@ namespace Rival
float hitDistance,
bool stepHandling,
float maxStepHeight,
float characterWidthForStepGroundingCheck,
out bool hasSteppedUp) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
{
hasSteppedUp = false;
@ -2086,7 +2123,8 @@ namespace Rival
if (characterProperties.EvaluateGrounding &&
stepHandling &&
!hit.IsGroundedOnHit &&
maxStepHeight > 0f)
maxStepHeight > 0f &&
!PhysicsUtilities.IsBodyDynamic(in baseContext.PhysicsWorld, hit.RigidBodyIndex))
{
float3 startPositionOfUpCheck = characterPosition;
float3 upCheckDirection = characterBody.GroundingUp;
@ -2119,7 +2157,7 @@ namespace Rival
{
float3 startPositionOfForwardCheck = startPositionOfUpCheck + (upCheckDirection * upStepHitDistance);
float distanceOverStep = math.length(math.projectsafe(remainingMovementDirection * (remainingMovementLength - hitDistance), hit.Normal));
float3 endPositionOfForwardCheck = startPositionOfForwardCheck + (remainingMovementDirection * remainingMovementLength);
float3 endPositionOfForwardCheck = startPositionOfForwardCheck + (remainingMovementDirection * (remainingMovementLength + Constants.CollisionOffset));
float minimumDistanceOverStep = Constants.CollisionOffset * 3f;
if (distanceOverStep < minimumDistanceOverStep)
{
@ -2185,22 +2223,53 @@ namespace Rival
}
if (isGroundedOnStepHit)
{
float steppedHeight = upStepHitDistance - downStepHitDistance;
if (steppedHeight > Constants.CollisionOffset)
{
float hitHeight = upStepHitDistance - downStepHitDistance;
float steppedHeight = hitHeight;
steppedHeight = math.max(0f, steppedHeight + Constants.CollisionOffset);
// Add slope & character width consideration to stepped height
if(characterWidthForStepGroundingCheck > 0f)
{
// Find the effective slope normal
float3 forwardSlopeCheckDirection = -math.normalizesafe(math.cross(math.cross(characterBody.GroundingUp, stepHit.Normal), stepHit.Normal));
if (RaycastClosestCollisions(
in processor,
ref context,
ref baseContext,
stepHit.Position + (characterBody.GroundingUp * Constants.CollisionOffset) + (forwardSlopeCheckDirection * Constants.CollisionOffset),
-characterBody.GroundingUp,
maxStepHeight,
characterProperties.ShouldIgnoreDynamicBodies(),
out RaycastHit forwardSlopeCheckHit,
out float forwardSlopeCheckHitDistance))
{
float3 effectiveSlopeNormal = forwardSlopeCheckHit.SurfaceNormal;
float slopeRadians = MathUtilities.AngleRadians(characterBody.GroundingUp, effectiveSlopeNormal);
float extraHeightFromAngleAndCharacterWidth = math.tan(slopeRadians) * characterWidthForStepGroundingCheck * 0.5f;
steppedHeight += extraHeightFromAngleAndCharacterWidth;
}
}
if (steppedHeight < maxStepHeight)
{
// Step up
characterPosition += characterBody.GroundingUp * steppedHeight;
characterPosition += characterBody.GroundingUp * hitHeight;
characterPosition += forwardCheckDirection * forwardStepHitDistance;
characterBody.IsGrounded = true;
characterBody.GroundHit = stepHit;
// Project vel
float3 characterVelocityBeforeHit = characterBody.RelativeVelocity;
characterBody.RelativeVelocity = MathUtilities.ProjectOnPlane(characterBody.RelativeVelocity, characterBody.GroundingUp);
remainingMovementDirection = math.normalizesafe(characterBody.RelativeVelocity);
remainingMovementLength -= forwardStepHitDistance;
// Replace hit with step hit
hit = KinematicCharacterUtilities.CreateCharacterHit(stepHit, characterBody.IsGrounded, characterVelocityBeforeHit, isGroundedOnStepHit);
hit.CharacterVelocityAfterHit = characterBody.RelativeVelocity;
hasSteppedUp = true;
}
@ -2542,19 +2611,23 @@ namespace Rival
if (!isGroundedOnSlope && stepAndSlopeHandling.StepHandling && stepAndSlopeHandling.MaxStepHeight > 0f)
{
bool hitIsOnCharacterBottom = math.dot(characterBody.GroundingUp, hit.Normal) > Constants.DotProductSimilarityEpsilon;
if (hitIsOnCharacterBottom ||
(groundingEvaluationType != (int)GroundingEvaluationType.MovementHit && groundingEvaluationType != (int)GroundingEvaluationType.InitialOverlaps))
if (hitIsOnCharacterBottom &&
(groundingEvaluationType == (int)GroundingEvaluationType.GroundProbing || groundingEvaluationType == (int)GroundingEvaluationType.StepUpHit))
{
isGroundedOnSteps = IsGroundedOnSteps(
in processor,
ref context,
ref baseContext,
in hit,
stepAndSlopeHandling.MaxStepHeight,
stepAndSlopeHandling.ExtraStepChecksDistance);
// Prevent step grounding detection on dynamic bodies, to prevent cases of character stepping onto sphere rolling towards it
if (!PhysicsUtilities.IsBodyDynamic(in baseContext.PhysicsWorld, hit.RigidBodyIndex))
{
isGroundedOnSteps = IsGroundedOnSteps(
in processor,
ref context,
ref baseContext,
in hit,
stepAndSlopeHandling.MaxStepHeight,
stepAndSlopeHandling.ExtraStepChecksDistance);
}
}
}
return isGroundedOnSlope || isGroundedOnSteps;
}
@ -2666,7 +2739,7 @@ namespace Rival
ProjectVelocityOnSingleHit(ref velocity, ref characterIsGrounded, ref characterGroundHit, in firstHit, characterBody.GroundingUp);
velocityDirection = math.normalizesafe(velocity);
// Original velocity direction will act as a plane constaint just like other hits, to prevent our velocity from going back the way it came from. Hit index -1 represents original velocity
// Original velocity direction will act as a plane constraint just like other hits, to prevent our velocity from going back the way it came from. Hit index -1 represents original velocity
KinematicVelocityProjectionHit originalVelocityHit = default;
originalVelocityHit.Normal = characterIsGrounded ? math.normalizesafe(MathUtilities.ProjectOnPlane(originalVelocityDirection, characterBody.GroundingUp)) : originalVelocityDirection;
@ -2760,6 +2833,7 @@ namespace Rival
/// <param name="movementHitDistance"> Distance of the hit </param>
/// <param name="stepHandling"> Whether step-handling is enabled or not </param>
/// <param name="maxStepHeight"> Maximum height of steps that can be stepped on </param>
/// <param name="characterWidthForStepGroundingCheck"> Character width used to determine grounding for steps. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height. </param>
/// <typeparam name="T"> The type of the struct implementing <see cref="IKinematicCharacterProcessor{C}"/> </typeparam>
/// <typeparam name="C"> The type of the user-created context struct </typeparam>
public void Default_OnMovementHit<T, C>(
@ -2774,7 +2848,8 @@ namespace Rival
float3 originalVelocityDirection,
float movementHitDistance,
bool stepHandling,
float maxStepHeight) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
float maxStepHeight,
float characterWidthForStepGroundingCheck = 0f) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
{
bool hasSteppedUp = false;
@ -2794,8 +2869,12 @@ namespace Rival
movementHitDistance,
stepHandling,
maxStepHeight,
characterWidthForStepGroundingCheck,
out hasSteppedUp);
}
// Add velocityProjection hits only after potential correction from step handling
VelocityProjectionHits.Add(new KinematicVelocityProjectionHit(hit));
if (!hasSteppedUp)
{
@ -2814,7 +2893,7 @@ namespace Rival
ref characterBody.GroundHit,
in VelocityProjectionHits,
originalVelocityDirection);
// Recalculate remaining movement after projection
float projectedVelocityLengthFactor = math.length(characterBody.RelativeVelocity) / math.length(velocityBeforeProjection);
remainingMovementLength *= projectedVelocityLengthFactor;

Просмотреть файл

@ -131,7 +131,7 @@ namespace Rival
// Grounding
EvaluateGrounding = true,
SnapToGround = true,
GroundSnappingDistance = 0.3f,
GroundSnappingDistance = 0.5f,
EnhancedGroundPrecision = false,
MaxGroundedSlopeAngle = 60f,

Просмотреть файл

@ -270,22 +270,25 @@ namespace Rival
float3 relativeVelocityAToB = pointVelocityB - pointVelocityA;
float relativeVelocityOnNormal = math.dot(relativeVelocityAToB, collisionNormalBToA);
float3 crossA = math.cross(centerOfMassAToPoint, collisionNormalBToA);
float3 crossB = math.cross(collisionNormalBToA, centerOfMassBToPoint);
float3 angularA = math.mul(new Math.MTransform(transformA).InverseRotation, crossA).xyz;
float3 angularB = math.mul(new Math.MTransform(transformB).InverseRotation, crossB).xyz;
float3 temp = angularA * angularA * physicsMassA.InverseInertia + angularB * angularB * physicsMassB.InverseInertia;
float invEffectiveMass = temp.x + temp.y + temp.z + (physicsMassA.InverseMass + physicsMassB.InverseMass);
if (invEffectiveMass > 0f)
if (relativeVelocityOnNormal > 0f)
{
float effectiveMass = 1f / invEffectiveMass;
float3 crossA = math.cross(centerOfMassAToPoint, collisionNormalBToA);
float3 crossB = math.cross(collisionNormalBToA, centerOfMassBToPoint);
float3 angularA = math.mul(new Math.MTransform(transformA).InverseRotation, crossA).xyz;
float3 angularB = math.mul(new Math.MTransform(transformB).InverseRotation, crossB).xyz;
float3 temp = angularA * angularA * physicsMassA.InverseInertia + angularB * angularB * physicsMassB.InverseInertia;
float invEffectiveMass = temp.x + temp.y + temp.z + (physicsMassA.InverseMass + physicsMassB.InverseMass);
float impulseScale = -relativeVelocityOnNormal * effectiveMass;
float3 totalImpulse = collisionNormalBToA * impulseScale;
if (invEffectiveMass > 0f)
{
float effectiveMass = 1f / invEffectiveMass;
impulseOnA = -totalImpulse;
impulseOnB = totalImpulse;
float impulseScale = -relativeVelocityOnNormal * effectiveMass;
float3 totalImpulse = collisionNormalBToA * impulseScale;
impulseOnA = -totalImpulse;
impulseOnB = totalImpulse;
}
}
}

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: e157296f77a4d34488f1881ea5841589
guid: 2ce285599b88d544892bbcced8e8f6ff
folderAsset: yes
DefaultImporter:
externalObjects: {}

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 0bf34e36e06c4b3f4c6b92a995b2e8e8
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -221,7 +221,8 @@ public readonly partial struct BasicCharacterAspect : IAspect, IKinematicCharact
originalVelocityDirection,
hitDistance,
characterComponent.StepAndSlopeHandling.StepHandling,
characterComponent.StepAndSlopeHandling.MaxStepHeight);
characterComponent.StepAndSlopeHandling.MaxStepHeight,
characterComponent.StepAndSlopeHandling.CharacterWidthForStepGroundingCheck);
}
public void OverrideDynamicHitMasses(

Просмотреть файл

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 3b39867e89a9a6545ba64408193c1916
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -3,7 +3,7 @@
--- !u!129 &1
PlayerSettings:
m_ObjectHideFlags: 0
serializedVersion: 24
serializedVersion: 25
productGUID: 23030eb1e4488734a94a1e4570b334b0
AndroidProfiler: 0
AndroidFilterTouchesWhenObscured: 0
@ -135,7 +135,6 @@ PlayerSettings:
bundleVersion: 0.1
preloadedAssets:
- {fileID: 0}
- {fileID: 11400000, guid: 7f47cca94c9d75843932abe26966e773, type: 2}
metroInputSource: 0
wsaTransparentSwapchain: 0
m_HolographicPauseOnTrackingLoss: 1
@ -288,6 +287,7 @@ PlayerSettings:
- m_BuildTarget: WebGL
m_StaticBatching: 0
m_DynamicBatching: 0
m_BuildTargetShaderSettings: []
m_BuildTargetGraphicsJobs:
- m_BuildTarget: MacStandaloneSupport
m_GraphicsJobs: 0
@ -337,6 +337,8 @@ PlayerSettings:
m_APIs: 0200000015000000
m_Automatic: 0
m_BuildTargetVRSettings: []
m_DefaultShaderChunkSizeInMB: 16
m_DefaultShaderChunkCount: 0
openGLRequireES31: 0
openGLRequireES31AEP: 0
openGLRequireES32: 0
@ -634,6 +636,7 @@ PlayerSettings:
webGLMemoryLinearGrowthStep: 16
webGLMemoryGeometricGrowthStep: 0.2
webGLMemoryGeometricGrowthCap: 96
webGLPowerPreference: 2
scriptingDefineSymbols:
Standalone: DOTS_ADD_PARTIAL_KEYWORD
additionalCompilerArguments: {}
@ -649,7 +652,7 @@ PlayerSettings:
suppressCommonWarnings: 1
allowUnsafeCode: 1
useDeterministicCompilation: 1
enableRoslynAnalyzers: 1
selectedPlatform: 0
additionalIl2CppArgs:
scriptingRuntimeVersion: 1
gcIncremental: 1
@ -726,8 +729,13 @@ PlayerSettings:
luminVersion:
m_VersionCode: 1
m_VersionName:
hmiPlayerDataPath:
hmiForceSRGBBlit: 1
embeddedLinuxEnableGamepadInput: 1
hmiCpuConfiguration:
apiCompatibilityLevel: 6
activeInputHandler: 2
windowsGamepadBackendHint: 0
cloudProjectId:
framebufferDepthMemorylessMode: 0
qualitySettingsNames: []
@ -735,10 +743,6 @@ PlayerSettings:
organizationId:
cloudEnabled: 0
legacyClampBlendShapeWeights: 0
playerDataPath:
forceSRGBBlit: 1
enableGamepadInput: 1
cpuConfiguration:
hmiLoadingImage: {fileID: 0}
virtualTexturingSupportEnabled: 0
insecureHttpOption: 0

Просмотреть файл

@ -1,2 +1,2 @@
m_EditorVersion: 2022.2.0f1
m_EditorVersionWithRevision: 2022.2.0f1 (35dcd44975df)
m_EditorVersion: 2022.2.1f1
m_EditorVersionWithRevision: 2022.2.1f1 (4fead5835099)

Просмотреть файл

@ -12,6 +12,7 @@ using Unity.Physics.Authoring;
using Unity.Physics.Extensions;
using Unity.Physics.Systems;
using Unity.Transforms;
using UnityEngine;
using Material = Unity.Physics.Material;
using RaycastHit = Unity.Physics.RaycastHit;
@ -333,18 +334,51 @@ namespace Rival
[Serializable]
public struct BasicStepAndSlopeHandlingParameters
{
/// <summary>
/// Whether or not step handling logic is enabled
/// </summary>
[UnityEngine.Header("Step Handling")]
[UnityEngine.Tooltip("Whether or not step handling logic is enabled")]
public bool StepHandling;
/// <summary>
/// Max height that the character can step on
/// </summary>
[UnityEngine.Tooltip("Max height that the character can step on")]
public float MaxStepHeight;
/// <summary>
/// Horizontal offset distance of extra downwards raycasts used to detect grounding around a step
/// </summary>
[UnityEngine.Tooltip("Horizontal offset distance of extra downwards raycasts used to detect grounding around a step")]
public float ExtraStepChecksDistance;
/// <summary>
/// Character width used to determine grounding for steps. For a capsule this should be 2x capsule radius, and for a box it should be maximum box width. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height.
/// </summary>
[UnityEngine.Tooltip("Character width used to determine grounding for steps. For a capsule this should be 2x capsule radius, and for a box it should be maximum box width. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height.")]
public float CharacterWidthForStepGroundingCheck;
/// <summary>
/// Whether or not to cancel grounding when the character is moving off a ledge. This prevents the character from "snapping" onto the ledge as it moves off of it
/// </summary>
[UnityEngine.Header("Slope Changes")]
[UnityEngine.Tooltip("Whether or not to cancel grounding when the character is moving off a ledge. This prevents the character from \"snapping\" onto the ledge as it moves off of it")]
public bool PreventGroundingWhenMovingTowardsNoGrounding;
/// <summary>
/// Whether or not the character has a max slope change that it can stay grounded on
/// </summary>
[UnityEngine.Tooltip("Whether or not the character has a max slope change that it can stay grounded on")]
public bool HasMaxDownwardSlopeChangeAngle;
/// <summary>
/// Max slope change that the character can stay grounded on
/// </summary>
[UnityEngine.Tooltip("Max slope change that the character can stay grounded on")]
[UnityEngine.Range(0f, 180f)]
public float MaxDownwardSlopeChangeAngle;
/// <summary>
/// Whether or not to constrain the character velocity to ground plane when it hits a non-grounded slope
/// </summary>
[UnityEngine.Header("Misc")]
[UnityEngine.Tooltip("Whether or not to constrain the character velocity to ground plane when it hits a non-grounded slope")]
public bool ConstrainVelocityToGroundPlane;
public static BasicStepAndSlopeHandlingParameters GetDefault()
@ -354,6 +388,7 @@ namespace Rival
StepHandling = false,
MaxStepHeight = 0.5f,
ExtraStepChecksDistance = 0.1f,
CharacterWidthForStepGroundingCheck = 1f,
PreventGroundingWhenMovingTowardsNoGrounding = true,
HasMaxDownwardSlopeChangeAngle = false,
@ -1448,7 +1483,6 @@ namespace Rival
characterBody.IsGrounded,
characterBody.RelativeVelocity,
isGroundedOnMovementHit);
VelocityProjectionHits.Add(new KinematicVelocityProjectionHit(currentCharacterHit));
processor.OnMovementHit(
ref context,
@ -1918,6 +1952,7 @@ namespace Rival
float maxStepHeight,
float extraStepChecksDistance) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
{
KinematicCharacterBody characterBody = CharacterBody.ValueRO;
KinematicCharacterProperties characterProperties = CharacterProperties.ValueRO;
@ -2045,7 +2080,7 @@ namespace Rival
return false;
}
/// <summary>
/// Handles the stepping-up-a-step logic during character movement iterations
/// </summary>
@ -2060,6 +2095,7 @@ namespace Rival
/// <param name="hitDistance"></param>
/// <param name="stepHandling"></param>
/// <param name="maxStepHeight"></param>
/// <param name="characterWidthForStepGroundingCheck"> Character width used to determine grounding for steps. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height. </param>
/// <param name="hasSteppedUp"></param>
/// <typeparam name="T"> The type of the struct implementing <see cref="IKinematicCharacterProcessor{C}"/> </typeparam>
/// <typeparam name="C"> The type of the user-created context struct </typeparam>
@ -2075,6 +2111,7 @@ namespace Rival
float hitDistance,
bool stepHandling,
float maxStepHeight,
float characterWidthForStepGroundingCheck,
out bool hasSteppedUp) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
{
hasSteppedUp = false;
@ -2086,7 +2123,8 @@ namespace Rival
if (characterProperties.EvaluateGrounding &&
stepHandling &&
!hit.IsGroundedOnHit &&
maxStepHeight > 0f)
maxStepHeight > 0f &&
!PhysicsUtilities.IsBodyDynamic(in baseContext.PhysicsWorld, hit.RigidBodyIndex))
{
float3 startPositionOfUpCheck = characterPosition;
float3 upCheckDirection = characterBody.GroundingUp;
@ -2119,7 +2157,7 @@ namespace Rival
{
float3 startPositionOfForwardCheck = startPositionOfUpCheck + (upCheckDirection * upStepHitDistance);
float distanceOverStep = math.length(math.projectsafe(remainingMovementDirection * (remainingMovementLength - hitDistance), hit.Normal));
float3 endPositionOfForwardCheck = startPositionOfForwardCheck + (remainingMovementDirection * remainingMovementLength);
float3 endPositionOfForwardCheck = startPositionOfForwardCheck + (remainingMovementDirection * (remainingMovementLength + Constants.CollisionOffset));
float minimumDistanceOverStep = Constants.CollisionOffset * 3f;
if (distanceOverStep < minimumDistanceOverStep)
{
@ -2185,22 +2223,53 @@ namespace Rival
}
if (isGroundedOnStepHit)
{
float steppedHeight = upStepHitDistance - downStepHitDistance;
if (steppedHeight > Constants.CollisionOffset)
{
float hitHeight = upStepHitDistance - downStepHitDistance;
float steppedHeight = hitHeight;
steppedHeight = math.max(0f, steppedHeight + Constants.CollisionOffset);
// Add slope & character width consideration to stepped height
if(characterWidthForStepGroundingCheck > 0f)
{
// Find the effective slope normal
float3 forwardSlopeCheckDirection = -math.normalizesafe(math.cross(math.cross(characterBody.GroundingUp, stepHit.Normal), stepHit.Normal));
if (RaycastClosestCollisions(
in processor,
ref context,
ref baseContext,
stepHit.Position + (characterBody.GroundingUp * Constants.CollisionOffset) + (forwardSlopeCheckDirection * Constants.CollisionOffset),
-characterBody.GroundingUp,
maxStepHeight,
characterProperties.ShouldIgnoreDynamicBodies(),
out RaycastHit forwardSlopeCheckHit,
out float forwardSlopeCheckHitDistance))
{
float3 effectiveSlopeNormal = forwardSlopeCheckHit.SurfaceNormal;
float slopeRadians = MathUtilities.AngleRadians(characterBody.GroundingUp, effectiveSlopeNormal);
float extraHeightFromAngleAndCharacterWidth = math.tan(slopeRadians) * characterWidthForStepGroundingCheck * 0.5f;
steppedHeight += extraHeightFromAngleAndCharacterWidth;
}
}
if (steppedHeight < maxStepHeight)
{
// Step up
characterPosition += characterBody.GroundingUp * steppedHeight;
characterPosition += characterBody.GroundingUp * hitHeight;
characterPosition += forwardCheckDirection * forwardStepHitDistance;
characterBody.IsGrounded = true;
characterBody.GroundHit = stepHit;
// Project vel
float3 characterVelocityBeforeHit = characterBody.RelativeVelocity;
characterBody.RelativeVelocity = MathUtilities.ProjectOnPlane(characterBody.RelativeVelocity, characterBody.GroundingUp);
remainingMovementDirection = math.normalizesafe(characterBody.RelativeVelocity);
remainingMovementLength -= forwardStepHitDistance;
// Replace hit with step hit
hit = KinematicCharacterUtilities.CreateCharacterHit(stepHit, characterBody.IsGrounded, characterVelocityBeforeHit, isGroundedOnStepHit);
hit.CharacterVelocityAfterHit = characterBody.RelativeVelocity;
hasSteppedUp = true;
}
@ -2542,19 +2611,23 @@ namespace Rival
if (!isGroundedOnSlope && stepAndSlopeHandling.StepHandling && stepAndSlopeHandling.MaxStepHeight > 0f)
{
bool hitIsOnCharacterBottom = math.dot(characterBody.GroundingUp, hit.Normal) > Constants.DotProductSimilarityEpsilon;
if (hitIsOnCharacterBottom ||
(groundingEvaluationType != (int)GroundingEvaluationType.MovementHit && groundingEvaluationType != (int)GroundingEvaluationType.InitialOverlaps))
if (hitIsOnCharacterBottom &&
(groundingEvaluationType == (int)GroundingEvaluationType.GroundProbing || groundingEvaluationType == (int)GroundingEvaluationType.StepUpHit))
{
isGroundedOnSteps = IsGroundedOnSteps(
in processor,
ref context,
ref baseContext,
in hit,
stepAndSlopeHandling.MaxStepHeight,
stepAndSlopeHandling.ExtraStepChecksDistance);
// Prevent step grounding detection on dynamic bodies, to prevent cases of character stepping onto sphere rolling towards it
if (!PhysicsUtilities.IsBodyDynamic(in baseContext.PhysicsWorld, hit.RigidBodyIndex))
{
isGroundedOnSteps = IsGroundedOnSteps(
in processor,
ref context,
ref baseContext,
in hit,
stepAndSlopeHandling.MaxStepHeight,
stepAndSlopeHandling.ExtraStepChecksDistance);
}
}
}
return isGroundedOnSlope || isGroundedOnSteps;
}
@ -2666,7 +2739,7 @@ namespace Rival
ProjectVelocityOnSingleHit(ref velocity, ref characterIsGrounded, ref characterGroundHit, in firstHit, characterBody.GroundingUp);
velocityDirection = math.normalizesafe(velocity);
// Original velocity direction will act as a plane constaint just like other hits, to prevent our velocity from going back the way it came from. Hit index -1 represents original velocity
// Original velocity direction will act as a plane constraint just like other hits, to prevent our velocity from going back the way it came from. Hit index -1 represents original velocity
KinematicVelocityProjectionHit originalVelocityHit = default;
originalVelocityHit.Normal = characterIsGrounded ? math.normalizesafe(MathUtilities.ProjectOnPlane(originalVelocityDirection, characterBody.GroundingUp)) : originalVelocityDirection;
@ -2760,6 +2833,7 @@ namespace Rival
/// <param name="movementHitDistance"> Distance of the hit </param>
/// <param name="stepHandling"> Whether step-handling is enabled or not </param>
/// <param name="maxStepHeight"> Maximum height of steps that can be stepped on </param>
/// <param name="characterWidthForStepGroundingCheck"> Character width used to determine grounding for steps. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height. </param>
/// <typeparam name="T"> The type of the struct implementing <see cref="IKinematicCharacterProcessor{C}"/> </typeparam>
/// <typeparam name="C"> The type of the user-created context struct </typeparam>
public void Default_OnMovementHit<T, C>(
@ -2774,7 +2848,8 @@ namespace Rival
float3 originalVelocityDirection,
float movementHitDistance,
bool stepHandling,
float maxStepHeight) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
float maxStepHeight,
float characterWidthForStepGroundingCheck = 0f) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
{
bool hasSteppedUp = false;
@ -2794,8 +2869,12 @@ namespace Rival
movementHitDistance,
stepHandling,
maxStepHeight,
characterWidthForStepGroundingCheck,
out hasSteppedUp);
}
// Add velocityProjection hits only after potential correction from step handling
VelocityProjectionHits.Add(new KinematicVelocityProjectionHit(hit));
if (!hasSteppedUp)
{
@ -2814,7 +2893,7 @@ namespace Rival
ref characterBody.GroundHit,
in VelocityProjectionHits,
originalVelocityDirection);
// Recalculate remaining movement after projection
float projectedVelocityLengthFactor = math.length(characterBody.RelativeVelocity) / math.length(velocityBeforeProjection);
remainingMovementLength *= projectedVelocityLengthFactor;

Просмотреть файл

@ -131,7 +131,7 @@ namespace Rival
// Grounding
EvaluateGrounding = true,
SnapToGround = true,
GroundSnappingDistance = 0.3f,
GroundSnappingDistance = 0.5f,
EnhancedGroundPrecision = false,
MaxGroundedSlopeAngle = 60f,

Просмотреть файл

@ -270,22 +270,25 @@ namespace Rival
float3 relativeVelocityAToB = pointVelocityB - pointVelocityA;
float relativeVelocityOnNormal = math.dot(relativeVelocityAToB, collisionNormalBToA);
float3 crossA = math.cross(centerOfMassAToPoint, collisionNormalBToA);
float3 crossB = math.cross(collisionNormalBToA, centerOfMassBToPoint);
float3 angularA = math.mul(new Math.MTransform(transformA).InverseRotation, crossA).xyz;
float3 angularB = math.mul(new Math.MTransform(transformB).InverseRotation, crossB).xyz;
float3 temp = angularA * angularA * physicsMassA.InverseInertia + angularB * angularB * physicsMassB.InverseInertia;
float invEffectiveMass = temp.x + temp.y + temp.z + (physicsMassA.InverseMass + physicsMassB.InverseMass);
if (invEffectiveMass > 0f)
if (relativeVelocityOnNormal > 0f)
{
float effectiveMass = 1f / invEffectiveMass;
float3 crossA = math.cross(centerOfMassAToPoint, collisionNormalBToA);
float3 crossB = math.cross(collisionNormalBToA, centerOfMassBToPoint);
float3 angularA = math.mul(new Math.MTransform(transformA).InverseRotation, crossA).xyz;
float3 angularB = math.mul(new Math.MTransform(transformB).InverseRotation, crossB).xyz;
float3 temp = angularA * angularA * physicsMassA.InverseInertia + angularB * angularB * physicsMassB.InverseInertia;
float invEffectiveMass = temp.x + temp.y + temp.z + (physicsMassA.InverseMass + physicsMassB.InverseMass);
float impulseScale = -relativeVelocityOnNormal * effectiveMass;
float3 totalImpulse = collisionNormalBToA * impulseScale;
if (invEffectiveMass > 0f)
{
float effectiveMass = 1f / invEffectiveMass;
impulseOnA = -totalImpulse;
impulseOnB = totalImpulse;
float impulseScale = -relativeVelocityOnNormal * effectiveMass;
float3 totalImpulse = collisionNormalBToA * impulseScale;
impulseOnA = -totalImpulse;
impulseOnB = totalImpulse;
}
}
}

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -222,7 +222,8 @@ public readonly partial struct FirstPersonCharacterAspect : IAspect, IKinematicC
originalVelocityDirection,
hitDistance,
characterComponent.StepAndSlopeHandling.StepHandling,
characterComponent.StepAndSlopeHandling.MaxStepHeight);
characterComponent.StepAndSlopeHandling.MaxStepHeight,
characterComponent.StepAndSlopeHandling.CharacterWidthForStepGroundingCheck);
}
public void OverrideDynamicHitMasses(

Просмотреть файл

@ -192,7 +192,7 @@ public partial struct ServerGameSystem : ISystem
// Accept join request
Entity joinRequestAcceptedEntity = state.EntityManager.CreateEntity();
ecb.AddComponent(joinRequestAcceptedEntity, new JoinRequestAccepted());
ecb.AddComponent(joinRequestAcceptedEntity, new SendRpcCommandRequestComponent());
ecb.AddComponent(joinRequestAcceptedEntity, new SendRpcCommandRequestComponent{ TargetConnection = rpcReceive.SourceConnection });
// Stream in game
ecb.AddComponent(rpcReceive.SourceConnection, new NetworkStreamInGame());

Просмотреть файл

@ -15,15 +15,20 @@ public partial struct PredictedFixedStepTransformsUpdateSystem : ISystem
private SystemHandle _transformsGroupHandle;
public void OnCreate(ref SystemState state)
{
_transformsGroupHandle = state.World.GetExistingSystem<TransformSystemGroup>();
}
{ }
public void OnDestroy(ref SystemState state)
{ }
public void OnUpdate(ref SystemState state)
{
_transformsGroupHandle.Update(state.WorldUnmanaged);
if (_transformsGroupHandle == SystemHandle.Null)
{
_transformsGroupHandle = state.World.GetExistingSystem<TransformSystemGroup>();
}
else
{
_transformsGroupHandle.Update(state.WorldUnmanaged);
}
}
}

Просмотреть файл

@ -1,2 +1,2 @@
m_EditorVersion: 2022.2.0f1
m_EditorVersionWithRevision: 2022.2.0f1 (35dcd44975df)
m_EditorVersion: 2022.2.1f1
m_EditorVersionWithRevision: 2022.2.1f1 (4fead5835099)

Просмотреть файл

@ -12,6 +12,7 @@ using Unity.Physics.Authoring;
using Unity.Physics.Extensions;
using Unity.Physics.Systems;
using Unity.Transforms;
using UnityEngine;
using Material = Unity.Physics.Material;
using RaycastHit = Unity.Physics.RaycastHit;
@ -333,18 +334,51 @@ namespace Rival
[Serializable]
public struct BasicStepAndSlopeHandlingParameters
{
/// <summary>
/// Whether or not step handling logic is enabled
/// </summary>
[UnityEngine.Header("Step Handling")]
[UnityEngine.Tooltip("Whether or not step handling logic is enabled")]
public bool StepHandling;
/// <summary>
/// Max height that the character can step on
/// </summary>
[UnityEngine.Tooltip("Max height that the character can step on")]
public float MaxStepHeight;
/// <summary>
/// Horizontal offset distance of extra downwards raycasts used to detect grounding around a step
/// </summary>
[UnityEngine.Tooltip("Horizontal offset distance of extra downwards raycasts used to detect grounding around a step")]
public float ExtraStepChecksDistance;
/// <summary>
/// Character width used to determine grounding for steps. For a capsule this should be 2x capsule radius, and for a box it should be maximum box width. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height.
/// </summary>
[UnityEngine.Tooltip("Character width used to determine grounding for steps. For a capsule this should be 2x capsule radius, and for a box it should be maximum box width. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height.")]
public float CharacterWidthForStepGroundingCheck;
/// <summary>
/// Whether or not to cancel grounding when the character is moving off a ledge. This prevents the character from "snapping" onto the ledge as it moves off of it
/// </summary>
[UnityEngine.Header("Slope Changes")]
[UnityEngine.Tooltip("Whether or not to cancel grounding when the character is moving off a ledge. This prevents the character from \"snapping\" onto the ledge as it moves off of it")]
public bool PreventGroundingWhenMovingTowardsNoGrounding;
/// <summary>
/// Whether or not the character has a max slope change that it can stay grounded on
/// </summary>
[UnityEngine.Tooltip("Whether or not the character has a max slope change that it can stay grounded on")]
public bool HasMaxDownwardSlopeChangeAngle;
/// <summary>
/// Max slope change that the character can stay grounded on
/// </summary>
[UnityEngine.Tooltip("Max slope change that the character can stay grounded on")]
[UnityEngine.Range(0f, 180f)]
public float MaxDownwardSlopeChangeAngle;
/// <summary>
/// Whether or not to constrain the character velocity to ground plane when it hits a non-grounded slope
/// </summary>
[UnityEngine.Header("Misc")]
[UnityEngine.Tooltip("Whether or not to constrain the character velocity to ground plane when it hits a non-grounded slope")]
public bool ConstrainVelocityToGroundPlane;
public static BasicStepAndSlopeHandlingParameters GetDefault()
@ -354,6 +388,7 @@ namespace Rival
StepHandling = false,
MaxStepHeight = 0.5f,
ExtraStepChecksDistance = 0.1f,
CharacterWidthForStepGroundingCheck = 1f,
PreventGroundingWhenMovingTowardsNoGrounding = true,
HasMaxDownwardSlopeChangeAngle = false,
@ -1448,7 +1483,6 @@ namespace Rival
characterBody.IsGrounded,
characterBody.RelativeVelocity,
isGroundedOnMovementHit);
VelocityProjectionHits.Add(new KinematicVelocityProjectionHit(currentCharacterHit));
processor.OnMovementHit(
ref context,
@ -1918,6 +1952,7 @@ namespace Rival
float maxStepHeight,
float extraStepChecksDistance) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
{
KinematicCharacterBody characterBody = CharacterBody.ValueRO;
KinematicCharacterProperties characterProperties = CharacterProperties.ValueRO;
@ -2045,7 +2080,7 @@ namespace Rival
return false;
}
/// <summary>
/// Handles the stepping-up-a-step logic during character movement iterations
/// </summary>
@ -2060,6 +2095,7 @@ namespace Rival
/// <param name="hitDistance"></param>
/// <param name="stepHandling"></param>
/// <param name="maxStepHeight"></param>
/// <param name="characterWidthForStepGroundingCheck"> Character width used to determine grounding for steps. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height. </param>
/// <param name="hasSteppedUp"></param>
/// <typeparam name="T"> The type of the struct implementing <see cref="IKinematicCharacterProcessor{C}"/> </typeparam>
/// <typeparam name="C"> The type of the user-created context struct </typeparam>
@ -2075,6 +2111,7 @@ namespace Rival
float hitDistance,
bool stepHandling,
float maxStepHeight,
float characterWidthForStepGroundingCheck,
out bool hasSteppedUp) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
{
hasSteppedUp = false;
@ -2086,7 +2123,8 @@ namespace Rival
if (characterProperties.EvaluateGrounding &&
stepHandling &&
!hit.IsGroundedOnHit &&
maxStepHeight > 0f)
maxStepHeight > 0f &&
!PhysicsUtilities.IsBodyDynamic(in baseContext.PhysicsWorld, hit.RigidBodyIndex))
{
float3 startPositionOfUpCheck = characterPosition;
float3 upCheckDirection = characterBody.GroundingUp;
@ -2119,7 +2157,7 @@ namespace Rival
{
float3 startPositionOfForwardCheck = startPositionOfUpCheck + (upCheckDirection * upStepHitDistance);
float distanceOverStep = math.length(math.projectsafe(remainingMovementDirection * (remainingMovementLength - hitDistance), hit.Normal));
float3 endPositionOfForwardCheck = startPositionOfForwardCheck + (remainingMovementDirection * remainingMovementLength);
float3 endPositionOfForwardCheck = startPositionOfForwardCheck + (remainingMovementDirection * (remainingMovementLength + Constants.CollisionOffset));
float minimumDistanceOverStep = Constants.CollisionOffset * 3f;
if (distanceOverStep < minimumDistanceOverStep)
{
@ -2185,22 +2223,53 @@ namespace Rival
}
if (isGroundedOnStepHit)
{
float steppedHeight = upStepHitDistance - downStepHitDistance;
if (steppedHeight > Constants.CollisionOffset)
{
float hitHeight = upStepHitDistance - downStepHitDistance;
float steppedHeight = hitHeight;
steppedHeight = math.max(0f, steppedHeight + Constants.CollisionOffset);
// Add slope & character width consideration to stepped height
if(characterWidthForStepGroundingCheck > 0f)
{
// Find the effective slope normal
float3 forwardSlopeCheckDirection = -math.normalizesafe(math.cross(math.cross(characterBody.GroundingUp, stepHit.Normal), stepHit.Normal));
if (RaycastClosestCollisions(
in processor,
ref context,
ref baseContext,
stepHit.Position + (characterBody.GroundingUp * Constants.CollisionOffset) + (forwardSlopeCheckDirection * Constants.CollisionOffset),
-characterBody.GroundingUp,
maxStepHeight,
characterProperties.ShouldIgnoreDynamicBodies(),
out RaycastHit forwardSlopeCheckHit,
out float forwardSlopeCheckHitDistance))
{
float3 effectiveSlopeNormal = forwardSlopeCheckHit.SurfaceNormal;
float slopeRadians = MathUtilities.AngleRadians(characterBody.GroundingUp, effectiveSlopeNormal);
float extraHeightFromAngleAndCharacterWidth = math.tan(slopeRadians) * characterWidthForStepGroundingCheck * 0.5f;
steppedHeight += extraHeightFromAngleAndCharacterWidth;
}
}
if (steppedHeight < maxStepHeight)
{
// Step up
characterPosition += characterBody.GroundingUp * steppedHeight;
characterPosition += characterBody.GroundingUp * hitHeight;
characterPosition += forwardCheckDirection * forwardStepHitDistance;
characterBody.IsGrounded = true;
characterBody.GroundHit = stepHit;
// Project vel
float3 characterVelocityBeforeHit = characterBody.RelativeVelocity;
characterBody.RelativeVelocity = MathUtilities.ProjectOnPlane(characterBody.RelativeVelocity, characterBody.GroundingUp);
remainingMovementDirection = math.normalizesafe(characterBody.RelativeVelocity);
remainingMovementLength -= forwardStepHitDistance;
// Replace hit with step hit
hit = KinematicCharacterUtilities.CreateCharacterHit(stepHit, characterBody.IsGrounded, characterVelocityBeforeHit, isGroundedOnStepHit);
hit.CharacterVelocityAfterHit = characterBody.RelativeVelocity;
hasSteppedUp = true;
}
@ -2542,19 +2611,23 @@ namespace Rival
if (!isGroundedOnSlope && stepAndSlopeHandling.StepHandling && stepAndSlopeHandling.MaxStepHeight > 0f)
{
bool hitIsOnCharacterBottom = math.dot(characterBody.GroundingUp, hit.Normal) > Constants.DotProductSimilarityEpsilon;
if (hitIsOnCharacterBottom ||
(groundingEvaluationType != (int)GroundingEvaluationType.MovementHit && groundingEvaluationType != (int)GroundingEvaluationType.InitialOverlaps))
if (hitIsOnCharacterBottom &&
(groundingEvaluationType == (int)GroundingEvaluationType.GroundProbing || groundingEvaluationType == (int)GroundingEvaluationType.StepUpHit))
{
isGroundedOnSteps = IsGroundedOnSteps(
in processor,
ref context,
ref baseContext,
in hit,
stepAndSlopeHandling.MaxStepHeight,
stepAndSlopeHandling.ExtraStepChecksDistance);
// Prevent step grounding detection on dynamic bodies, to prevent cases of character stepping onto sphere rolling towards it
if (!PhysicsUtilities.IsBodyDynamic(in baseContext.PhysicsWorld, hit.RigidBodyIndex))
{
isGroundedOnSteps = IsGroundedOnSteps(
in processor,
ref context,
ref baseContext,
in hit,
stepAndSlopeHandling.MaxStepHeight,
stepAndSlopeHandling.ExtraStepChecksDistance);
}
}
}
return isGroundedOnSlope || isGroundedOnSteps;
}
@ -2666,7 +2739,7 @@ namespace Rival
ProjectVelocityOnSingleHit(ref velocity, ref characterIsGrounded, ref characterGroundHit, in firstHit, characterBody.GroundingUp);
velocityDirection = math.normalizesafe(velocity);
// Original velocity direction will act as a plane constaint just like other hits, to prevent our velocity from going back the way it came from. Hit index -1 represents original velocity
// Original velocity direction will act as a plane constraint just like other hits, to prevent our velocity from going back the way it came from. Hit index -1 represents original velocity
KinematicVelocityProjectionHit originalVelocityHit = default;
originalVelocityHit.Normal = characterIsGrounded ? math.normalizesafe(MathUtilities.ProjectOnPlane(originalVelocityDirection, characterBody.GroundingUp)) : originalVelocityDirection;
@ -2760,6 +2833,7 @@ namespace Rival
/// <param name="movementHitDistance"> Distance of the hit </param>
/// <param name="stepHandling"> Whether step-handling is enabled or not </param>
/// <param name="maxStepHeight"> Maximum height of steps that can be stepped on </param>
/// <param name="characterWidthForStepGroundingCheck"> Character width used to determine grounding for steps. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height. </param>
/// <typeparam name="T"> The type of the struct implementing <see cref="IKinematicCharacterProcessor{C}"/> </typeparam>
/// <typeparam name="C"> The type of the user-created context struct </typeparam>
public void Default_OnMovementHit<T, C>(
@ -2774,7 +2848,8 @@ namespace Rival
float3 originalVelocityDirection,
float movementHitDistance,
bool stepHandling,
float maxStepHeight) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
float maxStepHeight,
float characterWidthForStepGroundingCheck = 0f) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
{
bool hasSteppedUp = false;
@ -2794,8 +2869,12 @@ namespace Rival
movementHitDistance,
stepHandling,
maxStepHeight,
characterWidthForStepGroundingCheck,
out hasSteppedUp);
}
// Add velocityProjection hits only after potential correction from step handling
VelocityProjectionHits.Add(new KinematicVelocityProjectionHit(hit));
if (!hasSteppedUp)
{
@ -2814,7 +2893,7 @@ namespace Rival
ref characterBody.GroundHit,
in VelocityProjectionHits,
originalVelocityDirection);
// Recalculate remaining movement after projection
float projectedVelocityLengthFactor = math.length(characterBody.RelativeVelocity) / math.length(velocityBeforeProjection);
remainingMovementLength *= projectedVelocityLengthFactor;

Просмотреть файл

@ -131,7 +131,7 @@ namespace Rival
// Grounding
EvaluateGrounding = true,
SnapToGround = true,
GroundSnappingDistance = 0.3f,
GroundSnappingDistance = 0.5f,
EnhancedGroundPrecision = false,
MaxGroundedSlopeAngle = 60f,

Просмотреть файл

@ -270,22 +270,25 @@ namespace Rival
float3 relativeVelocityAToB = pointVelocityB - pointVelocityA;
float relativeVelocityOnNormal = math.dot(relativeVelocityAToB, collisionNormalBToA);
float3 crossA = math.cross(centerOfMassAToPoint, collisionNormalBToA);
float3 crossB = math.cross(collisionNormalBToA, centerOfMassBToPoint);
float3 angularA = math.mul(new Math.MTransform(transformA).InverseRotation, crossA).xyz;
float3 angularB = math.mul(new Math.MTransform(transformB).InverseRotation, crossB).xyz;
float3 temp = angularA * angularA * physicsMassA.InverseInertia + angularB * angularB * physicsMassB.InverseInertia;
float invEffectiveMass = temp.x + temp.y + temp.z + (physicsMassA.InverseMass + physicsMassB.InverseMass);
if (invEffectiveMass > 0f)
if (relativeVelocityOnNormal > 0f)
{
float effectiveMass = 1f / invEffectiveMass;
float3 crossA = math.cross(centerOfMassAToPoint, collisionNormalBToA);
float3 crossB = math.cross(collisionNormalBToA, centerOfMassBToPoint);
float3 angularA = math.mul(new Math.MTransform(transformA).InverseRotation, crossA).xyz;
float3 angularB = math.mul(new Math.MTransform(transformB).InverseRotation, crossB).xyz;
float3 temp = angularA * angularA * physicsMassA.InverseInertia + angularB * angularB * physicsMassB.InverseInertia;
float invEffectiveMass = temp.x + temp.y + temp.z + (physicsMassA.InverseMass + physicsMassB.InverseMass);
float impulseScale = -relativeVelocityOnNormal * effectiveMass;
float3 totalImpulse = collisionNormalBToA * impulseScale;
if (invEffectiveMass > 0f)
{
float effectiveMass = 1f / invEffectiveMass;
impulseOnA = -totalImpulse;
impulseOnB = totalImpulse;
float impulseScale = -relativeVelocityOnNormal * effectiveMass;
float3 totalImpulse = collisionNormalBToA * impulseScale;
impulseOnA = -totalImpulse;
impulseOnB = totalImpulse;
}
}
}

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -361,7 +361,8 @@ public readonly partial struct PlatformerCharacterAspect : IAspect, IKinematicCh
originalVelocityDirection,
hitDistance,
characterComponent.StepAndSlopeHandling.StepHandling,
characterComponent.StepAndSlopeHandling.MaxStepHeight);
characterComponent.StepAndSlopeHandling.MaxStepHeight,
characterComponent.StepAndSlopeHandling.CharacterWidthForStepGroundingCheck);
}
public void OverrideDynamicHitMasses(

Просмотреть файл

@ -1,2 +1,2 @@
m_EditorVersion: 2022.2.0f1
m_EditorVersionWithRevision: 2022.2.0f1 (35dcd44975df)
m_EditorVersion: 2022.2.1f1
m_EditorVersionWithRevision: 2022.2.1f1 (4fead5835099)

Просмотреть файл

@ -12,6 +12,7 @@ using Unity.Physics.Authoring;
using Unity.Physics.Extensions;
using Unity.Physics.Systems;
using Unity.Transforms;
using UnityEngine;
using Material = Unity.Physics.Material;
using RaycastHit = Unity.Physics.RaycastHit;
@ -333,18 +334,51 @@ namespace Rival
[Serializable]
public struct BasicStepAndSlopeHandlingParameters
{
/// <summary>
/// Whether or not step handling logic is enabled
/// </summary>
[UnityEngine.Header("Step Handling")]
[UnityEngine.Tooltip("Whether or not step handling logic is enabled")]
public bool StepHandling;
/// <summary>
/// Max height that the character can step on
/// </summary>
[UnityEngine.Tooltip("Max height that the character can step on")]
public float MaxStepHeight;
/// <summary>
/// Horizontal offset distance of extra downwards raycasts used to detect grounding around a step
/// </summary>
[UnityEngine.Tooltip("Horizontal offset distance of extra downwards raycasts used to detect grounding around a step")]
public float ExtraStepChecksDistance;
/// <summary>
/// Character width used to determine grounding for steps. For a capsule this should be 2x capsule radius, and for a box it should be maximum box width. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height.
/// </summary>
[UnityEngine.Tooltip("Character width used to determine grounding for steps. For a capsule this should be 2x capsule radius, and for a box it should be maximum box width. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height.")]
public float CharacterWidthForStepGroundingCheck;
/// <summary>
/// Whether or not to cancel grounding when the character is moving off a ledge. This prevents the character from "snapping" onto the ledge as it moves off of it
/// </summary>
[UnityEngine.Header("Slope Changes")]
[UnityEngine.Tooltip("Whether or not to cancel grounding when the character is moving off a ledge. This prevents the character from \"snapping\" onto the ledge as it moves off of it")]
public bool PreventGroundingWhenMovingTowardsNoGrounding;
/// <summary>
/// Whether or not the character has a max slope change that it can stay grounded on
/// </summary>
[UnityEngine.Tooltip("Whether or not the character has a max slope change that it can stay grounded on")]
public bool HasMaxDownwardSlopeChangeAngle;
/// <summary>
/// Max slope change that the character can stay grounded on
/// </summary>
[UnityEngine.Tooltip("Max slope change that the character can stay grounded on")]
[UnityEngine.Range(0f, 180f)]
public float MaxDownwardSlopeChangeAngle;
/// <summary>
/// Whether or not to constrain the character velocity to ground plane when it hits a non-grounded slope
/// </summary>
[UnityEngine.Header("Misc")]
[UnityEngine.Tooltip("Whether or not to constrain the character velocity to ground plane when it hits a non-grounded slope")]
public bool ConstrainVelocityToGroundPlane;
public static BasicStepAndSlopeHandlingParameters GetDefault()
@ -354,6 +388,7 @@ namespace Rival
StepHandling = false,
MaxStepHeight = 0.5f,
ExtraStepChecksDistance = 0.1f,
CharacterWidthForStepGroundingCheck = 1f,
PreventGroundingWhenMovingTowardsNoGrounding = true,
HasMaxDownwardSlopeChangeAngle = false,
@ -1448,7 +1483,6 @@ namespace Rival
characterBody.IsGrounded,
characterBody.RelativeVelocity,
isGroundedOnMovementHit);
VelocityProjectionHits.Add(new KinematicVelocityProjectionHit(currentCharacterHit));
processor.OnMovementHit(
ref context,
@ -1918,6 +1952,7 @@ namespace Rival
float maxStepHeight,
float extraStepChecksDistance) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
{
KinematicCharacterBody characterBody = CharacterBody.ValueRO;
KinematicCharacterProperties characterProperties = CharacterProperties.ValueRO;
@ -2045,7 +2080,7 @@ namespace Rival
return false;
}
/// <summary>
/// Handles the stepping-up-a-step logic during character movement iterations
/// </summary>
@ -2060,6 +2095,7 @@ namespace Rival
/// <param name="hitDistance"></param>
/// <param name="stepHandling"></param>
/// <param name="maxStepHeight"></param>
/// <param name="characterWidthForStepGroundingCheck"> Character width used to determine grounding for steps. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height. </param>
/// <param name="hasSteppedUp"></param>
/// <typeparam name="T"> The type of the struct implementing <see cref="IKinematicCharacterProcessor{C}"/> </typeparam>
/// <typeparam name="C"> The type of the user-created context struct </typeparam>
@ -2075,6 +2111,7 @@ namespace Rival
float hitDistance,
bool stepHandling,
float maxStepHeight,
float characterWidthForStepGroundingCheck,
out bool hasSteppedUp) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
{
hasSteppedUp = false;
@ -2086,7 +2123,8 @@ namespace Rival
if (characterProperties.EvaluateGrounding &&
stepHandling &&
!hit.IsGroundedOnHit &&
maxStepHeight > 0f)
maxStepHeight > 0f &&
!PhysicsUtilities.IsBodyDynamic(in baseContext.PhysicsWorld, hit.RigidBodyIndex))
{
float3 startPositionOfUpCheck = characterPosition;
float3 upCheckDirection = characterBody.GroundingUp;
@ -2119,7 +2157,7 @@ namespace Rival
{
float3 startPositionOfForwardCheck = startPositionOfUpCheck + (upCheckDirection * upStepHitDistance);
float distanceOverStep = math.length(math.projectsafe(remainingMovementDirection * (remainingMovementLength - hitDistance), hit.Normal));
float3 endPositionOfForwardCheck = startPositionOfForwardCheck + (remainingMovementDirection * remainingMovementLength);
float3 endPositionOfForwardCheck = startPositionOfForwardCheck + (remainingMovementDirection * (remainingMovementLength + Constants.CollisionOffset));
float minimumDistanceOverStep = Constants.CollisionOffset * 3f;
if (distanceOverStep < minimumDistanceOverStep)
{
@ -2185,22 +2223,53 @@ namespace Rival
}
if (isGroundedOnStepHit)
{
float steppedHeight = upStepHitDistance - downStepHitDistance;
if (steppedHeight > Constants.CollisionOffset)
{
float hitHeight = upStepHitDistance - downStepHitDistance;
float steppedHeight = hitHeight;
steppedHeight = math.max(0f, steppedHeight + Constants.CollisionOffset);
// Add slope & character width consideration to stepped height
if(characterWidthForStepGroundingCheck > 0f)
{
// Find the effective slope normal
float3 forwardSlopeCheckDirection = -math.normalizesafe(math.cross(math.cross(characterBody.GroundingUp, stepHit.Normal), stepHit.Normal));
if (RaycastClosestCollisions(
in processor,
ref context,
ref baseContext,
stepHit.Position + (characterBody.GroundingUp * Constants.CollisionOffset) + (forwardSlopeCheckDirection * Constants.CollisionOffset),
-characterBody.GroundingUp,
maxStepHeight,
characterProperties.ShouldIgnoreDynamicBodies(),
out RaycastHit forwardSlopeCheckHit,
out float forwardSlopeCheckHitDistance))
{
float3 effectiveSlopeNormal = forwardSlopeCheckHit.SurfaceNormal;
float slopeRadians = MathUtilities.AngleRadians(characterBody.GroundingUp, effectiveSlopeNormal);
float extraHeightFromAngleAndCharacterWidth = math.tan(slopeRadians) * characterWidthForStepGroundingCheck * 0.5f;
steppedHeight += extraHeightFromAngleAndCharacterWidth;
}
}
if (steppedHeight < maxStepHeight)
{
// Step up
characterPosition += characterBody.GroundingUp * steppedHeight;
characterPosition += characterBody.GroundingUp * hitHeight;
characterPosition += forwardCheckDirection * forwardStepHitDistance;
characterBody.IsGrounded = true;
characterBody.GroundHit = stepHit;
// Project vel
float3 characterVelocityBeforeHit = characterBody.RelativeVelocity;
characterBody.RelativeVelocity = MathUtilities.ProjectOnPlane(characterBody.RelativeVelocity, characterBody.GroundingUp);
remainingMovementDirection = math.normalizesafe(characterBody.RelativeVelocity);
remainingMovementLength -= forwardStepHitDistance;
// Replace hit with step hit
hit = KinematicCharacterUtilities.CreateCharacterHit(stepHit, characterBody.IsGrounded, characterVelocityBeforeHit, isGroundedOnStepHit);
hit.CharacterVelocityAfterHit = characterBody.RelativeVelocity;
hasSteppedUp = true;
}
@ -2542,19 +2611,23 @@ namespace Rival
if (!isGroundedOnSlope && stepAndSlopeHandling.StepHandling && stepAndSlopeHandling.MaxStepHeight > 0f)
{
bool hitIsOnCharacterBottom = math.dot(characterBody.GroundingUp, hit.Normal) > Constants.DotProductSimilarityEpsilon;
if (hitIsOnCharacterBottom ||
(groundingEvaluationType != (int)GroundingEvaluationType.MovementHit && groundingEvaluationType != (int)GroundingEvaluationType.InitialOverlaps))
if (hitIsOnCharacterBottom &&
(groundingEvaluationType == (int)GroundingEvaluationType.GroundProbing || groundingEvaluationType == (int)GroundingEvaluationType.StepUpHit))
{
isGroundedOnSteps = IsGroundedOnSteps(
in processor,
ref context,
ref baseContext,
in hit,
stepAndSlopeHandling.MaxStepHeight,
stepAndSlopeHandling.ExtraStepChecksDistance);
// Prevent step grounding detection on dynamic bodies, to prevent cases of character stepping onto sphere rolling towards it
if (!PhysicsUtilities.IsBodyDynamic(in baseContext.PhysicsWorld, hit.RigidBodyIndex))
{
isGroundedOnSteps = IsGroundedOnSteps(
in processor,
ref context,
ref baseContext,
in hit,
stepAndSlopeHandling.MaxStepHeight,
stepAndSlopeHandling.ExtraStepChecksDistance);
}
}
}
return isGroundedOnSlope || isGroundedOnSteps;
}
@ -2666,7 +2739,7 @@ namespace Rival
ProjectVelocityOnSingleHit(ref velocity, ref characterIsGrounded, ref characterGroundHit, in firstHit, characterBody.GroundingUp);
velocityDirection = math.normalizesafe(velocity);
// Original velocity direction will act as a plane constaint just like other hits, to prevent our velocity from going back the way it came from. Hit index -1 represents original velocity
// Original velocity direction will act as a plane constraint just like other hits, to prevent our velocity from going back the way it came from. Hit index -1 represents original velocity
KinematicVelocityProjectionHit originalVelocityHit = default;
originalVelocityHit.Normal = characterIsGrounded ? math.normalizesafe(MathUtilities.ProjectOnPlane(originalVelocityDirection, characterBody.GroundingUp)) : originalVelocityDirection;
@ -2760,6 +2833,7 @@ namespace Rival
/// <param name="movementHitDistance"> Distance of the hit </param>
/// <param name="stepHandling"> Whether step-handling is enabled or not </param>
/// <param name="maxStepHeight"> Maximum height of steps that can be stepped on </param>
/// <param name="characterWidthForStepGroundingCheck"> Character width used to determine grounding for steps. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height. </param>
/// <typeparam name="T"> The type of the struct implementing <see cref="IKinematicCharacterProcessor{C}"/> </typeparam>
/// <typeparam name="C"> The type of the user-created context struct </typeparam>
public void Default_OnMovementHit<T, C>(
@ -2774,7 +2848,8 @@ namespace Rival
float3 originalVelocityDirection,
float movementHitDistance,
bool stepHandling,
float maxStepHeight) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
float maxStepHeight,
float characterWidthForStepGroundingCheck = 0f) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
{
bool hasSteppedUp = false;
@ -2794,8 +2869,12 @@ namespace Rival
movementHitDistance,
stepHandling,
maxStepHeight,
characterWidthForStepGroundingCheck,
out hasSteppedUp);
}
// Add velocityProjection hits only after potential correction from step handling
VelocityProjectionHits.Add(new KinematicVelocityProjectionHit(hit));
if (!hasSteppedUp)
{
@ -2814,7 +2893,7 @@ namespace Rival
ref characterBody.GroundHit,
in VelocityProjectionHits,
originalVelocityDirection);
// Recalculate remaining movement after projection
float projectedVelocityLengthFactor = math.length(characterBody.RelativeVelocity) / math.length(velocityBeforeProjection);
remainingMovementLength *= projectedVelocityLengthFactor;

Просмотреть файл

@ -131,7 +131,7 @@ namespace Rival
// Grounding
EvaluateGrounding = true,
SnapToGround = true,
GroundSnappingDistance = 0.3f,
GroundSnappingDistance = 0.5f,
EnhancedGroundPrecision = false,
MaxGroundedSlopeAngle = 60f,

Просмотреть файл

@ -270,22 +270,25 @@ namespace Rival
float3 relativeVelocityAToB = pointVelocityB - pointVelocityA;
float relativeVelocityOnNormal = math.dot(relativeVelocityAToB, collisionNormalBToA);
float3 crossA = math.cross(centerOfMassAToPoint, collisionNormalBToA);
float3 crossB = math.cross(collisionNormalBToA, centerOfMassBToPoint);
float3 angularA = math.mul(new Math.MTransform(transformA).InverseRotation, crossA).xyz;
float3 angularB = math.mul(new Math.MTransform(transformB).InverseRotation, crossB).xyz;
float3 temp = angularA * angularA * physicsMassA.InverseInertia + angularB * angularB * physicsMassB.InverseInertia;
float invEffectiveMass = temp.x + temp.y + temp.z + (physicsMassA.InverseMass + physicsMassB.InverseMass);
if (invEffectiveMass > 0f)
if (relativeVelocityOnNormal > 0f)
{
float effectiveMass = 1f / invEffectiveMass;
float3 crossA = math.cross(centerOfMassAToPoint, collisionNormalBToA);
float3 crossB = math.cross(collisionNormalBToA, centerOfMassBToPoint);
float3 angularA = math.mul(new Math.MTransform(transformA).InverseRotation, crossA).xyz;
float3 angularB = math.mul(new Math.MTransform(transformB).InverseRotation, crossB).xyz;
float3 temp = angularA * angularA * physicsMassA.InverseInertia + angularB * angularB * physicsMassB.InverseInertia;
float invEffectiveMass = temp.x + temp.y + temp.z + (physicsMassA.InverseMass + physicsMassB.InverseMass);
float impulseScale = -relativeVelocityOnNormal * effectiveMass;
float3 totalImpulse = collisionNormalBToA * impulseScale;
if (invEffectiveMass > 0f)
{
float effectiveMass = 1f / invEffectiveMass;
impulseOnA = -totalImpulse;
impulseOnB = totalImpulse;
float impulseScale = -relativeVelocityOnNormal * effectiveMass;
float3 totalImpulse = collisionNormalBToA * impulseScale;
impulseOnA = -totalImpulse;
impulseOnB = totalImpulse;
}
}
}

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: ff97f6f657bbe9caa9004db02d37109a
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -192,7 +192,8 @@ public readonly partial struct StressTestCharacterAspect : IAspect, IKinematicCh
originalVelocityDirection,
hitDistance,
characterComponent.StepAndSlopeHandling.StepHandling,
characterComponent.StepAndSlopeHandling.MaxStepHeight);
characterComponent.StepAndSlopeHandling.MaxStepHeight,
characterComponent.StepAndSlopeHandling.CharacterWidthForStepGroundingCheck);
}
public void OverrideDynamicHitMasses(

Просмотреть файл

@ -1,2 +1,2 @@
m_EditorVersion: 2022.2.0f1
m_EditorVersionWithRevision: 2022.2.0f1 (35dcd44975df)
m_EditorVersion: 2022.2.1f1
m_EditorVersionWithRevision: 2022.2.1f1 (4fead5835099)

Просмотреть файл

@ -12,6 +12,7 @@ using Unity.Physics.Authoring;
using Unity.Physics.Extensions;
using Unity.Physics.Systems;
using Unity.Transforms;
using UnityEngine;
using Material = Unity.Physics.Material;
using RaycastHit = Unity.Physics.RaycastHit;
@ -333,18 +334,51 @@ namespace Rival
[Serializable]
public struct BasicStepAndSlopeHandlingParameters
{
/// <summary>
/// Whether or not step handling logic is enabled
/// </summary>
[UnityEngine.Header("Step Handling")]
[UnityEngine.Tooltip("Whether or not step handling logic is enabled")]
public bool StepHandling;
/// <summary>
/// Max height that the character can step on
/// </summary>
[UnityEngine.Tooltip("Max height that the character can step on")]
public float MaxStepHeight;
/// <summary>
/// Horizontal offset distance of extra downwards raycasts used to detect grounding around a step
/// </summary>
[UnityEngine.Tooltip("Horizontal offset distance of extra downwards raycasts used to detect grounding around a step")]
public float ExtraStepChecksDistance;
/// <summary>
/// Character width used to determine grounding for steps. For a capsule this should be 2x capsule radius, and for a box it should be maximum box width. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height.
/// </summary>
[UnityEngine.Tooltip("Character width used to determine grounding for steps. For a capsule this should be 2x capsule radius, and for a box it should be maximum box width. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height.")]
public float CharacterWidthForStepGroundingCheck;
/// <summary>
/// Whether or not to cancel grounding when the character is moving off a ledge. This prevents the character from "snapping" onto the ledge as it moves off of it
/// </summary>
[UnityEngine.Header("Slope Changes")]
[UnityEngine.Tooltip("Whether or not to cancel grounding when the character is moving off a ledge. This prevents the character from \"snapping\" onto the ledge as it moves off of it")]
public bool PreventGroundingWhenMovingTowardsNoGrounding;
/// <summary>
/// Whether or not the character has a max slope change that it can stay grounded on
/// </summary>
[UnityEngine.Tooltip("Whether or not the character has a max slope change that it can stay grounded on")]
public bool HasMaxDownwardSlopeChangeAngle;
/// <summary>
/// Max slope change that the character can stay grounded on
/// </summary>
[UnityEngine.Tooltip("Max slope change that the character can stay grounded on")]
[UnityEngine.Range(0f, 180f)]
public float MaxDownwardSlopeChangeAngle;
/// <summary>
/// Whether or not to constrain the character velocity to ground plane when it hits a non-grounded slope
/// </summary>
[UnityEngine.Header("Misc")]
[UnityEngine.Tooltip("Whether or not to constrain the character velocity to ground plane when it hits a non-grounded slope")]
public bool ConstrainVelocityToGroundPlane;
public static BasicStepAndSlopeHandlingParameters GetDefault()
@ -354,6 +388,7 @@ namespace Rival
StepHandling = false,
MaxStepHeight = 0.5f,
ExtraStepChecksDistance = 0.1f,
CharacterWidthForStepGroundingCheck = 1f,
PreventGroundingWhenMovingTowardsNoGrounding = true,
HasMaxDownwardSlopeChangeAngle = false,
@ -1448,7 +1483,6 @@ namespace Rival
characterBody.IsGrounded,
characterBody.RelativeVelocity,
isGroundedOnMovementHit);
VelocityProjectionHits.Add(new KinematicVelocityProjectionHit(currentCharacterHit));
processor.OnMovementHit(
ref context,
@ -1918,6 +1952,7 @@ namespace Rival
float maxStepHeight,
float extraStepChecksDistance) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
{
KinematicCharacterBody characterBody = CharacterBody.ValueRO;
KinematicCharacterProperties characterProperties = CharacterProperties.ValueRO;
@ -2045,7 +2080,7 @@ namespace Rival
return false;
}
/// <summary>
/// Handles the stepping-up-a-step logic during character movement iterations
/// </summary>
@ -2060,6 +2095,7 @@ namespace Rival
/// <param name="hitDistance"></param>
/// <param name="stepHandling"></param>
/// <param name="maxStepHeight"></param>
/// <param name="characterWidthForStepGroundingCheck"> Character width used to determine grounding for steps. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height. </param>
/// <param name="hasSteppedUp"></param>
/// <typeparam name="T"> The type of the struct implementing <see cref="IKinematicCharacterProcessor{C}"/> </typeparam>
/// <typeparam name="C"> The type of the user-created context struct </typeparam>
@ -2075,6 +2111,7 @@ namespace Rival
float hitDistance,
bool stepHandling,
float maxStepHeight,
float characterWidthForStepGroundingCheck,
out bool hasSteppedUp) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
{
hasSteppedUp = false;
@ -2086,7 +2123,8 @@ namespace Rival
if (characterProperties.EvaluateGrounding &&
stepHandling &&
!hit.IsGroundedOnHit &&
maxStepHeight > 0f)
maxStepHeight > 0f &&
!PhysicsUtilities.IsBodyDynamic(in baseContext.PhysicsWorld, hit.RigidBodyIndex))
{
float3 startPositionOfUpCheck = characterPosition;
float3 upCheckDirection = characterBody.GroundingUp;
@ -2119,7 +2157,7 @@ namespace Rival
{
float3 startPositionOfForwardCheck = startPositionOfUpCheck + (upCheckDirection * upStepHitDistance);
float distanceOverStep = math.length(math.projectsafe(remainingMovementDirection * (remainingMovementLength - hitDistance), hit.Normal));
float3 endPositionOfForwardCheck = startPositionOfForwardCheck + (remainingMovementDirection * remainingMovementLength);
float3 endPositionOfForwardCheck = startPositionOfForwardCheck + (remainingMovementDirection * (remainingMovementLength + Constants.CollisionOffset));
float minimumDistanceOverStep = Constants.CollisionOffset * 3f;
if (distanceOverStep < minimumDistanceOverStep)
{
@ -2185,22 +2223,53 @@ namespace Rival
}
if (isGroundedOnStepHit)
{
float steppedHeight = upStepHitDistance - downStepHitDistance;
if (steppedHeight > Constants.CollisionOffset)
{
float hitHeight = upStepHitDistance - downStepHitDistance;
float steppedHeight = hitHeight;
steppedHeight = math.max(0f, steppedHeight + Constants.CollisionOffset);
// Add slope & character width consideration to stepped height
if(characterWidthForStepGroundingCheck > 0f)
{
// Find the effective slope normal
float3 forwardSlopeCheckDirection = -math.normalizesafe(math.cross(math.cross(characterBody.GroundingUp, stepHit.Normal), stepHit.Normal));
if (RaycastClosestCollisions(
in processor,
ref context,
ref baseContext,
stepHit.Position + (characterBody.GroundingUp * Constants.CollisionOffset) + (forwardSlopeCheckDirection * Constants.CollisionOffset),
-characterBody.GroundingUp,
maxStepHeight,
characterProperties.ShouldIgnoreDynamicBodies(),
out RaycastHit forwardSlopeCheckHit,
out float forwardSlopeCheckHitDistance))
{
float3 effectiveSlopeNormal = forwardSlopeCheckHit.SurfaceNormal;
float slopeRadians = MathUtilities.AngleRadians(characterBody.GroundingUp, effectiveSlopeNormal);
float extraHeightFromAngleAndCharacterWidth = math.tan(slopeRadians) * characterWidthForStepGroundingCheck * 0.5f;
steppedHeight += extraHeightFromAngleAndCharacterWidth;
}
}
if (steppedHeight < maxStepHeight)
{
// Step up
characterPosition += characterBody.GroundingUp * steppedHeight;
characterPosition += characterBody.GroundingUp * hitHeight;
characterPosition += forwardCheckDirection * forwardStepHitDistance;
characterBody.IsGrounded = true;
characterBody.GroundHit = stepHit;
// Project vel
float3 characterVelocityBeforeHit = characterBody.RelativeVelocity;
characterBody.RelativeVelocity = MathUtilities.ProjectOnPlane(characterBody.RelativeVelocity, characterBody.GroundingUp);
remainingMovementDirection = math.normalizesafe(characterBody.RelativeVelocity);
remainingMovementLength -= forwardStepHitDistance;
// Replace hit with step hit
hit = KinematicCharacterUtilities.CreateCharacterHit(stepHit, characterBody.IsGrounded, characterVelocityBeforeHit, isGroundedOnStepHit);
hit.CharacterVelocityAfterHit = characterBody.RelativeVelocity;
hasSteppedUp = true;
}
@ -2542,19 +2611,23 @@ namespace Rival
if (!isGroundedOnSlope && stepAndSlopeHandling.StepHandling && stepAndSlopeHandling.MaxStepHeight > 0f)
{
bool hitIsOnCharacterBottom = math.dot(characterBody.GroundingUp, hit.Normal) > Constants.DotProductSimilarityEpsilon;
if (hitIsOnCharacterBottom ||
(groundingEvaluationType != (int)GroundingEvaluationType.MovementHit && groundingEvaluationType != (int)GroundingEvaluationType.InitialOverlaps))
if (hitIsOnCharacterBottom &&
(groundingEvaluationType == (int)GroundingEvaluationType.GroundProbing || groundingEvaluationType == (int)GroundingEvaluationType.StepUpHit))
{
isGroundedOnSteps = IsGroundedOnSteps(
in processor,
ref context,
ref baseContext,
in hit,
stepAndSlopeHandling.MaxStepHeight,
stepAndSlopeHandling.ExtraStepChecksDistance);
// Prevent step grounding detection on dynamic bodies, to prevent cases of character stepping onto sphere rolling towards it
if (!PhysicsUtilities.IsBodyDynamic(in baseContext.PhysicsWorld, hit.RigidBodyIndex))
{
isGroundedOnSteps = IsGroundedOnSteps(
in processor,
ref context,
ref baseContext,
in hit,
stepAndSlopeHandling.MaxStepHeight,
stepAndSlopeHandling.ExtraStepChecksDistance);
}
}
}
return isGroundedOnSlope || isGroundedOnSteps;
}
@ -2666,7 +2739,7 @@ namespace Rival
ProjectVelocityOnSingleHit(ref velocity, ref characterIsGrounded, ref characterGroundHit, in firstHit, characterBody.GroundingUp);
velocityDirection = math.normalizesafe(velocity);
// Original velocity direction will act as a plane constaint just like other hits, to prevent our velocity from going back the way it came from. Hit index -1 represents original velocity
// Original velocity direction will act as a plane constraint just like other hits, to prevent our velocity from going back the way it came from. Hit index -1 represents original velocity
KinematicVelocityProjectionHit originalVelocityHit = default;
originalVelocityHit.Normal = characterIsGrounded ? math.normalizesafe(MathUtilities.ProjectOnPlane(originalVelocityDirection, characterBody.GroundingUp)) : originalVelocityDirection;
@ -2760,6 +2833,7 @@ namespace Rival
/// <param name="movementHitDistance"> Distance of the hit </param>
/// <param name="stepHandling"> Whether step-handling is enabled or not </param>
/// <param name="maxStepHeight"> Maximum height of steps that can be stepped on </param>
/// <param name="characterWidthForStepGroundingCheck"> Character width used to determine grounding for steps. This is for cases where character with a spherical base tries to step onto an angled surface that is near the character's max step height. In thoses cases, the character might be grounded on steps on one frame, but wouldn't be grounded on the next frame as the spherical nature of its shape would push it a bit further up beyond its max step height. </param>
/// <typeparam name="T"> The type of the struct implementing <see cref="IKinematicCharacterProcessor{C}"/> </typeparam>
/// <typeparam name="C"> The type of the user-created context struct </typeparam>
public void Default_OnMovementHit<T, C>(
@ -2774,7 +2848,8 @@ namespace Rival
float3 originalVelocityDirection,
float movementHitDistance,
bool stepHandling,
float maxStepHeight) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
float maxStepHeight,
float characterWidthForStepGroundingCheck = 0f) where T : unmanaged, IKinematicCharacterProcessor<C> where C : unmanaged
{
bool hasSteppedUp = false;
@ -2794,8 +2869,12 @@ namespace Rival
movementHitDistance,
stepHandling,
maxStepHeight,
characterWidthForStepGroundingCheck,
out hasSteppedUp);
}
// Add velocityProjection hits only after potential correction from step handling
VelocityProjectionHits.Add(new KinematicVelocityProjectionHit(hit));
if (!hasSteppedUp)
{
@ -2814,7 +2893,7 @@ namespace Rival
ref characterBody.GroundHit,
in VelocityProjectionHits,
originalVelocityDirection);
// Recalculate remaining movement after projection
float projectedVelocityLengthFactor = math.length(characterBody.RelativeVelocity) / math.length(velocityBeforeProjection);
remainingMovementLength *= projectedVelocityLengthFactor;

Просмотреть файл

@ -131,7 +131,7 @@ namespace Rival
// Grounding
EvaluateGrounding = true,
SnapToGround = true,
GroundSnappingDistance = 0.3f,
GroundSnappingDistance = 0.5f,
EnhancedGroundPrecision = false,
MaxGroundedSlopeAngle = 60f,

Просмотреть файл

@ -270,22 +270,25 @@ namespace Rival
float3 relativeVelocityAToB = pointVelocityB - pointVelocityA;
float relativeVelocityOnNormal = math.dot(relativeVelocityAToB, collisionNormalBToA);
float3 crossA = math.cross(centerOfMassAToPoint, collisionNormalBToA);
float3 crossB = math.cross(collisionNormalBToA, centerOfMassBToPoint);
float3 angularA = math.mul(new Math.MTransform(transformA).InverseRotation, crossA).xyz;
float3 angularB = math.mul(new Math.MTransform(transformB).InverseRotation, crossB).xyz;
float3 temp = angularA * angularA * physicsMassA.InverseInertia + angularB * angularB * physicsMassB.InverseInertia;
float invEffectiveMass = temp.x + temp.y + temp.z + (physicsMassA.InverseMass + physicsMassB.InverseMass);
if (invEffectiveMass > 0f)
if (relativeVelocityOnNormal > 0f)
{
float effectiveMass = 1f / invEffectiveMass;
float3 crossA = math.cross(centerOfMassAToPoint, collisionNormalBToA);
float3 crossB = math.cross(collisionNormalBToA, centerOfMassBToPoint);
float3 angularA = math.mul(new Math.MTransform(transformA).InverseRotation, crossA).xyz;
float3 angularB = math.mul(new Math.MTransform(transformB).InverseRotation, crossB).xyz;
float3 temp = angularA * angularA * physicsMassA.InverseInertia + angularB * angularB * physicsMassB.InverseInertia;
float invEffectiveMass = temp.x + temp.y + temp.z + (physicsMassA.InverseMass + physicsMassB.InverseMass);
float impulseScale = -relativeVelocityOnNormal * effectiveMass;
float3 totalImpulse = collisionNormalBToA * impulseScale;
if (invEffectiveMass > 0f)
{
float effectiveMass = 1f / invEffectiveMass;
impulseOnA = -totalImpulse;
impulseOnB = totalImpulse;
float impulseScale = -relativeVelocityOnNormal * effectiveMass;
float3 totalImpulse = collisionNormalBToA * impulseScale;
impulseOnA = -totalImpulse;
impulseOnB = totalImpulse;
}
}
}

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -227,7 +227,8 @@ public readonly partial struct ThirdPersonCharacterAspect : IAspect, IKinematicC
originalVelocityDirection,
hitDistance,
characterComponent.StepAndSlopeHandling.StepHandling,
characterComponent.StepAndSlopeHandling.MaxStepHeight);
characterComponent.StepAndSlopeHandling.MaxStepHeight,
characterComponent.StepAndSlopeHandling.CharacterWidthForStepGroundingCheck);
}
public void OverrideDynamicHitMasses(

Просмотреть файл

@ -1333,6 +1333,7 @@ MonoBehaviour:
StepHandling: 1
MaxStepHeight: 0.5
ExtraStepChecksDistance: 0.1
CharacterWidthForStepGroundingCheck: 1
PreventGroundingWhenMovingTowardsNoGrounding: 1
HasMaxDownwardSlopeChangeAngle: 0
MaxDownwardSlopeChangeAngle: 90
@ -1659,6 +1660,11 @@ PrefabInstance:
propertyPath: m_Name
value: ThirdPersonCharacter
objectReference: {fileID: 0}
- target: {fileID: 6689285571920954877, guid: 5fbcc801dea8fd640a004697d22ed448,
type: 3}
propertyPath: CharacterProperties.GroundSnappingDistance
value: 0.5
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
@ -3044,6 +3050,7 @@ MonoBehaviour:
StepHandling: 1
MaxStepHeight: 0.5
ExtraStepChecksDistance: 0.1
CharacterWidthForStepGroundingCheck: 1
PreventGroundingWhenMovingTowardsNoGrounding: 1
HasMaxDownwardSlopeChangeAngle: 0
MaxDownwardSlopeChangeAngle: 90
@ -5028,6 +5035,7 @@ MonoBehaviour:
StepHandling: 1
MaxStepHeight: 0.5
ExtraStepChecksDistance: 0.1
CharacterWidthForStepGroundingCheck: 1
PreventGroundingWhenMovingTowardsNoGrounding: 1
HasMaxDownwardSlopeChangeAngle: 0
MaxDownwardSlopeChangeAngle: 90

Просмотреть файл

@ -1,2 +1,2 @@
m_EditorVersion: 2022.2.0f1
m_EditorVersionWithRevision: 2022.2.0f1 (35dcd44975df)
m_EditorVersion: 2022.2.1f1
m_EditorVersionWithRevision: 2022.2.1f1 (4fead5835099)