using System; using NUnit.Framework.Internal; using UnityEngine; using ParadoxNotion.Design; using Sirenix.OdinInspector; using UnityEditor.Rendering; using UnityEngine.Serialization; public enum PlayerFacingDirection{ TowardsTarget = 0, MatchForward, MatchCamera, Static, Momentum, SpecifiedDirection } namespace Reset.Units{ // public interface IBuffSource{ - Unused good idea? // public Object sourceObject(); // public // } [Serializable] public class UnitMovementData : ICloneable{ // Movement Direction public float accelerationSmoothing = 5f; public float deaccelerationSmoothing = 5f; // public AnimationCurve deaccelerationCurve; // Currently unused, may return [SliderField(0,1)] public float airDirectionDecay; // Move Speed public float moveSpeed = 15f; public float moveSpeedSoothing = 10f; // Jumping [ShowInInspector, ReadOnly] public float jumpPower; public float jumpPowerDecay = 3f; // Gravity [ShowInInspector, ReadOnly] public float gravityPower; public float gravityMax = 8f; public float gravityAcceleration = 1f; public float gravityScale = 1f; public float settingsChangeSmoothing = 6f; // Rotation [ShowInInspector, SerializeReference] public Enum rotateFacing; [FormerlySerializedAs("rotationSpeedTarget")] public float rotationSpeed = 5f; public float rotationSmoothing = 1f; public float rotationInputBlending = .3f; public object Clone(){ return MemberwiseClone(); } } public class UnitMovementHandler : MonoBehaviour{ // class MovementFloatModifier{ // // IBuffSource source // public float value; // } // Debug viewing [ShowInInspector, ReadOnly] private float outputSpeed; [ShowInInspector, ReadOnly] private float additionalSpeed; [ShowInInspector, ReadOnly] public Vector3 outputMoveDirection; [ShowInInspector, ReadOnly] private Vector3 additionalMoveDirection; [ShowInInspector, ReadOnly] private Quaternion outputRotation; [ShowInInspector, ReadOnly] private Quaternion specifiedRotation; [ShowInInspector, ReadOnly] private float outputRotationSpeed; // Used by graph to gradually shift into new values rather than dump them. Even if they're smoothed values they need to be eased into [ShowInInspector, ReadOnly] private float outputJumpDecay; [ShowInInspector, ReadOnly] private float outputGravityAccel; [ShowInInspector, ReadOnly] private float outputGravityScale; [ShowInInspector, ReadOnly] private float settingsChangeRotationSpeed; private float directionChangeDot; private bool moveCallDisabledNextFrame; private CharacterController controller; private PlayerControls controls; private LockOnManager lockOnManager; private Vector3 moveSmooth; public float gravitySmooth; [ShowInInspector, PropertyOrder(2)] public UnitMovementData data = new(); [HideInInspector] public UnitMovementData defaultData; void Awake(){ controller = GetComponent(); controls = GetComponent(); lockOnManager = GetComponent(); } private void Start(){ defaultData = (UnitMovementData)data.Clone(); } void Update(){ UpdateCurrentDirection(); UpdateCurrentGravity(); UpdateCurrentSpeed(); UpdateCurrentRotation(); DoMovement(); } // Add directly to the direction public void AddToCurrentDirection(Vector3 inputDirection, float power){ additionalMoveDirection += inputDirection.normalized; additionalSpeed = power; } public void SetNewDirection(Vector3 inputDirection, float power){ additionalMoveDirection = inputDirection.normalized; additionalSpeed = power * Time.deltaTime; } // Hold a new rotation to be moved to during PlayerFacingDirection.SpecifiedRotation public void SetSpecifiedRotation(Quaternion inputRotation){ specifiedRotation = inputRotation; } // Blend between the current direction and an input direction, based on the current controller input public void OverwriteDirectionFromInput(Vector2 value, float priority, float speed = Mathf.Infinity){ // Create a new direction that is the current controller input * the amount of power they should have Vector3 dirToAdd = new Vector3(controls.rawMoveInput.x * value.x, 0f, controls.rawMoveInput.y * value.y); // Multiply it by the current magnitude (why? i forgor) dirToAdd *= controls.rawMoveInput.magnitude; // Blend the existing direction into it dirToAdd = Vector3.Lerp(outputMoveDirection, dirToAdd, priority * controls.rawMoveInput.magnitude); // Set the new direction outputMoveDirection = new Vector3(dirToAdd.x, outputMoveDirection.y, dirToAdd.z); // Everthing under here is for the speed now. If it's not set do...nothing. Otherwise set it to the max value between move speed and the new speed if (float.IsPositiveInfinity(speed)) { return; } outputSpeed = Mathf.Lerp(outputSpeed, Mathf.Max(data.moveSpeed, speed), priority); } // Update the direction, called every frame private void UpdateCurrentDirection(){ // Get input value Vector3 inputMovement = new Vector3(controls.rawMoveInput.x, 0f, controls.rawMoveInput.y); // Construct move direction Vector3 targetDirection = inputMovement; // Deadzone if (inputMovement.magnitude < .05f) { targetDirection = Vector3.zero; } // Remove Y from variables Vector3 targetNoY = new Vector3(targetDirection.x, 0f, targetDirection.z); Vector3 currentNoY = new Vector3(outputMoveDirection.x, 0f, outputMoveDirection.z); // Also need to find the dot value of the current input versus the current move direction Vector3 slerpedValue; Vector3 lerpedValue; float switchedDirection = Vector3.Dot(inputMovement, outputMoveDirection); float switchedDirectionRemapped = Mathf.Lerp(0, 1, switchedDirection); directionChangeDot = Mathf.Lerp(switchedDirection, switchedDirectionRemapped, 5f * Time.deltaTime) ; // turn that .5f into a variable // Smooth movement. Use deaccel smoothing if the input magnitude is lower, and accel smoothing if it's higher // Also checks when grounded to only use Slerp on the ground if (targetNoY.magnitude > currentNoY.magnitude) { if (controller.isGrounded){ slerpedValue = Vector3.Slerp(currentNoY, targetNoY, data.accelerationSmoothing * Time.deltaTime); lerpedValue = Vector3.Lerp(currentNoY, targetNoY, data.accelerationSmoothing * Time.deltaTime); currentNoY = Vector3.Lerp(lerpedValue, slerpedValue, directionChangeDot); } else { currentNoY = Vector3.Lerp(currentNoY, targetNoY, data.accelerationSmoothing * Time.deltaTime); } } else { if (controller.isGrounded){ slerpedValue = Vector3.Slerp(currentNoY, targetNoY, data.deaccelerationSmoothing * Time.deltaTime); lerpedValue = Vector3.Lerp(currentNoY, targetNoY, data.deaccelerationSmoothing * Time.deltaTime); currentNoY = Vector3.Lerp(lerpedValue, slerpedValue, directionChangeDot); } else { currentNoY = Vector3.Lerp(currentNoY, targetNoY, data.deaccelerationSmoothing * data.airDirectionDecay * Time.deltaTime); } } // Commit move direction outputMoveDirection = Vector3.SmoothDamp(outputMoveDirection, new Vector3(currentNoY.x, outputMoveDirection.y, currentNoY.z),ref moveSmooth , .5f *Time.deltaTime); } // Update the speed, called every frame private void UpdateCurrentSpeed(){ outputSpeed = Mathf.Lerp(outputSpeed, data.moveSpeed, data.moveSpeedSoothing * Time.deltaTime); } // Update the gravity, called every frame private void UpdateCurrentGravity(){ // Accelerate gravity data.gravityPower += outputGravityAccel * Time.deltaTime; data.gravityPower = Mathf.Clamp(data.gravityPower, Mathf.NegativeInfinity, data.gravityMax); // Apply a constant gravity if the player is grounded if (controller.isGrounded) { data.gravityPower = .1f; } // Create the final gravity value float gravityMoveDirection = data.jumpPower + (Physics.gravity.y * data.gravityPower); // Commit gravity to move direction, ignoring XZ since those are done in UpdateMovementDirection outputMoveDirection.y = Mathf.SmoothDamp(outputMoveDirection.y, gravityMoveDirection, ref gravitySmooth, .1f * Time.deltaTime); } // Update the rotation, called every frame private void UpdateCurrentRotation(){ // Get input value Vector3 inputMovement = new Vector3(controls.rawMoveInput.x, 0f, controls.rawMoveInput.y); // Switch the desired rotation based on current movement setting switch (data.rotateFacing) { // Just look at target case PlayerFacingDirection.TowardsTarget: // Look directly at the target outputRotation = Quaternion.LookRotation(transform.position.DirectionTo(lockOnManager.mainTarget.gameObject.transform.position)); break; case PlayerFacingDirection.Momentum: // Look towards the current direction the agent is moving if (inputMovement.magnitude > .05f){ outputRotation = Camera.main.transform.rotation * Quaternion.LookRotation(outputMoveDirection); } break; case PlayerFacingDirection.MatchForward: // Look towards the input direction....why??? I guess cause move direction is PlayerFacingDirection.Momentum. if (controls.rawMoveInput.magnitude < 0.05f) { break; } outputRotation = Camera.main.transform.rotation * Quaternion.LookRotation(inputMovement); break; case PlayerFacingDirection.MatchCamera: // Look the same direction as the camera outputRotation = Quaternion.Euler(Camera.main.transform.rotation.eulerAngles.Flatten(0, null, 0)); break; case PlayerFacingDirection.Static: // Don't change outputRotation = transform.rotation; break; case PlayerFacingDirection.SpecifiedDirection: // Look at an inputed rotation outputRotation = specifiedRotation; break; } // Add the current input into the created rotation if (inputMovement.magnitude > .05){ outputRotation = Quaternion.Lerp(outputRotation, Camera.main.transform.rotation * Quaternion.LookRotation(inputMovement), data.rotationInputBlending); } // Calculate rotation speed, as in how fast the fella rotates. This value has it's own smoothing to allow for gradual increasing/decreasing of the speed till it reaches the target outputRotationSpeed = Mathf.Lerp(outputRotationSpeed, data.rotationSpeed, settingsChangeRotationSpeed * Time.deltaTime); // Set final rotation transform.rotation = Quaternion.Slerp(transform.rotation, outputRotation, outputRotationSpeed * Time.deltaTime).Flatten(0, null, 0); } // Move with default settings public void DoMovement(){ DoMovement(outputMoveDirection, outputSpeed, data.gravityScale); } public void DisableNextMoveCall(){ moveCallDisabledNextFrame = true; } // Custom move from input public void DoMovement(Vector3 moveDir, float speed, float gravityScale, bool relativeToCamera = true){ if (moveCallDisabledNextFrame) { // Vector3 velocity = transform.InverseTransformDirection(controller.velocity.normalized); // outputMoveDirection = new Vector3(velocity.x, outputMoveDirection.y, velocity.z); moveCallDisabledNextFrame = false; return; } Debug.Log($"moveDir: {moveDir}, agent velocity: {transform.InverseTransformDirection(controller.velocity.normalized)}"); // Seperate the different move directions. Additonal becomes it's own because it needs to be added independently of the other two Vector3 moveXZDir = new Vector3(moveDir.x, 0f, moveDir.z); Vector3 moveYDir = new Vector3(0f, moveDir.y, 0f); Vector3 addDir = additionalMoveDirection; // Add their related speeds moveXZDir *= speed * Time.deltaTime; moveYDir *= outputGravityScale * Time.deltaTime; addDir *= additionalSpeed * Time.deltaTime; // Construct the direction and move Vector3 finalDir = moveXZDir + moveYDir; if (relativeToCamera) { controller.Move((Camera.main.transform.rotation.Flatten(0, null, 0) * finalDir) + addDir); } else { controller.Move(finalDir + addDir); } } void LateUpdate(){ UpdateGravityLate(); DecayAdditionalDirection(); SmoothingSettingsChanges(); } private void SmoothingSettingsChanges(){ outputJumpDecay = Mathf.Lerp(outputJumpDecay, data.jumpPowerDecay, data.settingsChangeSmoothing * Time.deltaTime); outputGravityAccel = Mathf.Lerp(outputGravityAccel, data.gravityAcceleration, data.settingsChangeSmoothing * Time.deltaTime); outputGravityScale = Mathf.Lerp(outputGravityScale, data.gravityScale, data.settingsChangeSmoothing * Time.deltaTime); settingsChangeRotationSpeed = Mathf.Lerp(settingsChangeRotationSpeed, data.rotationSpeed, data.settingsChangeSmoothing * Time.deltaTime); } void DecayAdditionalDirection(){ // Get input value Vector3 inputMovement = new Vector3(controls.rawMoveInput.x, 0f, controls.rawMoveInput.y); // Ignore values under deadzone if (inputMovement.magnitude < .1f) { inputMovement = Vector3.zero; } // Remove Y from variables Vector3 currentNoY = new Vector3(additionalMoveDirection.x, 0f, additionalMoveDirection.z); // Decay the direction if (inputMovement.magnitude < currentNoY.magnitude) { additionalMoveDirection = Vector3.Slerp(additionalMoveDirection, Vector3.zero,data.accelerationSmoothing * Time.deltaTime); } else { // float deaccelValue = data.deaccelerationCurve.Evaluate(inputMovement.magnitude); additionalMoveDirection = Vector3.Lerp(additionalMoveDirection, Vector3.zero, data.deaccelerationSmoothing * Time.deltaTime); } // Decay the gravity additionalMoveDirection.y -= data.gravityPower; additionalMoveDirection.y = Mathf.Max(0f, additionalMoveDirection.y); } private void UpdateGravityLate(){ // Decay jump power data.jumpPower -= outputJumpDecay * Time.deltaTime; data.jumpPower = Mathf.Max(0f, data.jumpPower); } } }