using UnityEngine; using Reset.Core.Tools; using Sirenix.OdinInspector; namespace Reset.Units{ public class UnitMovementHandler : MonoBehaviour{ [ShowInInspector] public ResolvedMovement resolvedMovement; [FoldoutGroup("Final Values"), ShowInInspector, ReadOnly] private float outputSpeed; [FoldoutGroup("Final Values"), ShowInInspector, ReadOnly] private float additionalSpeed; [FoldoutGroup("Final Values"), ShowInInspector, ReadOnly] public Vector3 outputMoveDirection; [FoldoutGroup("Final Values"), ShowInInspector, ReadOnly] public Vector3 additionalMoveDirection; [FoldoutGroup("Final Values"), ShowInInspector, ReadOnly] private Quaternion outputRotation; [FoldoutGroup("Final Values"), ShowInInspector, ReadOnly] private Quaternion specifiedRotation; [FoldoutGroup("Final Values"), ShowInInspector, ReadOnly] private float outputRotationSpeed; // Smoothing Values private float directionChangeDotLerp; private Vector3 moveSmoothVelocityRef; private float gravitySmoothVelocityRef; // References private CharacterController controller; private PlayerControls controls; private LockOnManager lockOnManager; // Movement Data [ShowInInspector, PropertyOrder(2)] public UnitMovementData data = new(); void Awake(){ controller = GetComponent(); controls = GetComponent(); lockOnManager = GetComponent(); } void Start(){ resolvedMovement = new ResolvedMovement(); resolvedMovement.moveDirection = new ResolvedMovement.MoveDirection(transform); } void Update(){ UpdateCurrentDirection(); UpdateCurrentGravity(); UpdateCurrentSpeed(); // UpdateCurrentRotation(); DoMovement(); } // Add directly to the direction public void AddToCurrentDirection(Vector3 inputDirection, float power){ // Old additionalMoveDirection += inputDirection.normalized; additionalSpeed = power; } public void SmoothToSpeed(float desiredSpeed, float smoothing){ // Old additionalSpeed = Mathf.Lerp(additionalSpeed, desiredSpeed, smoothing * Time.deltaTime); } public void SetNewDirection(Vector3 inputDirection){ // NOTE: If smoothing desired add a default bool for smoothing maybe? // Old additionalMoveDirection = inputDirection.Flatten(null, 0f, null); } public void SetNewGravity(float value){ additionalMoveDirection.y = value; } // Hold a new rotation to be moved to during PlayerFacingDirection.SpecifiedRotation public void SetSpecifiedRotation(Quaternion inputRotation){ // Old 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){ // Old // 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.Value, speed), priority); } // Setting absolute to true will cause the current gravity to snap to the new gravity value. // Keeping it false will make it apply additively to the current gravity. Both options use relativty for linear interpolation. public void SetNewGravity(float value, float relativity, bool absolute){ // new if (absolute){ resolvedMovement.gravity = Mathf.Lerp(resolvedMovement.gravity, value, relativity); } else { resolvedMovement.gravity = Mathf.Lerp(resolvedMovement.gravity, resolvedMovement.gravity + value, relativity); } } // Update the direction, called every frame private void UpdateCurrentDirection(){ // Get input value Vector2 targetDirection = new Vector2(controls.rawMoveInput.x, controls.rawMoveInput.y); // Deadzone if (targetDirection.magnitude < .05f) { targetDirection = Vector2.zero; } // Get current direction Vector2 currentDirection = resolvedMovement.moveDirection.Local; // Also need to find the dot value of the current input versus the current move direction Vector3 slerpedValue; Vector2 lerpedValue; Vector2 newDirection; float switchedDirection = Vector3.Dot(targetDirection, currentDirection); float switchedDirectionRemapped = Mathf.Lerp(0, 1, switchedDirection); directionChangeDotLerp = Mathf.Lerp(switchedDirection, switchedDirectionRemapped, 5f * Time.deltaTime) ; // turn that .5f into a variable DebugOverlayDrawer.ChangeValue("Movement", "Direction Change Dot", directionChangeDotLerp); // 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 (controller.isGrounded){ slerpedValue = Vector3.Slerp(currentDirection, targetDirection, data.softening.Value * Time.deltaTime); // This used to be a slerp. If rotational movement broke this is why lerpedValue = Vector2.Lerp(currentDirection, targetDirection, data.softening.Value * Time.deltaTime); newDirection = Vector2.Lerp(slerpedValue, lerpedValue, directionChangeDotLerp); } else { newDirection = Vector2.Lerp(currentDirection, targetDirection, data.softening.Value * data.airDirectionDecay.Value * Time.deltaTime); } // newDirection = Vector2.MoveTowards(currentDirection, targetDirection.Rotate(-Vector2.Angle(currentDirection, targetDirection)), 2f * Time.deltaTime); // Commit move direction // resolvedMovement.moveDirection.Local = Vector2.SmoothDamp( // resolvedMovement.moveDirection.Local, // newDirection, // ref smoothing.moveSmoothing.refVel, // smoothing.moveSmoothing.smoothing *Time.deltaTime); // TODO: Check this smoothing resolvedMovement.moveDirection.Local = newDirection; } // Update the speed, called every frame private void UpdateCurrentSpeed(){ float speed; if (resolvedMovement.moveDirection.Local.magnitude < controls.rawMoveInput.magnitude) { speed = data.moveSpeed.Value * Time.deltaTime * data.acceleration.Value; } else { speed = data.moveSpeed.Value * Time.deltaTime * data.deacceleration.Value; } resolvedMovement.moveSpeed = speed; DebugOverlayDrawer.ChangeValue("Movement", "Resolved Speed", resolvedMovement.moveSpeed); } // Update the gravity, called every frame private void UpdateCurrentGravity(){ // Accelerate gravity if (!controller.isGrounded){ resolvedMovement.gravity -= data.gravityAcceleration.Value * Time.deltaTime; } // resolvedMovement.gravity = Mathf.Clamp(resolvedMovement.gravity, Mathf.NegativeInfinity, data.gravityMax.value); // Apply a constant gravity if the player is grounded if (controller.isGrounded) { // resolvedMovement.gravity = -.3f; } // Create the final gravity value float gravityMoveDirection = data.jumpPower.Value + (Physics.gravity.y * resolvedMovement.gravity); // resolvedMovement.gravity = data.jumpPower.value + (Physics.gravity.y * data.gravityPower.currentValue); // Commit gravity to move direction, ignoring XZ since those are done in UpdateMovementDirection outputMoveDirection.y = Mathf.SmoothDamp(outputMoveDirection.y, gravityMoveDirection, ref gravitySmoothVelocityRef, .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.Value); } // 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.Value, data.softening.Value * 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(resolvedMovement.moveDirection.Local, resolvedMovement.moveSpeed, data.gravityScale.Value); // TODO: Gets multiplied a second time in DoMovement by gravity scale???? } // Custom move from input public void DoMovement(Vector2 moveDir, float speed, float gravityScale){ // 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 Vector2 moveXZDir = new Vector3(moveDir.x, moveDir.y); float moveYDir = resolvedMovement.gravity; Vector3 addDir = additionalMoveDirection; // Add their related speeds moveXZDir *= speed * Time.deltaTime; moveYDir *= data.gravityScale.Value * Time.deltaTime; addDir *= additionalSpeed * Time.deltaTime; // Construct the direction and move Vector3 finalDir = new Vector3(moveXZDir.x, moveYDir,moveXZDir.y) ; controller.Move((Camera.main.transform.rotation.Flatten(0, null, 0) * finalDir) + addDir); } void LateUpdate(){ UpdateGravityLate(); DecayAdditionalDirection(); } 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.acceleration.Value * Time.deltaTime); } else { // float deaccelValue = data.deaccelerationCurve.Evaluate(inputMovement.magnitude); additionalMoveDirection = Vector3.Lerp(additionalMoveDirection, Vector3.zero, data.deacceleration.Value * Time.deltaTime); } // Decay the gravity additionalMoveDirection.y -= data.gravityPower.Value; additionalMoveDirection.y = Mathf.Max(0f, additionalMoveDirection.y); } private void UpdateGravityLate(){ // Decay jump power data.jumpPower.Value -= data.jumpPowerDecay.Value * Time.deltaTime; data.jumpPower.Value = Mathf.Max(0f, data.jumpPower.Value); } [Button("Initialize Settings")] void InitAllSettings(){ var newthing = data.GetAllSettings(); } void SmoothAllSettings(){ } } }