also contains changes to the animation controller for all the currently made animations, events for the singular attack animation, corresponding methods, and graph changes to utilize them
400 lines
17 KiB
C#
400 lines
17 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using Reset.Core.Tools;
|
|
using Sirenix.OdinInspector;
|
|
using Unity.Netcode;
|
|
|
|
namespace Reset.Units{
|
|
public class UnitMovementHandler : UnitComponent {
|
|
[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 IUnitDirectionProvider directionProvider;
|
|
private IUnitTargetProvider targetProvider;
|
|
|
|
// 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>();
|
|
directionProvider = GetComponent<IUnitDirectionProvider>();
|
|
targetProvider = GetComponent<IUnitTargetProvider>();
|
|
|
|
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(directionProvider.Direction.x, directionProvider.Direction.y);
|
|
|
|
// Rotate input by camera rotation (instead of rotating the output direction by camera rotation)
|
|
if (GetComponent<Player>()) {
|
|
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, directionProvider.Direction.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 < directionProvider.Direction.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(directionProvider.Direction.x, 0f, directionProvider.Direction.y);
|
|
|
|
Quaternion targetRotation = Quaternion.identity;
|
|
// Switch the desired rotation based on current movement setting
|
|
switch (data.facingDirection.Value) {
|
|
// Just look at target
|
|
case UnitFacingDirection.TowardsTarget:
|
|
// Look directly at the target
|
|
if (targetProvider.UnitTarget == null) {
|
|
Debug.LogError("Trying to rotate towards a target but there is no target. Forcing rotation to Static and continuing");
|
|
data.facingDirection.Value = UnitFacingDirection.Static;
|
|
data.facingDirection.currentValue = UnitFacingDirection.Static;
|
|
|
|
targetRotation = transform.rotation;
|
|
break;
|
|
}
|
|
|
|
targetRotation = Quaternion.LookRotation(transform.position.DirectionTo(targetProvider.UnitTarget.transform.position));
|
|
break;
|
|
case UnitFacingDirection.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 UnitFacingDirection.MatchInput:
|
|
// Look towards the input direction- similar to Momentum but snappier
|
|
if (directionProvider.Direction.magnitude < 0.05f) { break; }
|
|
|
|
targetRotation = Camera.main.transform.rotation * Quaternion.LookRotation(inputMovement);
|
|
break;
|
|
case UnitFacingDirection.MatchCamera:
|
|
// Look the same direction as the camera
|
|
targetRotation = Quaternion.Euler(Camera.main.transform.rotation.eulerAngles.Flatten(0, null, 0));
|
|
break;
|
|
case UnitFacingDirection.Static:
|
|
// Don't change
|
|
targetRotation = resolvedMovement.rotation;
|
|
break;
|
|
}
|
|
|
|
DebugOverlayDrawer.ChangeValue("Rotation", "Target Rotation", targetRotation.eulerAngles);
|
|
|
|
// Add the current input into the created rotation
|
|
if (data.facingDirection.Value == UnitFacingDirection.MatchCamera || data.facingDirection.Value == UnitFacingDirection.TowardsTarget) {
|
|
resolvedMovement.rotation = targetRotation;
|
|
} else if (directionProvider.Direction.sqrMagnitude > .1){
|
|
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
|
|
float newGravity;
|
|
|
|
if (absolute){
|
|
newGravity = Mathf.Lerp(resolvedMovement.gravity, value, relativity);
|
|
} else {
|
|
newGravity = Mathf.Lerp(resolvedMovement.gravity, resolvedMovement.gravity + value, relativity);
|
|
}
|
|
|
|
if (Unit.UnitIsNetworked() && !Unit.UnitIsLocal()) {
|
|
SetNewGravityRpc(newGravity);
|
|
} else {
|
|
resolvedMovement.gravity = newGravity;
|
|
}
|
|
}
|
|
|
|
[Rpc(SendTo.Owner)]
|
|
public void SetNewGravityRpc(float value){
|
|
resolvedMovement.gravity = value;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
Vector2 newValue;
|
|
|
|
if (absolute){
|
|
newValue = Vector2.Lerp(resolvedMovement.moveDirection.World, relativeValue, relativity);
|
|
} else {
|
|
newValue = Vector2.Lerp(resolvedMovement.moveDirection.World, resolvedMovement.moveDirection.World + relativeValue, relativity);
|
|
}
|
|
|
|
if (Unit.UnitIsNetworked() && !Unit.UnitIsLocal()) {
|
|
SetNewDirectionRpc(newValue);
|
|
} else {
|
|
resolvedMovement.moveDirection.World = newValue;
|
|
}
|
|
}
|
|
|
|
[Rpc(SendTo.Owner)]
|
|
public void SetNewDirectionRpc(Vector2 value){
|
|
resolvedMovement.moveDirection.World = value;
|
|
}
|
|
|
|
public void SetNewRawDirection(Vector2 value, float relativity, bool absolute, Vector2 relativeTo = default){ // new
|
|
Vector2 relativeValue = relativeTo + value;
|
|
Vector2 newValue;
|
|
|
|
if (absolute){
|
|
newValue = Vector2.Lerp(resolvedMovement.moveDirection.RawWorld, relativeValue, relativity);
|
|
} else {
|
|
newValue = Vector2.Lerp(resolvedMovement.moveDirection.RawWorld, resolvedMovement.moveDirection.RawWorld + relativeValue, relativity);
|
|
}
|
|
|
|
if (Unit.UnitIsNetworked() && !Unit.UnitIsLocal()) {
|
|
SetNewRawDirectionRpc(newValue);
|
|
} else {
|
|
resolvedMovement.moveDirection.RawWorld = newValue;
|
|
}
|
|
}
|
|
|
|
[Rpc(SendTo.Owner)]
|
|
public void SetNewRawDirectionRpc(Vector2 value){
|
|
resolvedMovement.moveDirection.RawWorld = value;
|
|
}
|
|
|
|
public void SetNewSpeed(float value, float relativity, bool absolute, float relativeTo = Mathf.Infinity){ // new
|
|
float newSpeed;
|
|
|
|
if (absolute){
|
|
newSpeed = Mathf.Lerp(resolvedMovement.moveSpeed, value, relativity);
|
|
} else {
|
|
newSpeed = Mathf.Lerp(resolvedMovement.moveSpeed, resolvedMovement.moveSpeed + value, relativity);
|
|
}
|
|
|
|
if (Unit.UnitIsNetworked() && !Unit.UnitIsLocal()) {
|
|
SetNewSpeedRpc(newSpeed);
|
|
} else {
|
|
resolvedMovement.moveSpeed = newSpeed;
|
|
}
|
|
}
|
|
|
|
[Rpc(SendTo.Owner)]
|
|
public void SetNewSpeedRpc(float value){
|
|
resolvedMovement.moveSpeed = value;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
Quaternion newRotation;
|
|
|
|
if (absolute){
|
|
newRotation = Quaternion.Lerp(resolvedMovement.rotation, valueAsQuaternion, relativity);
|
|
} else {
|
|
newRotation = Quaternion.Lerp(resolvedMovement.rotation, resolvedMovement.rotation * valueAsQuaternion, relativity);
|
|
}
|
|
|
|
if (Unit.UnitIsNetworked() && !Unit.UnitIsLocal()) {
|
|
SetNewRotationRpc(newRotation);
|
|
} else {
|
|
resolvedMovement.rotation = newRotation;
|
|
}
|
|
}
|
|
|
|
[Rpc(SendTo.Owner)]
|
|
public void SetNewRotationRpc(Quaternion value){
|
|
resolvedMovement.rotation = value;
|
|
}
|
|
|
|
public void SetSpecifiedRotation(Vector3 inputRotation){
|
|
specifiedRotation = inputRotation;
|
|
}
|
|
|
|
public Vector2 GetResolvedDirection(){
|
|
return resolvedMovement.moveDirection.World;
|
|
}
|
|
|
|
public Vector2 GetResolvedDirectionLocal(){
|
|
return resolvedMovement.moveDirection.Local;
|
|
}
|
|
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!");
|
|
}
|
|
|
|
}
|
|
}
|
|
|