// Copyright (C) 2023 Nicholas Maltbie // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and // associated documentation files (the "Software"), to deal in the Software without restriction, // including without limitation the rights to use, copy, modify, merge, publish, distribute, // sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING // BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. using System; using nickmaltbie.OpenKCC.Character.Config; using nickmaltbie.OpenKCC.Input; using nickmaltbie.OpenKCC.Utils; using UnityEngine; namespace nickmaltbie.OpenKCC.Character.Action { /// /// Jump action that can be performed by a character controller. /// [Serializable] public class JumpAction : ConditionalAction { [Header("Player Jump Settings")] /// /// Action reference for jumping. /// [Tooltip("Action reference for jumping.")] [SerializeField] public BufferedInput jumpInput; /// /// Velocity of player jump in units per second. /// [Tooltip("Vertical velocity of player jump.")] [SerializeField] public float jumpVelocity = 5.0f; /// /// Maximum angle at which the player can jump (in degrees). /// [Tooltip("Maximum angle at which the player can jump (in degrees).")] [SerializeField] [Range(0, 90)] public float maxJumpAngle = 85f; /// /// Weight to which the player's jump is weighted towards the direction /// of the surface they are standing on. /// [Tooltip("Weight to which the player's jump is weighted towards the angle of their surface.")] [SerializeField] [Range(0, 1)] public float jumpAngleWeightFactor = 0.0f; /// /// Grounded state for managing player grounded configuration. /// private IKCCGrounded kccGrounded; /// /// Actor to apply jumps to. /// private IJumping actor; /// /// Configuration of the character controller. /// private IKCCConfig kccConfig; /// /// Has the player jumped while they are sliding. /// public bool JumpedWhileSliding { get; private set; } /// /// Setup this jump action. /// public void Setup(IKCCGrounded kccGrounded, IKCCConfig kccConfig, IJumping actor) { base.condition = CanJump; this.kccGrounded = kccGrounded; this.kccConfig = kccConfig; this.actor = actor; JumpedWhileSliding = false; jumpInput.InputAction?.Enable(); } /// public override void Update() { base.Update(); jumpInput?.Update(); if (kccGrounded != null && kccGrounded.StandingOnGround && !kccGrounded.Sliding) { JumpedWhileSliding = false; } } /// /// Apply the jump action to the actor if attempting /// jump and the player can jump. /// /// True if the player jumped, false otherwise. public bool ApplyJumpIfPossible(IKCCGrounded grounded) { kccGrounded = grounded; if (AttemptingJump && CanPerform) { Jump(); return true; } return false; } /// /// Apply the jump to the player. /// public void Jump() { if (kccGrounded.Sliding) { JumpedWhileSliding = true; } Vector3 jumpDirection = (kccGrounded.StandingOnGround ? kccGrounded.SurfaceNormal : kccConfig.Up) * jumpAngleWeightFactor + kccConfig.Up * (1 - jumpAngleWeightFactor); actor.ApplyJump(jumpVelocity * jumpDirection.normalized); jumpInput.Reset(); } /// /// Is the player currently attempting to jump /// public bool AttemptingJump => jumpInput.Pressed; /// /// Can the player jump based on their current state. /// /// True if the player can jump, false otherwise. public bool CanJump() { bool canJump = kccGrounded.StandingOnGround && kccGrounded.Angle <= maxJumpAngle; if (canJump && !kccGrounded.Sliding) { return true; } else if (canJump) { return !JumpedWhileSliding; } return false; } } }