361 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using KinematicCharacterController;
namespace CustomUse
{
//默认QE是控制人性控制器高度
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("最大移动速度")]
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("悬空移动属性")]
public float NoClipMoveSpeed = 10f;
public float NoClipSharpness = 15;
[Header("Misc")]
[Header("忽略碰撞体列表")]
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() {
//设置胶囊对撞机是否检测碰撞
Motor.SetCapsuleCollisionsActivation(true);
//设置电机在移动(或移动到)时是否解决碰撞问题。
Motor.SetMovementCollisionsSolvingActivation(true);
//设置是否对所有命中评估接地
Motor.SetGroundSolvingActivation(true);
}
/// <summary>
/// 为了告诉角色它的输入是什么MyPlayer每一帧都会调用这个参数
/// </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;
}
///在其更新周期中由KinematicCharacterMotor调用
/// 这是在角色完成移动更新后调用的
/// <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>
/// 在其更新周期中由KinematicCharacterMotor调用
/// 这是你告诉你的角色现在应该如何旋转的地方。
/// 这是你唯一应该设置角色旋转的地方
/// </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>
/// 在其更新周期中由KinematicCharacterMotor调用
/// 这是你告诉你的角色现在的速度应该是多少的地方。
/// 这是你唯一可以设置角色速度的地方
/// </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)
{
}
}
}