using System; using UnityEngine; using ParadoxNotion.Design; using PlasticPipe.PlasticProtocol.Messages; using Sirenix.OdinInspector; 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 SettingValue acceleration = new SettingValue(5f); public SettingValue deaccerlation = new SettingValue(5f); [SliderField(0,1)] public SettingValue airDirectionDecay = new SettingValue(1f); // TODO: Check default value // Move Speed public SettingValue moveSpeed = new SettingValue(15f, defaultSmoothing: 10f); // Jumping [ShowInInspector] public SettingValue jumpPower = new SettingValue(0f); public SettingValue jumpPowerDecay = new SettingValue(3f); // TODO: Check default value // Gravity [ShowInInspector] public SettingValue gravityPower = new SettingValue(1f); public SettingValue gravityMax = new SettingValue(8f); public SettingValue gravityAcceleration = new SettingValue(1f); public SettingValue gravityScale = new SettingValue(1f); // Rotation [ShowInInspector, SerializeReference] public Enum rotateFacing; public SettingValue rotationSpeed = new SettingValue(5f); public SettingValue rotationInputBlending = new SettingValue(.3f); public object Clone(){ return MemberwiseClone(); } } public class ResolvedMovement{ public UnitMovementHandler.MoveDirection moveDirection; public float moveSpeed; public Quaternion rotation; public float rotationSpeed; public float gravity; } public class UnitMovementHandler : MonoBehaviour{ public struct MoveDirection{ private Transform owner; private Vector2 moveDir; // Always local public Vector2 World{ get => owner.TransformDirection(Local); set{ moveDir = owner.InverseTransformDirection(value); Local = moveDir; } } public Vector2 Local{ get => owner.InverseTransformDirection(World); set { moveDir = value; World = owner.TransformDirection(value); } } } [ShowInInspector] public ResolvedMovement resolvedMovement; // class MovementFloatModifier{ // // IBuffSource source // public float value; // } // [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; // Lerps 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(); [ShowInInspector, PropertyOrder(2)] public UnitMovementData smoothing = new(); [ShowInInspector, PropertyOrder(2)] public UnitMovementData easing = new(); [HideInInspector] public UnitMovementData defaultData; [HideInInspector] public UnitMovementData defaultSmoothing; [HideInInspector] public UnitMovementData defaultEasing; void Awake(){ controller = GetComponent(); controls = GetComponent(); lockOnManager = GetComponent(); } void Start(){ defaultData = (UnitMovementData)data.Clone(); defaultSmoothing = (UnitMovementData)smoothing.Clone(); defaultEasing = (UnitMovementData)easing.Clone(); resolvedMovement = new ResolvedMovement(); } 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 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); directionChangeDotLerp = 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.acceleration.value * Time.deltaTime); lerpedValue = Vector3.Lerp(currentNoY, targetNoY, data.acceleration.value * Time.deltaTime); currentNoY = Vector3.Lerp(lerpedValue, slerpedValue, directionChangeDotLerp); } else { currentNoY = Vector3.Lerp(currentNoY, targetNoY, data.acceleration.value * Time.deltaTime); } } else { if (controller.isGrounded){ slerpedValue = Vector3.Slerp(currentNoY, targetNoY, data.deaccerlation.value * Time.deltaTime); lerpedValue = Vector3.Lerp(currentNoY, targetNoY, data.deaccerlation.value * Time.deltaTime); currentNoY = Vector3.Lerp(lerpedValue, slerpedValue, directionChangeDotLerp); } else { currentNoY = Vector3.Lerp(currentNoY, targetNoY, data.deaccerlation.value * data.airDirectionDecay.value * Time.deltaTime); } } // Commit move direction outputMoveDirection = Vector3.SmoothDamp(outputMoveDirection, new Vector3(currentNoY.x, outputMoveDirection.y, currentNoY.z),ref moveSmoothVelocityRef , .5f *Time.deltaTime); // TODO: Check this smoothing } // Update the speed, called every frame private void UpdateCurrentSpeed(){ resolvedMovement.moveSpeed = Mathf.Lerp(resolvedMovement.moveSpeed, data.moveSpeed.value, data.moveSpeed.smoothing * Time.deltaTime); } // 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.rotationSpeed.smoothing * 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.value); // TODO: Gets multiplied a second time in DoMovement by gravity scale???? } // Custom move from input public void DoMovement(Vector3 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 Vector3 moveXZDir = new Vector3(moveDir.x, 0f, moveDir.z); Vector3 moveYDir = new Vector3(0f, resolvedMovement.gravity, 0f); 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 = moveXZDir + moveYDir; 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.deaccerlation.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); } } }