556 lines
23 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;
using System;
namespace KinematicCharacterController.Walkthrough.NoClipState
{
public enum CharacterState
{
Default,
NoClip,
}
public struct PlayerCharacterInputs
{
public float MoveAxisForward;
public float MoveAxisRight;
public Quaternion CameraRotation;
public bool JumpDown;
public bool JumpHeld;
public bool CrouchDown;
public bool CrouchUp;
public bool CrouchHeld;
public bool NoClipDown;
}
public class MyCharacterController : MonoBehaviour, ICharacterController
{
public KinematicCharacterMotor Motor;
[Header("Stable Movement")]
public float MaxStableMoveSpeed = 10f;
public float StableMovementSharpness = 15;
public float OrientationSharpness = 10;
public float MaxStableDistanceFromLedge = 5f;
[Range(0f, 180f)]
public float MaxStableDenivelationAngle = 180f;
[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")]
public float NoClipMoveSpeed = 10f;
public float NoClipSharpness = 15;
[Header("Misc")]
public List<Collider> IgnoredColliders = new List<Collider>();
public bool OrientTowardsGravity = false;
public Vector3 Gravity = new Vector3(0, -30f, 0);
public Transform MeshRoot;
public CharacterState CurrentCharacterState { get; private set; }
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;
//public float _rotSpeed = 5.12f;// 256.0f;
//public float _RotSpeed { get => _rotSpeed * ZXK.GYJQR.GameManager.Instance._CurMouseFlexible; }
//public float _moveSpeed = 0.06f;// 3.0f;
//private float _MoveSpeed { get => _moveSpeed /** ZXK.GYJQR.GameManager.Instance._CurMouseFlexible*/; }
private void Start()
{
// Assign to motor
Motor.CharacterController = this;
// Handle initial state
TransitionToState(CharacterState.Default);
}
/// <summary>
/// Handles movement state transitions and enter/exit callbacks
/// </summary>
public void TransitionToState(CharacterState newState)
{
CharacterState tmpInitialState = CurrentCharacterState;
OnStateExit(tmpInitialState, newState);
CurrentCharacterState = newState;
OnStateEnter(newState, tmpInitialState);
}
/// <summary>
/// Event when entering a state
/// </summary>
public void OnStateEnter(CharacterState state, CharacterState fromState)
{
switch (state)
{
case CharacterState.Default:
{
break;
}
case CharacterState.NoClip:
{
//<2F><>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ײ<EFBFBD><D7B2><EFBFBD>Ҳ<EFBFBD><D2B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
//Motor.SetCapsuleCollisionsActivation(false);
// Motor.SetMovementCollisionsSolvingActivation(false);
Motor.SetGroundSolvingActivation(false);
break;
}
}
}
/// <summary>
/// Event when exiting a state
/// </summary>
public void OnStateExit(CharacterState state, CharacterState toState)
{
switch (state)
{
case CharacterState.Default:
{
break;
}
case CharacterState.NoClip:
{
Motor.SetCapsuleCollisionsActivation(true);
Motor.SetMovementCollisionsSolvingActivation(true);
Motor.SetGroundSolvingActivation(true);
break;
}
}
}
/// <summary>
/// This is called every frame by MyPlayer in order to tell the character what its inputs are
/// </summary>
public void SetInputs(ref PlayerCharacterInputs inputs)
{
//<2F><>Զ<EFBFBD><D4B6>NoClip״̬
TransitionToState(CharacterState.NoClip);
_jumpInputIsHeld = inputs.JumpHeld;
_crouchInputIsHeld = inputs.CrouchHeld;
Vector3 moveInputVector = GetInputTranslationDirection();
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);
switch (CurrentCharacterState)
{
case CharacterState.Default:
{
// Move and look inputs
_moveInputVector = cameraPlanarRotation * moveInputVector;
_lookInputVector = cameraPlanarDirection;
// Jumping input
if (inputs.JumpDown)
{
_timeSinceJumpRequested = 0f;
_jumpRequested = true;
}
// Crouching input
if (inputs.CrouchDown)
{
_shouldBeCrouching = true;
if (!_isCrouching)
{
_isCrouching = true;
Motor.SetCapsuleDimensions(0.5f, 1f, 0.5f);
MeshRoot.localScale = new Vector3(1f, 0.5f, 1f);
}
}
else if (inputs.CrouchUp)
{
_shouldBeCrouching = false;
}
break;
}
//<2F><>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģʽ
case CharacterState.NoClip:
{
_moveInputVector = moveInputVector;
if (_moveInputVector.magnitude != 0)
{
}
_lookInputVector = cameraPlanarDirection;
break;
}
}
}
Vector3 GetInputTranslationDirection()
{
Vector3 direction = new Vector3();
if (Input.GetKey(KeyCode.W))
{
Vector3 forward = new Vector3(transform.forward.x, 0, transform.forward.z);
direction += forward.normalized;
}
if (Input.GetKey(KeyCode.S))
{
Vector3 forward = new Vector3(transform.forward.x, 0, transform.forward.z);
direction -= forward.normalized;
}
if (Input.GetKey(KeyCode.A))
{
Vector3 right = new Vector3(transform.right.x, 0, transform.right.z);
direction -= right;
}
if (Input.GetKey(KeyCode.D))
{
Vector3 right = new Vector3(transform.right.x, 0, transform.right.z);
direction += right;
}
if (Input.GetKey(KeyCode.Q))//transform.position.y < _maxPos.y &&
{
direction += Vector3.up;
}
if (Input.GetKey(KeyCode.E))//transform.position.y > _minPos.y &&
{
direction += Vector3.down;
}
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֣<EFBFBD><D6A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƶ<EFBFBD><C6B6><EFBFBD><EFBFBD><EFBFBD>
//if (/*CG.UTility.PopUpMng._TriAble*/)
// return direction;
//else
// return Vector3.zero;
//<2F>κ<EFBFBD><CEBA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƶ<EFBFBD><C6B6><EFBFBD><EFBFBD><EFBFBD>
return direction;
}
int index;
/// <summary>
/// (Called by KinematicCharacterMotor during its update cycle)
/// This is called before the character begins its movement update
/// </summary>
public void BeforeCharacterUpdate(float deltaTime)
{
switch (CurrentCharacterState)
{
case CharacterState.Default:
{
break;
}
}
}
/// <summary>
/// (Called by KinematicCharacterMotor during its update cycle)
/// This is where you tell your character what its rotation should be right now.
/// This is the ONLY place where you should set the character's rotation
/// </summary>
public void UpdateRotation(ref Quaternion currentRotation, float deltaTime)
{
switch (CurrentCharacterState)
{
case CharacterState.Default:
case CharacterState.NoClip:
{
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;
}
break;
}
}
}
/// <summary>
/// (Called by KinematicCharacterMotor during its update cycle)
/// This is where you tell your character what its velocity should be right now.
/// This is the ONLY place where you can set the character's velocity
/// </summary>
public void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime)
{
switch (CurrentCharacterState)
{
case CharacterState.Default:
{
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;
}
break;
}
case CharacterState.NoClip:
{
float verticalInput = 0f + (_jumpInputIsHeld ? 1f : 0f) + (_crouchInputIsHeld ? -1f : 0f);
// Smoothly interpolate to target velocity
Vector3 targetMovementVelocity = (_moveInputVector + (Motor.CharacterUp * verticalInput)).normalized * NoClipMoveSpeed;
currentVelocity = Vector3.Lerp(currentVelocity, targetMovementVelocity, 1 - Mathf.Exp(-NoClipSharpness * deltaTime));
break;
}
}
}
/// <summary>
/// (Called by KinematicCharacterMotor during its update cycle)
/// This is called after the character has finished its movement update
/// </summary>
public void AfterCharacterUpdate(float deltaTime)
{
switch (CurrentCharacterState)
{
case CharacterState.Default:
{
// 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.CharacterOverlap(
Motor.TransientPosition,
Motor.TransientRotation,
_probedColliders,
Motor.CollidableLayers,
QueryTriggerInteraction.Ignore) > 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;
}
}
break;
}
}
}
public bool IsColliderValidForCollisions(Collider coll)
{
if (IgnoredColliders.Contains(coll))
{
return false;
}
return true;
}
public void OnGroundHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, ref HitStabilityReport hitStabilityReport)
{
}
public void OnMovementHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, ref HitStabilityReport hitStabilityReport)
{
switch (CurrentCharacterState)
{
case CharacterState.Default:
{
// We can wall jump only if we are not stable on ground and are moving against an obstruction
if (AllowWallJump && !Motor.GroundingStatus.IsStableOnGround && !hitStabilityReport.IsStable)
{
_canWallJump = true;
_wallJumpNormal = hitNormal;
}
break;
}
}
}
public void AddVelocity(Vector3 velocity)
{
switch (CurrentCharacterState)
{
case CharacterState.Default:
{
_internalVelocityAdd += velocity;
break;
}
}
}
public void ProcessHitStabilityReport(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, Vector3 atCharacterPosition, Quaternion atCharacterRotation, ref HitStabilityReport hitStabilityReport)
{
}
public void PostGroundingUpdate(float deltaTime)
{
}
public void OnDiscreteCollisionDetected(Collider hitCollider)
{
}
}
}