diff --git a/src/CharacterState.cs b/src/CharacterState.cs new file mode 100644 index 0000000..c0560b5 --- /dev/null +++ b/src/CharacterState.cs @@ -0,0 +1,11 @@ +using System; + +[Serializable] +public enum CharacterState +{ + Idle, + Walking, + Trotting, + Running, + Jumping +} diff --git a/src/ThirdPersonController.cs b/src/ThirdPersonController.cs new file mode 100644 index 0000000..1e9ea04 --- /dev/null +++ b/src/ThirdPersonController.cs @@ -0,0 +1,413 @@ +using System; +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; + + public float trotMaxAnimationSpeed; + + public float runMaxAnimationSpeed; + + public float jumpAnimationSpeed; + + public float landAnimationSpeed; + + private Animation _animation; + + private CharacterState _characterState; + + public float walkSpeed; + + public float trotSpeed; + + public float runSpeed; + + public float inAirControlAcceleration; + + public float jumpHeight; + + public float gravity; + + public float speedSmoothing; + + public float rotateSpeed; + + public float trotAfterSeconds; + + public bool canJump; + + private float jumpRepeatTime; + + private float jumpTimeout; + + private float groundedTimeout; + + private float lockCameraTimer; + + private Vector3 moveDirection; + + private float verticalSpeed; + + private float moveSpeed; + + private CollisionFlags collisionFlags; + + private bool jumping; + + private bool jumpingReachedApex; + + private bool movingBack; + + private bool isMoving; + + private float walkTimeStart; + + private float lastJumpButtonTime; + + private float lastJumpTime; + + private float lastJumpStartHeight; + + private Vector3 inAirVelocity; + + private float lastGroundedTime; + + private bool isControllable; + + public ThirdPersonController() + { + walkMaxAnimationSpeed = 0.75f; + trotMaxAnimationSpeed = 1f; + runMaxAnimationSpeed = 1f; + jumpAnimationSpeed = 1.15f; + landAnimationSpeed = 1f; + walkSpeed = 2f; + trotSpeed = 4f; + runSpeed = 6f; + inAirControlAcceleration = 3f; + jumpHeight = 0.5f; + gravity = 20f; + speedSmoothing = 10f; + rotateSpeed = 500f; + trotAfterSeconds = 3f; + canJump = true; + jumpRepeatTime = 0.05f; + jumpTimeout = 0.15f; + groundedTimeout = 0.25f; + moveDirection = Vector3.zero; + lastJumpButtonTime = -10f; + lastJumpTime = -1f; + inAirVelocity = Vector3.zero; + isControllable = true; + } + + public override void Awake() + { + moveDirection = transform.TransformDirection(Vector3.forward); + _animation = (Animation)GetComponent(typeof(Animation)); + if (!(bool)_animation) + { + Debug.Log("The character you would like to control doesn't have animations. Moving her might look weird."); + } + if (!(bool)idleAnimation) + { + _animation = null; + Debug.Log("No idle animation found. Turning off animations."); + } + if (!(bool)walkAnimation) + { + _animation = null; + Debug.Log("No walk animation found. Turning off animations."); + } + if (!(bool)runAnimation) + { + _animation = null; + Debug.Log("No run animation found. Turning off animations."); + } + if (!(bool)jumpPoseAnimation && canJump) + { + _animation = null; + Debug.Log("No jump animation found and the character has canJump enabled. Turning off animations."); + } + } + + public override void UpdateSmoothedMovementDirection() + { + Transform transform = Camera.main.transform; + bool flag = IsGrounded(); + Vector3 a = transform.TransformDirection(Vector3.forward); + a.y = 0f; + a = a.normalized; + Vector3 a2 = new Vector3(a.z, 0f, 0f - a.x); + float axisRaw = Input.GetAxisRaw("Vertical"); + float axisRaw2 = Input.GetAxisRaw("Horizontal"); + if (!(axisRaw >= -0.2f)) + { + movingBack = true; + } + else + { + movingBack = false; + } + bool flag2 = isMoving; + int num = (Mathf.Abs(axisRaw2) > 0.1f) ? 1 : 0; + if (num == 0) + { + num = ((Mathf.Abs(axisRaw) > 0.1f) ? 1 : 0); + } + isMoving = ((byte)num != 0); + Vector3 vector = axisRaw2 * a2 + axisRaw * a; + if (flag) + { + lockCameraTimer += Time.deltaTime; + if (isMoving != flag2) + { + lockCameraTimer = 0f; + } + if (vector != Vector3.zero) + { + if (!(moveSpeed >= walkSpeed * 0.9f) && flag) + { + moveDirection = vector.normalized; + } + else + { + moveDirection = Vector3.RotateTowards(moveDirection, vector, rotateSpeed * 0.0174532924f * Time.deltaTime, 1000f); + moveDirection = moveDirection.normalized; + } + } + float t = speedSmoothing * Time.deltaTime; + float num2 = Mathf.Min(vector.magnitude, 1f); + _characterState = CharacterState.Idle; + if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) + { + num2 *= runSpeed; + _characterState = CharacterState.Running; + } + else if (!(Time.time - trotAfterSeconds <= walkTimeStart)) + { + num2 *= trotSpeed; + _characterState = CharacterState.Trotting; + } + else + { + num2 *= walkSpeed; + _characterState = CharacterState.Walking; + } + moveSpeed = Mathf.Lerp(moveSpeed, num2, t); + if (!(moveSpeed >= walkSpeed * 0.3f)) + { + walkTimeStart = Time.time; + } + } + else + { + if (jumping) + { + lockCameraTimer = 0f; + } + if (isMoving) + { + inAirVelocity += vector.normalized * Time.deltaTime * inAirControlAcceleration; + } + } + } + + public override void ApplyJumping() + { + if (lastJumpTime + jumpRepeatTime <= Time.time && IsGrounded() && canJump && !(Time.time >= lastJumpButtonTime + jumpTimeout)) + { + verticalSpeed = CalculateJumpVerticalSpeed(jumpHeight); + SendMessage("DidJump", SendMessageOptions.DontRequireReceiver); + } + } + + public override void ApplyGravity() + { + if (isControllable) + { + bool button = Input.GetButton("Jump"); + if (jumping && !jumpingReachedApex && !(verticalSpeed > 0f)) + { + jumpingReachedApex = true; + SendMessage("DidJumpReachApex", SendMessageOptions.DontRequireReceiver); + } + if (IsGrounded()) + { + verticalSpeed = 0f; + } + else + { + verticalSpeed -= gravity * Time.deltaTime; + } + } + } + + public override float CalculateJumpVerticalSpeed(float targetJumpHeight) + { + return Mathf.Sqrt(2f * targetJumpHeight * gravity); + } + + public override void DidJump() + { + jumping = true; + jumpingReachedApex = false; + lastJumpTime = Time.time; + Vector3 position = transform.position; + lastJumpStartHeight = position.y; + lastJumpButtonTime = -10f; + _characterState = CharacterState.Jumping; + } + + public override void Update() + { + if (!isControllable) + { + Input.ResetInputAxes(); + } + if (Input.GetButtonDown("Jump")) + { + lastJumpButtonTime = Time.time; + } + UpdateSmoothedMovementDirection(); + ApplyGravity(); + ApplyJumping(); + Vector3 a = moveDirection * moveSpeed + new Vector3(0f, verticalSpeed, 0f) + inAirVelocity; + a *= Time.deltaTime; + CharacterController characterController = (CharacterController)GetComponent(typeof(CharacterController)); + collisionFlags = characterController.Move(a); + if ((bool)_animation) + { + 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 = 0f - landAnimationSpeed; + _animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever; + _animation.CrossFade(jumpPoseAnimation.name); + } + } + else if (!(characterController.velocity.sqrMagnitude >= 0.1f)) + { + _animation.CrossFade(idleAnimation.name); + } + else if (_characterState == CharacterState.Running) + { + _animation[runAnimation.name].speed = Mathf.Clamp(characterController.velocity.magnitude, 0f, runMaxAnimationSpeed); + _animation.CrossFade(runAnimation.name); + } + else if (_characterState == CharacterState.Trotting) + { + _animation[walkAnimation.name].speed = Mathf.Clamp(characterController.velocity.magnitude, 0f, trotMaxAnimationSpeed); + _animation.CrossFade(walkAnimation.name); + } + else if (_characterState == CharacterState.Walking) + { + _animation[walkAnimation.name].speed = Mathf.Clamp(characterController.velocity.magnitude, 0f, walkMaxAnimationSpeed); + _animation.CrossFade(walkAnimation.name); + } + } + if (IsGrounded()) + { + transform.rotation = Quaternion.LookRotation(moveDirection); + } + else + { + Vector3 forward = a; + forward.y = 0f; + if (!(forward.sqrMagnitude <= 0.001f)) + { + transform.rotation = Quaternion.LookRotation(forward); + } + } + if (IsGrounded()) + { + lastGroundedTime = Time.time; + inAirVelocity = Vector3.zero; + if (jumping) + { + jumping = false; + SendMessage("DidLand", SendMessageOptions.DontRequireReceiver); + } + } + } + + public override void OnControllerColliderHit(ControllerColliderHit hit) + { + Vector3 vector = hit.moveDirection; + if (vector.y <= 0.01f) + { + } + } + + public override float GetSpeed() + { + return moveSpeed; + } + + public override bool IsJumping() + { + return jumping; + } + + public override bool IsGrounded() + { + return (collisionFlags & CollisionFlags.Below) != CollisionFlags.None; + } + + public override Vector3 GetDirection() + { + return moveDirection; + } + + public override bool IsMovingBackwards() + { + return movingBack; + } + + public override float GetLockCameraTimer() + { + return lockCameraTimer; + } + + public override bool IsMoving() + { + return Mathf.Abs(Input.GetAxisRaw("Vertical")) + Mathf.Abs(Input.GetAxisRaw("Horizontal")) > 0.5f; + } + + public override bool HasJumpReachedApex() + { + return jumpingReachedApex; + } + + public override bool IsGroundedWithTimeout() + { + return lastGroundedTime + groundedTimeout > Time.time; + } + + public override void Reset() + { + gameObject.tag = "Player"; + } + + public override void Main() + { + } +}