Files
project-reset/Assets/Scripts/Units/UnitMovementHandler.cs

329 lines
15 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using Reset.Core.Tools;
using Sirenix.OdinInspector;
namespace Reset.Units{
public class UnitMovementHandler : MonoBehaviour{
[ShowInInspector, InlineProperty, HideLabel, FoldoutGroup("Resolved Movement", expanded: true)]
public ResolvedMovement resolvedMovement;
// SmoothDamp Velocities
private Vector2 refVelocityDirectionChangingHardness;
// 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), FoldoutGroup("Movement Data", expanded: true), InlineProperty, HideLabel] public UnitMovementData data = new();
// Other
public Vector3 specifiedRotation; // Used for locking a specific direction
void Awake(){
controller = GetComponent<CharacterController>();
controls = GetComponent<PlayerControls>();
lockOnManager = GetComponent<LockOnManager>();
InitAllSettings();
}
void Start(){
resolvedMovement = new ResolvedMovement{
moveDirection = new ResolvedMovement.MoveDirection(transform)
};
}
void Update(){
SmoothAllSettings();
UpdateCurrentDirection();
UpdateCurrentGravity();
UpdateCurrentSpeed();
UpdateCurrentRotation();
// Apply movement
DoMovement(resolvedMovement.moveDirection.World, resolvedMovement.gravity, resolvedMovement.moveSpeed, data.gravityScale.Value);
DebugOverlayDrawer.ChangeValue("Movement", "Move Direction (Local)", resolvedMovement.moveDirection.Local);
DebugOverlayDrawer.ChangeValue("Movement", "Move Direction (World)", resolvedMovement.moveDirection.World);
}
// Update the direction, called every frame
private void UpdateCurrentDirection(){
// Get input value
Vector2 targetDirection = new Vector2(controls.rawMoveInput.x, controls.rawMoveInput.y);
// Rotate input by camera rotation (instead of rotating the output direction by camera rotation)
targetDirection = (Camera.main.transform.rotation * targetDirection.ToVector3()).ToVector2();
// Deadzone
if (targetDirection.magnitude < .1f) {
targetDirection = resolvedMovement.moveDirection.RawWorld;
}
// Set Raw Direction (this is used by the camera later)
resolvedMovement.moveDirection.RawWorld = targetDirection;
// Get current direction
Vector2 currentDirection = resolvedMovement.moveDirection.World;
// Also need to find the dot value of the current input versus the current move direction
float switchedDirection = Vector3.Dot(targetDirection, currentDirection);
float switchedDirectionRemapped = Mathf.Lerp(0, 1, switchedDirection);
directionChangeDotLerp = Mathf.Lerp(switchedDirection, switchedDirectionRemapped, data.directionSpinningHardness.Value * Time.deltaTime);
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
Vector3 slerpedValue;
Vector2 lerpedValue;
Vector2 newDirection;
if (controller.isGrounded){
slerpedValue = Vector3.Slerp(currentDirection, targetDirection, data.directionSpinningSpeed.Value * Time.deltaTime);
lerpedValue = Vector2.SmoothDamp(currentDirection, targetDirection, ref refVelocityDirectionChangingHardness, data.directionChangingSoftness.Value * Time.deltaTime);
newDirection = Vector2.Lerp(slerpedValue, lerpedValue, directionChangeDotLerp);
} else {
newDirection = Vector2.SmoothDamp(currentDirection, targetDirection, ref refVelocityDirectionChangingHardness, data.directionChangingSoftness.Value * data.airDirectionDecay.Value * Time.deltaTime);
}
newDirection = Vector3.Slerp(resolvedMovement.moveDirection.World, newDirection, controls.rawMoveInput.magnitude);
// Commit the new direction
resolvedMovement.moveDirection.World = newDirection;
}
// Update the speed, called every frame
private void UpdateCurrentSpeed(){
// ""Smooth"" the speed
float smoothedSpeed;
if (resolvedMovement.moveDirection.Local.magnitude < controls.rawMoveInput.magnitude) {
smoothedSpeed = Mathf.MoveTowards(resolvedMovement.moveSpeed, data.moveSpeed.Value, data.acceleration.Value * Time.deltaTime);
} else {
smoothedSpeed = Mathf.MoveTowards(resolvedMovement.moveSpeed, 0f, data.deacceleration.Value * Time.deltaTime);
}
// Commit the speed
resolvedMovement.moveSpeed = smoothedSpeed;
DebugOverlayDrawer.ChangeValue("Movement", "Resolved Speed", resolvedMovement.moveSpeed);
}
// Update the gravity, called every frame
// NOTE: most gravity interactions, like when grounded or not, is now handled by the state machine
private void UpdateCurrentGravity(){
// Accelerate gravity
if (!controller.isGrounded){
resolvedMovement.gravity -= data.gravityAcceleration.Value * Time.deltaTime;
}
// Create the final gravity value
float gravityMoveDirection = Physics.gravity.y * resolvedMovement.gravity;
}
// Update the rotation, called every frame
private void UpdateCurrentRotation(){
// Get input value
Vector3 inputMovement = new Vector3(controls.rawMoveInput.x, 0f, controls.rawMoveInput.y);
Quaternion targetRotation = Quaternion.identity;
// Switch the desired rotation based on current movement setting
switch (data.facingDirection.Value) {
// Just look at target
case PlayerFacingDirection.TowardsTarget:
// Look directly at the target
if (lockOnManager.mainTarget == null) {
Debug.LogError("Trying to rotate towards a target but there is no target. Not setting a rotation");
targetRotation = transform.rotation;
break;
}
targetRotation = 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){
targetRotation = Quaternion.LookRotation(resolvedMovement.moveDirection.RawWorld.ToVector3(), Vector3.up);
}
break;
case PlayerFacingDirection.MatchInput:
// Look towards the input direction- similar to Momentum but snappier
if (controls.rawMoveInput.magnitude < 0.05f) { break; }
targetRotation = Camera.main.transform.rotation * Quaternion.LookRotation(inputMovement);
break;
case PlayerFacingDirection.MatchCamera:
// Look the same direction as the camera
targetRotation = Quaternion.Euler(Camera.main.transform.rotation.eulerAngles.Flatten(0, null, 0));
break;
case PlayerFacingDirection.Static:
// Don't change
targetRotation = resolvedMovement.rotation;
break;
}
DebugOverlayDrawer.ChangeValue("Rotation", "Target Rotation", targetRotation.eulerAngles);
// Add the current input into the created rotation
if (inputMovement.magnitude > .05) {
resolvedMovement.rotation = targetRotation;
} else if (data.facingDirection.Value == PlayerFacingDirection.MatchCamera) {
resolvedMovement.rotation = targetRotation;
}
// Apply rotation to the character
transform.rotation = Quaternion.Slerp(transform.rotation, resolvedMovement.rotation, data.rotationSpeed.Value * Time.deltaTime).Flatten(0, null, 0);
}
// Custom move from input
private void DoMovement(Vector2 moveDir, float gravDir, float speed, float gravityScale){
// Seperate the different move directions. Additonal becomes it's own because it needs to be added independently of the other two
Vector2 moveXZDir = moveDir;
float moveYDir = gravDir;
// Add their related speeds
moveXZDir *= speed * Time.deltaTime;
moveYDir *= data.gravityScale.Value * Time.deltaTime;
// Construct the direction and move
Vector3 finalDir = new Vector3(moveXZDir.x, moveYDir, moveXZDir.y);
controller.Move(finalDir);
}
// 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);
}
}
public void SetNewDirection(Vector2 value, float relativity, bool absolute, Vector2 relativeTo = default, bool relativeToRotation = true){ // new
Vector2 relativeValue;
if (relativeToRotation){
relativeValue = (Quaternion.LookRotation(relativeTo.ToVector3()) * value.ToVector3()).ToVector2();
} else {
relativeValue = relativeTo + value;
}
if (absolute){
resolvedMovement.moveDirection.World = Vector2.Lerp(resolvedMovement.moveDirection.World, relativeValue, relativity);
} else {
resolvedMovement.moveDirection.World = Vector2.Lerp(resolvedMovement.moveDirection.World, resolvedMovement.moveDirection.World + relativeValue, relativity);
}
Debug.Log(resolvedMovement.moveDirection.World);
}
public void SetNewRawDirection(Vector2 value, float relativity, bool absolute, Vector2 relativeTo = default){ // new
Vector2 relativeValue = relativeTo + value;
if (absolute){
resolvedMovement.moveDirection.RawWorld = Vector2.Lerp(resolvedMovement.moveDirection.RawWorld, relativeValue, relativity);
} else {
resolvedMovement.moveDirection.RawWorld = Vector2.Lerp(resolvedMovement.moveDirection.RawWorld, resolvedMovement.moveDirection.RawWorld + relativeValue, relativity);
}
}
public void SetNewSpeed(float value, float relativity, bool absolute, float relativeTo = Mathf.Infinity){ // new
if (absolute){
resolvedMovement.moveSpeed = Mathf.Lerp(resolvedMovement.moveSpeed, value, relativity);
} else {
resolvedMovement.moveSpeed = Mathf.Lerp(resolvedMovement.moveSpeed, resolvedMovement.moveSpeed + value, relativity);
}
}
public void SetNewRotation(Vector3 value, float relativity, bool absolute, Vector3 relativeTo = default){ // new
Quaternion valueAsQuaternion = Quaternion.LookRotation(value);
if (relativeTo != default) {
valueAsQuaternion = Quaternion.LookRotation(relativeTo) * valueAsQuaternion;
}
if (absolute){
resolvedMovement.rotation = Quaternion.Lerp(resolvedMovement.rotation, valueAsQuaternion, relativity);
} else {
resolvedMovement.rotation = Quaternion.Lerp(resolvedMovement.rotation, resolvedMovement.rotation * valueAsQuaternion, relativity);
}
}
public void SetSpecifiedRotation(Vector3 inputRotation){
specifiedRotation = inputRotation;
}
public Vector2 GetResolvedDirection(){
return resolvedMovement.moveDirection.World;
}
public Vector3 GetResolvedDirectionVector3(){
return resolvedMovement.moveDirection.World.ToVector3();
}
public float GetResolvedSpeed(){
return resolvedMovement.moveSpeed;
}
public float GetResolvedGravity(){
return resolvedMovement.gravity;
}
public Quaternion GetResolvedRotation(){
return resolvedMovement.rotation;
}
[Button("Initialize Settings", ButtonHeight = 30), PropertySpace(10,5 )]
void InitAllSettings(){
var settingsList = data.GetAllSettings();
foreach (IResettableSettingValue value in settingsList) {
value.Initialize();
value.Verify();
}
}
void SmoothAllSettings(){
var settingsList = data.GetAllSettings();
foreach (IResettableSettingValue value in settingsList) {
value.SmoothAndEase();
}
}
public void AddToCurrentDirection(Vector3 inputDirection, float power){ // Old
Debug.LogError("Using an old movement command! Switch to one of the new alternatives!");
}
public void SmoothToSpeed(float desiredSpeed, float smoothing){ // Old
Debug.LogError("Using an old movement command! Switch to one of the new alternatives!");
}
public void SetNewDirection(Vector3 inputDirection){ // NOTE: If smoothing desired add a default bool for smoothing maybe? // Old
Debug.LogError("Using an old movement command! Switch to one of the new alternatives!");
}
public void SetNewGravity(float value){
Debug.LogError("Using an old movement command! Switch to one of the new alternatives!");
}
public void OverwriteDirectionFromInput(Vector2 value, float priority, float speed = Mathf.Infinity){ // Old
Debug.LogError("Using an old movement command! Switch to one of the new alternatives!");
}
}
}