mirror of
https://github.com/ApfelTeeSaft/Slender.git
synced 2026-08-27 03:43:28 +00:00
391 lines
10 KiB
C#
391 lines
10 KiB
C#
using System;
|
|
// using System.Collections.Generic;
|
|
// using Boo.Lang;
|
|
using UnityEngine;
|
|
|
|
[Serializable]
|
|
[RequireComponent(typeof(CharacterController))]
|
|
public class ThirdPersonController : MonoBehaviour
|
|
{
|
|
public AnimationClip idleAnimation;
|
|
public AnimationClip walkAnimation;
|
|
public AnimationClip runAnimation;
|
|
public AnimationClip jumpPoseAnimation;
|
|
|
|
public float walkMaxAnimationSpeed = 0.75f;
|
|
public float trotMaxAnimationSpeed = 1f;
|
|
public float runMaxAnimationSpeed = 1f;
|
|
public float jumpAnimationSpeed = 1.15f;
|
|
public float landAnimationSpeed = 1f;
|
|
|
|
private Animation _animation;
|
|
|
|
private CharacterState _characterState;
|
|
|
|
public float walkSpeed = 2f;
|
|
public float trotSpeed = 4f;
|
|
public float runSpeed = 6f;
|
|
public float inAirControlAcceleration = 3f;
|
|
public float jumpHeight = 0.5f;
|
|
public float gravity = 20f;
|
|
public float speedSmoothing = 10f;
|
|
public float rotateSpeed = 500f;
|
|
public float trotAfterSeconds = 3f;
|
|
public bool canJump = true;
|
|
|
|
private float jumpRepeatTime = 0.05f;
|
|
private float jumpTimeout = 0.15f;
|
|
private float groundedTimeout = 0.25f;
|
|
|
|
private float lockCameraTimer = 0f;
|
|
|
|
private Vector3 moveDirection = Vector3.zero;
|
|
private float verticalSpeed = 0f;
|
|
private float moveSpeed = 0f;
|
|
|
|
private CollisionFlags collisionFlags;
|
|
|
|
private bool jumping = false;
|
|
private bool jumpingReachedApex = false;
|
|
private bool movingBack = false;
|
|
private bool isMoving = false;
|
|
|
|
private float walkTimeStart = 0f;
|
|
private float lastJumpButtonTime = -10f;
|
|
private float lastJumpTime = -1f;
|
|
private float lastJumpStartHeight = 0f;
|
|
|
|
private Vector3 inAirVelocity = Vector3.zero;
|
|
private float lastGroundedTime = 0f;
|
|
|
|
private bool isControllable = true;
|
|
|
|
void Awake()
|
|
{
|
|
moveDirection = transform.TransformDirection(Vector3.forward);
|
|
|
|
_animation = GetComponent<Animation>();
|
|
|
|
if (_animation == null)
|
|
{
|
|
Debug.LogWarning("No Animation component found.");
|
|
return;
|
|
}
|
|
|
|
if (!idleAnimation)
|
|
{
|
|
Debug.LogWarning("No idle animation found.");
|
|
_animation = null;
|
|
}
|
|
|
|
if (!walkAnimation)
|
|
{
|
|
Debug.LogWarning("No walk animation found.");
|
|
_animation = null;
|
|
}
|
|
|
|
if (!runAnimation)
|
|
{
|
|
Debug.LogWarning("No run animation found.");
|
|
_animation = null;
|
|
}
|
|
|
|
if (!jumpPoseAnimation && canJump)
|
|
{
|
|
Debug.LogWarning("No jump animation found.");
|
|
_animation = null;
|
|
}
|
|
}
|
|
|
|
void UpdateSmoothedMovementDirection()
|
|
{
|
|
Transform cameraTransform = Camera.main.transform;
|
|
|
|
bool grounded = IsGrounded();
|
|
|
|
Vector3 forward = cameraTransform.TransformDirection(Vector3.forward);
|
|
forward.y = 0;
|
|
forward.Normalize();
|
|
|
|
Vector3 right = new Vector3(forward.z, 0, -forward.x);
|
|
|
|
float v = Input.GetAxisRaw("Vertical");
|
|
float h = Input.GetAxisRaw("Horizontal");
|
|
|
|
movingBack = v < -0.2f;
|
|
|
|
bool wasMoving = isMoving;
|
|
|
|
isMoving = Mathf.Abs(h) > 0.1f || Mathf.Abs(v) > 0.1f;
|
|
|
|
Vector3 targetDirection = h * right + v * forward;
|
|
|
|
if (grounded)
|
|
{
|
|
lockCameraTimer += Time.deltaTime;
|
|
|
|
if (isMoving != wasMoving)
|
|
lockCameraTimer = 0f;
|
|
|
|
if (targetDirection != Vector3.zero)
|
|
{
|
|
if (moveSpeed < walkSpeed * 0.9f)
|
|
{
|
|
moveDirection = targetDirection.normalized;
|
|
}
|
|
else
|
|
{
|
|
moveDirection = Vector3.RotateTowards(
|
|
moveDirection,
|
|
targetDirection,
|
|
rotateSpeed * Mathf.Deg2Rad * Time.deltaTime,
|
|
1000
|
|
).normalized;
|
|
}
|
|
}
|
|
|
|
float smooth = speedSmoothing * Time.deltaTime;
|
|
float targetSpeed = Mathf.Min(targetDirection.magnitude, 1f);
|
|
|
|
_characterState = CharacterState.Idle;
|
|
|
|
if (Input.GetButton("Jog/Sprint"))
|
|
{
|
|
targetSpeed *= runSpeed;
|
|
_characterState = CharacterState.Running;
|
|
}
|
|
else if (Time.time - trotAfterSeconds > walkTimeStart)
|
|
{
|
|
targetSpeed *= trotSpeed;
|
|
_characterState = CharacterState.Trotting;
|
|
}
|
|
else
|
|
{
|
|
targetSpeed *= walkSpeed;
|
|
_characterState = CharacterState.Walking;
|
|
}
|
|
|
|
moveSpeed = Mathf.Lerp(moveSpeed, targetSpeed, smooth);
|
|
|
|
if (moveSpeed < walkSpeed * 0.3f)
|
|
walkTimeStart = Time.time;
|
|
}
|
|
else
|
|
{
|
|
if (jumping)
|
|
lockCameraTimer = 0f;
|
|
|
|
if (isMoving)
|
|
{
|
|
inAirVelocity += targetDirection.normalized *
|
|
Time.deltaTime *
|
|
inAirControlAcceleration;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ApplyJumping()
|
|
{
|
|
if (
|
|
lastJumpTime + jumpRepeatTime <= Time.time &&
|
|
IsGrounded() &&
|
|
canJump &&
|
|
Time.time < lastJumpButtonTime + jumpTimeout
|
|
)
|
|
{
|
|
verticalSpeed = CalculateJumpVerticalSpeed(jumpHeight);
|
|
SendMessage("DidJump", SendMessageOptions.DontRequireReceiver);
|
|
}
|
|
}
|
|
|
|
void ApplyGravity()
|
|
{
|
|
if (!isControllable)
|
|
return;
|
|
|
|
if (jumping && !jumpingReachedApex && verticalSpeed <= 0.0f)
|
|
{
|
|
jumpingReachedApex = true;
|
|
SendMessage("DidJumpReachApex", SendMessageOptions.DontRequireReceiver);
|
|
}
|
|
|
|
if (IsGrounded())
|
|
{
|
|
verticalSpeed = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
verticalSpeed -= gravity * Time.deltaTime;
|
|
}
|
|
}
|
|
|
|
float CalculateJumpVerticalSpeed(float targetJumpHeight)
|
|
{
|
|
return Mathf.Sqrt(2f * targetJumpHeight * gravity);
|
|
}
|
|
|
|
void DidJump()
|
|
{
|
|
jumping = true;
|
|
jumpingReachedApex = false;
|
|
|
|
lastJumpTime = Time.time;
|
|
lastJumpStartHeight = transform.position.y;
|
|
lastJumpButtonTime = -10;
|
|
|
|
_characterState = CharacterState.Jumping;
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (!isControllable)
|
|
Input.ResetInputAxes();
|
|
|
|
if (Input.GetButtonDown("Jump"))
|
|
lastJumpButtonTime = Time.time;
|
|
|
|
UpdateSmoothedMovementDirection();
|
|
ApplyGravity();
|
|
ApplyJumping();
|
|
|
|
Vector3 movement =
|
|
moveDirection * moveSpeed +
|
|
Vector3.up * verticalSpeed +
|
|
inAirVelocity;
|
|
|
|
movement *= Time.deltaTime;
|
|
|
|
CharacterController controller = GetComponent<CharacterController>();
|
|
|
|
collisionFlags = controller.Move(movement);
|
|
|
|
UpdateAnimation(controller);
|
|
|
|
if (IsGrounded())
|
|
{
|
|
transform.rotation = Quaternion.LookRotation(moveDirection);
|
|
}
|
|
else
|
|
{
|
|
Vector3 dir = movement;
|
|
dir.y = 0;
|
|
|
|
if (dir.sqrMagnitude > 0.001f)
|
|
transform.rotation = Quaternion.LookRotation(dir);
|
|
}
|
|
|
|
if (IsGrounded())
|
|
{
|
|
lastGroundedTime = Time.time;
|
|
inAirVelocity = Vector3.zero;
|
|
|
|
if (jumping)
|
|
{
|
|
jumping = false;
|
|
SendMessage("DidLand", SendMessageOptions.DontRequireReceiver);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UpdateAnimation(CharacterController controller)
|
|
{
|
|
if (_animation == null)
|
|
return;
|
|
|
|
if (_characterState == CharacterState.Jumping)
|
|
{
|
|
if (!jumpingReachedApex)
|
|
{
|
|
_animation[jumpPoseAnimation.name].speed = jumpAnimationSpeed;
|
|
_animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever;
|
|
_animation.CrossFade(jumpPoseAnimation.name);
|
|
}
|
|
else
|
|
{
|
|
_animation[jumpPoseAnimation.name].speed = -landAnimationSpeed;
|
|
_animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever;
|
|
_animation.CrossFade(jumpPoseAnimation.name);
|
|
}
|
|
}
|
|
else if (controller.velocity.sqrMagnitude < 0.1f)
|
|
{
|
|
_animation.CrossFade(idleAnimation.name);
|
|
}
|
|
else if (_characterState == CharacterState.Running)
|
|
{
|
|
_animation[runAnimation.name].speed =
|
|
Mathf.Clamp(controller.velocity.magnitude, 0, runMaxAnimationSpeed);
|
|
|
|
_animation.CrossFade(runAnimation.name);
|
|
}
|
|
else if (_characterState == CharacterState.Trotting)
|
|
{
|
|
_animation[walkAnimation.name].speed =
|
|
Mathf.Clamp(controller.velocity.magnitude, 0, trotMaxAnimationSpeed);
|
|
|
|
_animation.CrossFade(walkAnimation.name);
|
|
}
|
|
else if (_characterState == CharacterState.Walking)
|
|
{
|
|
_animation[walkAnimation.name].speed =
|
|
Mathf.Clamp(controller.velocity.magnitude, 0, walkMaxAnimationSpeed);
|
|
|
|
_animation.CrossFade(walkAnimation.name);
|
|
}
|
|
}
|
|
|
|
void OnControllerColliderHit(ControllerColliderHit hit)
|
|
{
|
|
}
|
|
|
|
public float GetSpeed()
|
|
{
|
|
return moveSpeed;
|
|
}
|
|
|
|
public bool IsJumping()
|
|
{
|
|
return jumping;
|
|
}
|
|
|
|
public bool IsGrounded()
|
|
{
|
|
return (collisionFlags & CollisionFlags.Below) != 0;
|
|
}
|
|
|
|
public Vector3 GetDirection()
|
|
{
|
|
return moveDirection;
|
|
}
|
|
|
|
public bool IsMovingBackwards()
|
|
{
|
|
return movingBack;
|
|
}
|
|
|
|
public float GetLockCameraTimer()
|
|
{
|
|
return lockCameraTimer;
|
|
}
|
|
|
|
public bool IsMoving()
|
|
{
|
|
return Mathf.Abs(Input.GetAxisRaw("Vertical")) +
|
|
Mathf.Abs(Input.GetAxisRaw("Horizontal")) > 0.5f;
|
|
}
|
|
|
|
public bool HasJumpReachedApex()
|
|
{
|
|
return jumpingReachedApex;
|
|
}
|
|
|
|
public bool IsGroundedWithTimeout()
|
|
{
|
|
return lastGroundedTime + groundedTimeout > Time.time;
|
|
}
|
|
|
|
void Reset()
|
|
{
|
|
gameObject.tag = "Player";
|
|
}
|
|
} |