361 lines
14 KiB
C#
361 lines
14 KiB
C#
|
|
using System.Collections;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using UnityEngine;
|
|||
|
|
using KinematicCharacterController;
|
|||
|
|
|
|||
|
|
|
|||
|
|
namespace CustomUse
|
|||
|
|
{
|
|||
|
|
//Ĭ<><C4AC>QE<51>ǿ<EFBFBD><C7BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Կ<EFBFBD><D4BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD>
|
|||
|
|
|
|||
|
|
public struct PlayerCharacterInputs
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
public float MoveAxisForward;
|
|||
|
|
public float MoveAxisRight;
|
|||
|
|
public Quaternion CameraRotation;
|
|||
|
|
public bool JumpHeld;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public class MyCharacterController : MonoBehaviour, ICharacterController
|
|||
|
|
{
|
|||
|
|
public KinematicCharacterMotor Motor;
|
|||
|
|
|
|||
|
|
[Header("Stable Movement")]
|
|||
|
|
[Header("<22><><EFBFBD><EFBFBD><EFBFBD>ƶ<EFBFBD><C6B6>ٶ<EFBFBD>")]
|
|||
|
|
public float MaxStableMoveSpeed = 10f;
|
|||
|
|
public float StableMovementSharpness = 15;
|
|||
|
|
public float OrientationSharpness = 10;
|
|||
|
|
public float MaxStableDistanceFromLedge = 5f;
|
|||
|
|
|
|||
|
|
[Header("Air Movement")]
|
|||
|
|
public float MaxAirMoveSpeed = 10f;
|
|||
|
|
public float AirAccelerationSpeed = 5f;
|
|||
|
|
public float Drag = 0.1f;
|
|||
|
|
|
|||
|
|
[Header("Jumping")]
|
|||
|
|
public bool AllowJumpingWhenSliding = false;
|
|||
|
|
public bool AllowDoubleJump = false;
|
|||
|
|
public bool AllowWallJump = false;
|
|||
|
|
public float JumpSpeed = 10f;
|
|||
|
|
public float JumpPreGroundingGraceTime = 0f;
|
|||
|
|
public float JumpPostGroundingGraceTime = 0f;
|
|||
|
|
|
|||
|
|
|
|||
|
|
[Header("NoClip")]
|
|||
|
|
[Header("<22><><EFBFBD><EFBFBD><EFBFBD>ƶ<EFBFBD><C6B6><EFBFBD><EFBFBD><EFBFBD>")]
|
|||
|
|
public float NoClipMoveSpeed = 10f;
|
|||
|
|
public float NoClipSharpness = 15;
|
|||
|
|
|
|||
|
|
[Header("Misc")]
|
|||
|
|
[Header("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ײ<EFBFBD><D7B2><EFBFBD>б<EFBFBD>")]
|
|||
|
|
public List<Collider> IgnoredColliders = new List<Collider>();
|
|||
|
|
public bool OrientTowardsGravity = false;
|
|||
|
|
public Vector3 Gravity = new Vector3(0, -30f, 0);
|
|||
|
|
public Transform MeshRoot;
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
private Collider[] _probedColliders = new Collider[8];
|
|||
|
|
private Vector3 _moveInputVector;
|
|||
|
|
private Vector3 _lookInputVector;
|
|||
|
|
private bool _jumpInputIsHeld = false;
|
|||
|
|
private bool _crouchInputIsHeld = false;
|
|||
|
|
private bool _jumpRequested = false;
|
|||
|
|
private bool _jumpConsumed = false;
|
|||
|
|
private bool _doubleJumpConsumed = false;
|
|||
|
|
private bool _jumpedThisFrame = false;
|
|||
|
|
private bool _canWallJump = false;
|
|||
|
|
private Vector3 _wallJumpNormal;
|
|||
|
|
private float _timeSinceJumpRequested = Mathf.Infinity;
|
|||
|
|
private float _timeSinceLastAbleToJump = 0f;
|
|||
|
|
private Vector3 _internalVelocityAdd = Vector3.zero;
|
|||
|
|
private bool _shouldBeCrouching = false;
|
|||
|
|
private bool _isCrouching = false;
|
|||
|
|
|
|||
|
|
private void Start()
|
|||
|
|
{
|
|||
|
|
// Assign to motor
|
|||
|
|
Motor.CharacterController = this;
|
|||
|
|
OnStateEnter();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void OnStateEnter() {
|
|||
|
|
|
|||
|
|
|
|||
|
|
Motor.SetGroundSolvingActivation(false);
|
|||
|
|
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
public void OnStateExit() {
|
|||
|
|
|
|||
|
|
//<2F><><EFBFBD>ý<EFBFBD><C3BD>Ҷ<EFBFBD>ײ<EFBFBD><D7B2><EFBFBD>Ƿ<EFBFBD><C7B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ײ
|
|||
|
|
Motor.SetCapsuleCollisionsActivation(true);
|
|||
|
|
//<2F><><EFBFBD>õ<EFBFBD><C3B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƶ<EFBFBD><C6B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƶ<EFBFBD><C6B6><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD>Ƿ<EFBFBD><C7B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ײ<EFBFBD><D7B2><EFBFBD>⡣
|
|||
|
|
Motor.SetMovementCollisionsSolvingActivation(true);
|
|||
|
|
//<2F><><EFBFBD><EFBFBD><EFBFBD>Ƿ<EFBFBD><C7B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӵ<EFBFBD>
|
|||
|
|
Motor.SetGroundSolvingActivation(true);
|
|||
|
|
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// Ϊ<>˸<EFBFBD><CBB8>߽<EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʲô<CAB2><C3B4>MyPlayerÿһ֡<D2BB><D6A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
|
/// </summary>
|
|||
|
|
public void SetInputs(ref PlayerCharacterInputs inputs)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
// Clamp input
|
|||
|
|
Vector3 moveInputVector = Vector3.ClampMagnitude(new Vector3(inputs.MoveAxisRight, 0f, inputs.MoveAxisForward), 1f);
|
|||
|
|
|
|||
|
|
// Calculate camera direction and rotation on the character plane
|
|||
|
|
Vector3 cameraPlanarDirection = Vector3.ProjectOnPlane(inputs.CameraRotation * Vector3.forward, Motor.CharacterUp).normalized;
|
|||
|
|
if (cameraPlanarDirection.sqrMagnitude == 0f)
|
|||
|
|
{
|
|||
|
|
cameraPlanarDirection = Vector3.ProjectOnPlane(inputs.CameraRotation * Vector3.up, Motor.CharacterUp).normalized;
|
|||
|
|
}
|
|||
|
|
Quaternion cameraPlanarRotation = Quaternion.LookRotation(cameraPlanarDirection, Motor.CharacterUp);
|
|||
|
|
|
|||
|
|
// Move and look inputs
|
|||
|
|
_moveInputVector = cameraPlanarRotation * moveInputVector;
|
|||
|
|
_lookInputVector = cameraPlanarDirection;
|
|||
|
|
|
|||
|
|
_moveInputVector = inputs.CameraRotation * moveInputVector;
|
|||
|
|
_lookInputVector = cameraPlanarDirection;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
///<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>KinematicCharacterMotor<6F><72><EFBFBD>ã<EFBFBD>
|
|||
|
|
/// <20><><EFBFBD><EFBFBD><EFBFBD>ڽ<EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD>ƶ<EFBFBD><C6B6><EFBFBD><EFBFBD>º<EFBFBD><C2BA><EFBFBD><EFBFBD>õ<EFBFBD>
|
|||
|
|
/// <param name="deltaTime"></param>
|
|||
|
|
|
|||
|
|
public void AfterCharacterUpdate(float deltaTime)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
// Handle jump-related values
|
|||
|
|
{
|
|||
|
|
// Handle jumping pre-ground grace period
|
|||
|
|
if (_jumpRequested && _timeSinceJumpRequested > JumpPreGroundingGraceTime)
|
|||
|
|
{
|
|||
|
|
_jumpRequested = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (AllowJumpingWhenSliding ? Motor.GroundingStatus.FoundAnyGround : Motor.GroundingStatus.IsStableOnGround)
|
|||
|
|
{
|
|||
|
|
// If we're on a ground surface, reset jumping values
|
|||
|
|
if (!_jumpedThisFrame)
|
|||
|
|
{
|
|||
|
|
_doubleJumpConsumed = false;
|
|||
|
|
_jumpConsumed = false;
|
|||
|
|
}
|
|||
|
|
_timeSinceLastAbleToJump = 0f;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// Keep track of time since we were last able to jump (for grace period)
|
|||
|
|
_timeSinceLastAbleToJump += deltaTime;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Handle uncrouching
|
|||
|
|
if (_isCrouching && !_shouldBeCrouching)
|
|||
|
|
{
|
|||
|
|
// Do an overlap test with the character's standing height to see if there are any obstructions
|
|||
|
|
Motor.SetCapsuleDimensions(0.5f, 2f, 1f);
|
|||
|
|
if (Motor.CharacterCollisionsOverlap(
|
|||
|
|
Motor.TransientPosition,
|
|||
|
|
Motor.TransientRotation,
|
|||
|
|
_probedColliders) > 0)
|
|||
|
|
{
|
|||
|
|
// If obstructions, just stick to crouching dimensions
|
|||
|
|
Motor.SetCapsuleDimensions(0.5f, 1f, 0.5f);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// If no obstructions, uncrouch
|
|||
|
|
MeshRoot.localScale = new Vector3(1f, 1f, 1f);
|
|||
|
|
_isCrouching = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
public void ProcessHitStabilityReport(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, Vector3 atCharacterPosition, Quaternion atCharacterRotation, ref HitStabilityReport hitStabilityReport)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>KinematicCharacterMotor<6F><72><EFBFBD>ã<EFBFBD>
|
|||
|
|
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ľ<EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ת<EFBFBD>ĵط<C4B5><D8B7><EFBFBD>
|
|||
|
|
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ΨһӦ<D2BB><D3A6><EFBFBD><EFBFBD><EFBFBD>ý<EFBFBD>ɫ<EFBFBD><C9AB>ת<EFBFBD>ĵط<C4B5>
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="currentRotation"></param>
|
|||
|
|
/// <param name="deltaTime"></param>
|
|||
|
|
public void UpdateRotation(ref Quaternion currentRotation, float deltaTime)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
if (_lookInputVector != Vector3.zero && OrientationSharpness > 0f)
|
|||
|
|
{
|
|||
|
|
// Smoothly interpolate from current to target look direction
|
|||
|
|
Vector3 smoothedLookInputDirection = Vector3.Slerp(Motor.CharacterForward, _lookInputVector, 1 - Mathf.Exp(-OrientationSharpness * deltaTime)).normalized;
|
|||
|
|
|
|||
|
|
// Set the current rotation (which will be used by the KinematicCharacterMotor)
|
|||
|
|
currentRotation = Quaternion.LookRotation(smoothedLookInputDirection, Motor.CharacterUp);
|
|||
|
|
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
if (OrientTowardsGravity)
|
|||
|
|
{
|
|||
|
|
// Rotate from current up to invert gravity
|
|||
|
|
currentRotation = Quaternion.FromToRotation((currentRotation * Vector3.up), -Gravity) * currentRotation;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>KinematicCharacterMotor<6F><72><EFBFBD>ã<EFBFBD>
|
|||
|
|
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ľ<EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD>ڵ<EFBFBD><DAB5>ٶ<EFBFBD>Ӧ<EFBFBD><D3A6><EFBFBD>Ƕ<EFBFBD><C7B6>ٵĵط<C4B5><D8B7><EFBFBD>
|
|||
|
|
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ψһ<CEA8><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ý<EFBFBD>ɫ<EFBFBD>ٶȵĵط<C4B5>
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="currentVelocity"></param>
|
|||
|
|
/// <param name="deltaTime"></param>
|
|||
|
|
public void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime)
|
|||
|
|
{
|
|||
|
|
Vector3 targetMovementVelocity = Vector3.zero;
|
|||
|
|
if (Motor.GroundingStatus.IsStableOnGround)
|
|||
|
|
{
|
|||
|
|
// Reorient velocity on slope
|
|||
|
|
currentVelocity = Motor.GetDirectionTangentToSurface(currentVelocity, Motor.GroundingStatus.GroundNormal) * currentVelocity.magnitude;
|
|||
|
|
|
|||
|
|
// Calculate target velocity
|
|||
|
|
Vector3 inputRight = Vector3.Cross(_moveInputVector, Motor.CharacterUp);
|
|||
|
|
Vector3 reorientedInput = Vector3.Cross(Motor.GroundingStatus.GroundNormal, inputRight).normalized * _moveInputVector.magnitude;
|
|||
|
|
targetMovementVelocity = reorientedInput * MaxStableMoveSpeed;
|
|||
|
|
|
|||
|
|
// Smooth movement Velocity
|
|||
|
|
currentVelocity = Vector3.Lerp(currentVelocity, targetMovementVelocity, 1 - Mathf.Exp(-StableMovementSharpness * deltaTime));
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// Add move input
|
|||
|
|
if (_moveInputVector.sqrMagnitude > 0f)
|
|||
|
|
{
|
|||
|
|
targetMovementVelocity = _moveInputVector * MaxAirMoveSpeed;
|
|||
|
|
|
|||
|
|
// Prevent climbing on un-stable slopes with air movement
|
|||
|
|
if (Motor.GroundingStatus.FoundAnyGround)
|
|||
|
|
{
|
|||
|
|
Vector3 perpenticularObstructionNormal = Vector3.Cross(Vector3.Cross(Motor.CharacterUp, Motor.GroundingStatus.GroundNormal), Motor.CharacterUp).normalized;
|
|||
|
|
targetMovementVelocity = Vector3.ProjectOnPlane(targetMovementVelocity, perpenticularObstructionNormal);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Vector3 velocityDiff = Vector3.ProjectOnPlane(targetMovementVelocity - currentVelocity, Gravity);
|
|||
|
|
currentVelocity += velocityDiff * AirAccelerationSpeed * deltaTime;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Gravity
|
|||
|
|
currentVelocity += Gravity * deltaTime;
|
|||
|
|
|
|||
|
|
// Drag
|
|||
|
|
currentVelocity *= (1f / (1f + (Drag * deltaTime)));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Handle jumping
|
|||
|
|
{
|
|||
|
|
_jumpedThisFrame = false;
|
|||
|
|
_timeSinceJumpRequested += deltaTime;
|
|||
|
|
if (_jumpRequested)
|
|||
|
|
{
|
|||
|
|
// Handle double jump
|
|||
|
|
if (AllowDoubleJump)
|
|||
|
|
{
|
|||
|
|
if (_jumpConsumed && !_doubleJumpConsumed && (AllowJumpingWhenSliding ? !Motor.GroundingStatus.FoundAnyGround : !Motor.GroundingStatus.IsStableOnGround))
|
|||
|
|
{
|
|||
|
|
Motor.ForceUnground(0.1f);
|
|||
|
|
|
|||
|
|
// Add to the return velocity and reset jump state
|
|||
|
|
currentVelocity += (Motor.CharacterUp * JumpSpeed) - Vector3.Project(currentVelocity, Motor.CharacterUp);
|
|||
|
|
_jumpRequested = false;
|
|||
|
|
_doubleJumpConsumed = true;
|
|||
|
|
_jumpedThisFrame = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// See if we actually are allowed to jump
|
|||
|
|
if (_canWallJump ||
|
|||
|
|
(!_jumpConsumed && ((AllowJumpingWhenSliding ? Motor.GroundingStatus.FoundAnyGround : Motor.GroundingStatus.IsStableOnGround) || _timeSinceLastAbleToJump <= JumpPostGroundingGraceTime)))
|
|||
|
|
{
|
|||
|
|
// Calculate jump direction before ungrounding
|
|||
|
|
Vector3 jumpDirection = Motor.CharacterUp;
|
|||
|
|
if (_canWallJump)
|
|||
|
|
{
|
|||
|
|
jumpDirection = _wallJumpNormal;
|
|||
|
|
}
|
|||
|
|
else if (Motor.GroundingStatus.FoundAnyGround && !Motor.GroundingStatus.IsStableOnGround)
|
|||
|
|
{
|
|||
|
|
jumpDirection = Motor.GroundingStatus.GroundNormal;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Makes the character skip ground probing/snapping on its next update.
|
|||
|
|
// If this line weren't here, the character would remain snapped to the ground when trying to jump. Try commenting this line out and see.
|
|||
|
|
Motor.ForceUnground(0.1f);
|
|||
|
|
|
|||
|
|
// Add to the return velocity and reset jump state
|
|||
|
|
currentVelocity += (jumpDirection * JumpSpeed) - Vector3.Project(currentVelocity, Motor.CharacterUp);
|
|||
|
|
_jumpRequested = false;
|
|||
|
|
_jumpConsumed = true;
|
|||
|
|
_jumpedThisFrame = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Reset wall jump
|
|||
|
|
_canWallJump = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Take into account additive velocity
|
|||
|
|
if (_internalVelocityAdd.sqrMagnitude > 0f)
|
|||
|
|
{
|
|||
|
|
currentVelocity += _internalVelocityAdd;
|
|||
|
|
_internalVelocityAdd = Vector3.zero;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
public void AddVelocity(Vector3 velocity)
|
|||
|
|
{
|
|||
|
|
_internalVelocityAdd += velocity;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void BeforeCharacterUpdate(float deltaTime)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public bool IsColliderValidForCollisions(Collider coll)
|
|||
|
|
{
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void OnDiscreteCollisionDetected(Collider hitCollider)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void OnGroundHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, ref HitStabilityReport hitStabilityReport)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void OnMovementHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, ref HitStabilityReport hitStabilityReport)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void PostGroundingUpdate(float deltaTime)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|