Files
Ultimate-Fishing-Simulator-…/Assets/Scripts/Assembly-CSharp/RootMotion/Demos/CharacterThirdPerson.cs
2026-03-04 09:37:33 +08:00

529 lines
13 KiB
C#

using System;
using System.Collections;
using UnityEngine;
namespace RootMotion.Demos
{
public class CharacterThirdPerson : CharacterBase
{
[Serializable]
public enum MoveMode
{
Directional = 0,
Strafe = 1
}
public struct AnimState
{
public Vector3 moveDirection;
public bool jump;
public bool crouch;
public bool onGround;
public bool isStrafing;
public float yVelocity;
public bool doubleJump;
}
[Header("References")]
public CharacterAnimationBase characterAnimation;
public UserControlThirdPerson userControl;
public CameraController cam;
[Header("Movement")]
public MoveMode moveMode;
public bool smoothPhysics = true;
public float smoothAccelerationTime = 0.2f;
public float linearAccelerationSpeed = 3f;
public float platformFriction = 7f;
public float groundStickyEffect = 4f;
public float maxVerticalVelocityOnGround = 3f;
public float velocityToGroundTangentWeight;
[Header("Rotation")]
public bool lookInCameraDirection;
public float turnSpeed = 5f;
public float stationaryTurnSpeedMlp = 1f;
[Header("Jumping and Falling")]
public bool smoothJump = true;
public float airSpeed = 6f;
public float airControl = 2f;
public float jumpPower = 12f;
public float jumpRepeatDelayTime;
public bool doubleJumpEnabled;
public float doubleJumpPowerMlp = 1f;
[Header("Wall Running")]
public LayerMask wallRunLayers;
public float wallRunMaxLength = 1f;
public float wallRunMinMoveMag = 0.6f;
public float wallRunMinVelocityY = -1f;
public float wallRunRotationSpeed = 1.5f;
public float wallRunMaxRotationAngle = 70f;
public float wallRunWeightSpeed = 5f;
[Header("Crouching")]
public float crouchCapsuleScaleMlp = 0.6f;
public AnimState animState;
protected Vector3 moveDirection;
private Animator animator;
private Vector3 normal;
private Vector3 platformVelocity;
private Vector3 platformAngularVelocity;
private RaycastHit hit;
private float jumpLeg;
private float jumpEndTime;
private float forwardMlp;
private float groundDistance;
private float lastAirTime;
private float stickyForce;
private Vector3 wallNormal = Vector3.up;
private Vector3 moveDirectionVelocity;
private float wallRunWeight;
private float lastWallRunWeight;
private float fixedDeltaTime;
private Vector3 fixedDeltaPosition;
private Quaternion fixedDeltaRotation = Quaternion.identity;
private bool fixedFrame;
private float wallRunEndTime;
private Vector3 gravity;
private Vector3 verticalVelocity;
private float velocityY;
private bool doubleJumped;
private bool jumpReleased;
public bool fullRootMotion { get; set; }
public bool onGround { get; private set; }
protected override void Start()
{
base.Start();
animator = GetComponent<Animator>();
if (animator == null)
{
animator = characterAnimation.GetComponent<Animator>();
}
wallNormal = -gravity.normalized;
onGround = true;
animState.onGround = true;
if (cam != null)
{
cam.enabled = false;
}
}
private void OnAnimatorMove()
{
Move(animator.deltaPosition, animator.deltaRotation);
}
public override void Move(Vector3 deltaPosition, Quaternion deltaRotation)
{
fixedDeltaTime += Time.deltaTime;
fixedDeltaPosition += deltaPosition;
fixedDeltaRotation *= deltaRotation;
}
private void FixedUpdate()
{
gravity = (fullRootMotion ? Vector3.zero : GetGravity());
verticalVelocity = V3Tools.ExtractVertical(r.linearVelocity, gravity, 1f);
velocityY = verticalVelocity.magnitude;
if (Vector3.Dot(verticalVelocity, gravity) > 0f)
{
velocityY = 0f - velocityY;
}
r.interpolation = (smoothPhysics ? RigidbodyInterpolation.Interpolate : RigidbodyInterpolation.None);
characterAnimation.smoothFollow = smoothPhysics;
MoveFixed(fixedDeltaPosition);
fixedDeltaTime = 0f;
fixedDeltaPosition = Vector3.zero;
r.MoveRotation(base.transform.rotation * fixedDeltaRotation);
fixedDeltaRotation = Quaternion.identity;
Rotate();
GroundCheck();
if (userControl.state.move == Vector3.zero && groundDistance < airborneThreshold * 0.5f)
{
HighFriction();
}
else
{
ZeroFriction();
}
bool flag = !fullRootMotion && onGround && userControl.state.move == Vector3.zero && r.linearVelocity.magnitude < 0.5f && groundDistance < airborneThreshold * 0.5f;
if (gravityTarget != null)
{
r.useGravity = false;
if (!flag)
{
r.AddForce(gravity);
}
}
if (flag)
{
r.useGravity = false;
r.linearVelocity = Vector3.zero;
}
else if (gravityTarget == null)
{
r.useGravity = true;
}
if (onGround)
{
animState.jump = Jump();
jumpReleased = false;
doubleJumped = false;
}
else
{
if (!userControl.state.jump)
{
jumpReleased = true;
}
if (jumpReleased && userControl.state.jump && !doubleJumped && doubleJumpEnabled)
{
jumpEndTime = Time.time + 0.1f;
animState.doubleJump = true;
Vector3 linearVelocity = userControl.state.move * airSpeed;
r.linearVelocity = linearVelocity;
r.linearVelocity += base.transform.up * jumpPower * doubleJumpPowerMlp;
doubleJumped = true;
}
}
ScaleCapsule(userControl.state.crouch ? crouchCapsuleScaleMlp : 1f);
fixedFrame = true;
}
protected virtual void Update()
{
animState.onGround = onGround;
animState.moveDirection = GetMoveDirection();
animState.yVelocity = Mathf.Lerp(animState.yVelocity, velocityY, Time.deltaTime * 10f);
animState.crouch = userControl.state.crouch;
animState.isStrafing = moveMode == MoveMode.Strafe;
}
protected virtual void LateUpdate()
{
if (!(cam == null))
{
cam.UpdateInput();
if (fixedFrame || r.interpolation != RigidbodyInterpolation.None)
{
cam.UpdateTransform((r.interpolation == RigidbodyInterpolation.None) ? Time.fixedDeltaTime : Time.deltaTime);
fixedFrame = false;
}
}
}
private void MoveFixed(Vector3 deltaPosition)
{
WallRun();
Vector3 vector = ((fixedDeltaTime > 0f) ? (deltaPosition / fixedDeltaTime) : Vector3.zero);
if (!fullRootMotion)
{
vector += V3Tools.ExtractHorizontal(platformVelocity, gravity, 1f);
if (onGround)
{
if (velocityToGroundTangentWeight > 0f)
{
Quaternion b = Quaternion.FromToRotation(base.transform.up, normal);
vector = Quaternion.Lerp(Quaternion.identity, b, velocityToGroundTangentWeight) * vector;
}
}
else
{
Vector3 b2 = V3Tools.ExtractHorizontal(userControl.state.move * airSpeed, gravity, 1f);
vector = Vector3.Lerp(r.linearVelocity, b2, Time.deltaTime * airControl);
}
if (onGround && Time.time > jumpEndTime)
{
r.linearVelocity -= base.transform.up * stickyForce * Time.deltaTime;
}
Vector3 vector2 = V3Tools.ExtractVertical(r.linearVelocity, gravity, 1f);
Vector3 vector3 = V3Tools.ExtractHorizontal(vector, gravity, 1f);
if (onGround && Vector3.Dot(vector2, gravity) < 0f)
{
vector2 = Vector3.ClampMagnitude(vector2, maxVerticalVelocityOnGround);
}
r.linearVelocity = vector3 + vector2;
}
else
{
r.linearVelocity = vector;
}
forwardMlp = 1f;
}
private void WallRun()
{
bool flag = CanWallRun();
if (wallRunWeight > 0f && !flag)
{
wallRunEndTime = Time.time;
}
if (Time.time < wallRunEndTime + 0.5f)
{
flag = false;
}
wallRunWeight = Mathf.MoveTowards(wallRunWeight, flag ? 1f : 0f, Time.deltaTime * wallRunWeightSpeed);
if (wallRunWeight <= 0f && lastWallRunWeight > 0f)
{
Vector3 forward = V3Tools.ExtractHorizontal(base.transform.forward, gravity, 1f);
base.transform.rotation = Quaternion.LookRotation(forward, -gravity);
wallNormal = -gravity.normalized;
}
lastWallRunWeight = wallRunWeight;
if (!(wallRunWeight <= 0f))
{
if (onGround && velocityY < 0f)
{
r.linearVelocity = V3Tools.ExtractHorizontal(r.linearVelocity, gravity, 1f);
}
Vector3 vector = V3Tools.ExtractHorizontal(base.transform.forward, gravity, 1f);
RaycastHit hitInfo = new RaycastHit
{
normal = -gravity.normalized
};
Physics.Raycast(onGround ? base.transform.position : capsule.bounds.center, vector, out hitInfo, 3f, wallRunLayers);
wallNormal = Vector3.Lerp(wallNormal, hitInfo.normal, Time.deltaTime * wallRunRotationSpeed);
wallNormal = Vector3.RotateTowards(-gravity.normalized, wallNormal, wallRunMaxRotationAngle * (MathF.PI / 180f), 0f);
Vector3 tangent = base.transform.forward;
Vector3 vector2 = wallNormal;
Vector3.OrthoNormalize(ref vector2, ref tangent);
base.transform.rotation = Quaternion.Slerp(Quaternion.LookRotation(vector, -gravity), Quaternion.LookRotation(tangent, wallNormal), wallRunWeight);
}
}
private bool CanWallRun()
{
if (fullRootMotion)
{
return false;
}
if (Time.time < jumpEndTime - 0.1f)
{
return false;
}
if (Time.time > jumpEndTime - 0.1f + wallRunMaxLength)
{
return false;
}
if (velocityY < wallRunMinVelocityY)
{
return false;
}
if (userControl.state.move.magnitude < wallRunMinMoveMag)
{
return false;
}
return true;
}
private Vector3 GetMoveDirection()
{
switch (moveMode)
{
case MoveMode.Directional:
moveDirection = Vector3.SmoothDamp(moveDirection, new Vector3(0f, 0f, userControl.state.move.magnitude), ref moveDirectionVelocity, smoothAccelerationTime);
moveDirection = Vector3.MoveTowards(moveDirection, new Vector3(0f, 0f, userControl.state.move.magnitude), Time.deltaTime * linearAccelerationSpeed);
return moveDirection * forwardMlp;
case MoveMode.Strafe:
moveDirection = Vector3.SmoothDamp(moveDirection, userControl.state.move, ref moveDirectionVelocity, smoothAccelerationTime);
moveDirection = Vector3.MoveTowards(moveDirection, userControl.state.move, Time.deltaTime * linearAccelerationSpeed);
return base.transform.InverseTransformDirection(moveDirection);
default:
return Vector3.zero;
}
}
protected virtual void Rotate()
{
if (gravityTarget != null)
{
r.MoveRotation(Quaternion.FromToRotation(base.transform.up, base.transform.position - gravityTarget.position) * base.transform.rotation);
}
if (platformAngularVelocity != Vector3.zero)
{
r.MoveRotation(Quaternion.Euler(platformAngularVelocity) * base.transform.rotation);
}
float num = GetAngleFromForward(GetForwardDirection());
if (userControl.state.move == Vector3.zero)
{
num *= (1.01f - Mathf.Abs(num) / 180f) * stationaryTurnSpeedMlp;
}
r.MoveRotation(Quaternion.AngleAxis(num * Time.deltaTime * turnSpeed, base.transform.up) * r.rotation);
}
private Vector3 GetForwardDirection()
{
bool flag = userControl.state.move != Vector3.zero;
switch (moveMode)
{
case MoveMode.Directional:
if (flag)
{
return userControl.state.move;
}
if (!lookInCameraDirection)
{
return base.transform.forward;
}
return userControl.state.lookPos - r.position;
case MoveMode.Strafe:
if (flag)
{
return userControl.state.lookPos - r.position;
}
if (!lookInCameraDirection)
{
return base.transform.forward;
}
return userControl.state.lookPos - r.position;
default:
return Vector3.zero;
}
}
protected virtual bool Jump()
{
if (!userControl.state.jump)
{
return false;
}
if (userControl.state.crouch)
{
return false;
}
if (!characterAnimation.animationGrounded)
{
return false;
}
if (Time.time < lastAirTime + jumpRepeatDelayTime)
{
return false;
}
onGround = false;
jumpEndTime = Time.time + 0.1f;
Vector3 vector = userControl.state.move * airSpeed;
vector += base.transform.up * jumpPower;
if (smoothJump)
{
StopAllCoroutines();
StartCoroutine(JumpSmooth(vector - r.linearVelocity));
}
else
{
r.linearVelocity = vector;
}
return true;
}
private IEnumerator JumpSmooth(Vector3 jumpVelocity)
{
int steps = 0;
int stepsToTake = 3;
while (steps < stepsToTake)
{
r.AddForce(jumpVelocity / stepsToTake, ForceMode.VelocityChange);
steps++;
yield return new WaitForFixedUpdate();
}
}
private void GroundCheck()
{
Vector3 b = Vector3.zero;
platformAngularVelocity = Vector3.zero;
float num = 0f;
hit = GetSpherecastHit();
normal = base.transform.up;
groundDistance = Vector3.Project(r.position - hit.point, base.transform.up).magnitude;
if (Time.time > jumpEndTime && velocityY < jumpPower * 0.5f)
{
bool num2 = onGround;
onGround = false;
float num3 = ((!num2) ? (airborneThreshold * 0.5f) : airborneThreshold);
float magnitude = V3Tools.ExtractHorizontal(r.linearVelocity, gravity, 1f).magnitude;
if (groundDistance < num3)
{
num = groundStickyEffect * magnitude * num3;
if (hit.rigidbody != null)
{
b = hit.rigidbody.GetPointVelocity(hit.point);
platformAngularVelocity = Vector3.Project(hit.rigidbody.angularVelocity, base.transform.up);
}
onGround = true;
}
}
platformVelocity = Vector3.Lerp(platformVelocity, b, Time.deltaTime * platformFriction);
if (fullRootMotion)
{
stickyForce = 0f;
}
stickyForce = num;
if (!onGround)
{
lastAirTime = Time.time;
}
}
}
}