600 lines
25 KiB
C#
600 lines
25 KiB
C#
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;
|
||
public 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)
|
||
{
|
||
// Handle state transition from input
|
||
//if (inputs.NoClipDown)
|
||
//{
|
||
|
||
// //if (CurrentCharacterState == CharacterState.Default)
|
||
// //{
|
||
// // TransitionToState(CharacterState.NoClip);
|
||
// //}
|
||
// //else if (CurrentCharacterState == CharacterState.NoClip)
|
||
// //{
|
||
// // TransitionToState(CharacterState.Default);
|
||
// //}
|
||
//}
|
||
//<2F><>Զ<EFBFBD><D4B6>NoClip״̬
|
||
TransitionToState(CharacterState.NoClip);
|
||
|
||
_jumpInputIsHeld = inputs.JumpHeld;
|
||
_crouchInputIsHeld = inputs.CrouchHeld;
|
||
|
||
// Clamp input
|
||
// Vector3 moveInputVector = Vector3.ClampMagnitude(new Vector3(inputs.MoveAxisRight, 0f, inputs.MoveAxisForward), 1f);
|
||
Vector3 moveInputVector = GetInputTranslationDirection();
|
||
|
||
/*
|
||
float mouse_x = Input.GetAxis("Mouse X");
|
||
float mouse_y = Input.GetAxis("Mouse Y");
|
||
if (Input.GetKey(KeyCode.Mouse1))
|
||
{
|
||
//posX = mouse_x * _MoveSpeed * Time.deltaTime;
|
||
//posY = mouse_y * _MoveSpeed * Time.deltaTime;
|
||
|
||
|
||
transform.Translate(new Vector3(-posX, -posY, 0), Space.Self);
|
||
distance = (transform.position - _CenObj.position).magnitude;
|
||
}
|
||
*/
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
// 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);
|
||
|
||
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;
|
||
}
|
||
case CharacterState.NoClip:
|
||
{
|
||
// _moveInputVector = inputs.CameraRotation * moveInputVector;
|
||
//if (Mathf.Abs(transform.rotation.y) %180<180)
|
||
//{
|
||
// _moveInputVector = moveInputVector;
|
||
//}
|
||
//else
|
||
//{
|
||
// _moveInputVector = -moveInputVector;
|
||
//}
|
||
_moveInputVector = moveInputVector;
|
||
|
||
if (_moveInputVector.magnitude != 0)
|
||
{//<2F><><EFBFBD><EFBFBD>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ť<EFBFBD><C5A5><EFBFBD><EFBFBD><EFBFBD>仯
|
||
ZXK.LouDiXvMuNiu.OperationStepPanel stepPanel = CG.Framework.UI_Manage.Instance.GetPanel("OperationStepPanel").GetComponent<ZXK.LouDiXvMuNiu.OperationStepPanel>();
|
||
stepPanel.SetCameraTRTogFalse();
|
||
}
|
||
_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;
|
||
}
|
||
if (CG.UTility.PopUpMng._TriAble)
|
||
return direction;
|
||
else
|
||
return Vector3.zero;
|
||
}
|
||
int index;
|
||
//private void Update()
|
||
//{
|
||
// if (Mathf.Abs(transform.rotation.y) % 180 < 180)
|
||
// {
|
||
// index = 1;
|
||
// }
|
||
// else
|
||
// {
|
||
// index = -1;
|
||
// }
|
||
|
||
//}
|
||
|
||
/// <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)
|
||
{
|
||
}
|
||
}
|
||
} |