init
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
using RPGCore.StatusEffect.ObjectModules.StatusObjectModule;
|
||||
using RPGCoreCommon.Settings;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.Movement
|
||||
{
|
||||
[CustomSettings("RPG Core/Movement")]
|
||||
public class MovementSettings : CustomSettingsSO
|
||||
{
|
||||
[Header("Statuses Definitions")]
|
||||
public StatusDefinitionSO runStatusDefinitionSO;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed5576b120bd480796129f99e38965a5
|
||||
timeCreated: 1772991814
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c3413a6703346cfb369fc7a8959562f
|
||||
timeCreated: 1772991398
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7dba86a6b4be4134866887f89495c479
|
||||
timeCreated: 1767463510
|
||||
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using RPGCore.Movement.ObjectModules.UnitMovement;
|
||||
using RPGCore.Movement.ObjectModules.UnitMovement.Events;
|
||||
using RPGCore.Core;
|
||||
using RPGCore.Core.Objects;
|
||||
using RPGCore.ObjectModules.EventObjectModule;
|
||||
using RPGCoreCommon.Helpers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.Movement.ObjectModules.UnitAnimator
|
||||
{
|
||||
[Serializable]
|
||||
[RequireComponent(typeof(UnitObject))]
|
||||
[RequireComponent(typeof(UnitMovementModule))]
|
||||
public class UnitAnimatorModule : ObjectModule<UnitObject>
|
||||
{
|
||||
// CONSTANT (defined in animator)
|
||||
private static readonly int BaseLayer = 0;
|
||||
private static readonly int VelocityMagnitude = Animator.StringToHash("VELOCITY_MAGNITUDE");
|
||||
private static readonly int DirectionX = Animator.StringToHash("DIRECTION_X");
|
||||
private static readonly int DirectionZ = Animator.StringToHash("DIRECTION_Z");
|
||||
private static readonly int MoveAnimSpeed = Animator.StringToHash("MOVE_ANIM_SPEED");
|
||||
private static readonly int JumpTrigger = Animator.StringToHash("JUMP_TRIGGER");
|
||||
private static readonly int IsOnGround = Animator.StringToHash("IS_ON_GROUND");
|
||||
|
||||
// SERIALIZED
|
||||
[SerializeField] private Animator _animator;
|
||||
|
||||
[Header("Movement animation speed - used for animation speed scaling")]
|
||||
[SerializeField] private float _sprintSpeed = 6f;
|
||||
[SerializeField] private float _runSpeed = 3f;
|
||||
|
||||
[Header("Animation spine helper")]
|
||||
[SerializeField] private bool _spineHelper;
|
||||
[SerializeField] private Transform _hips;
|
||||
[SerializeField] private Transform _spine;
|
||||
[SerializeField] private Quaternion _spineRotation;
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
if (!_animator)
|
||||
{
|
||||
_animator = parent.GetComponentInChildren<Animator>();
|
||||
UnityEditor.EditorUtility.SetDirty(parent.gameObject);
|
||||
}
|
||||
|
||||
if (_animator)
|
||||
{
|
||||
_hips = _animator.GetBoneTransform(HumanBodyBones.Hips);
|
||||
_spine = _animator.GetBoneTransform(HumanBodyBones.Spine);
|
||||
_spineRotation = _spine.localRotation;
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
parent.events.Register<JumpEvent>(_ => _animator.SetTrigger(JumpTrigger));
|
||||
parent.events.Register<LandEvent>(_ => _animator.SetBool(IsOnGround, true));
|
||||
parent.events.Register<FallEvent>(_ => _animator.SetBool(IsOnGround, false));
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_animator.SetBool(IsOnGround, parent.GetComponent<UnitMovementModule>().isOnGround);
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (_spineHelper) _spine.rotation = _hips.rotation * _spineRotation;
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
HandleMoveAnimation();
|
||||
}
|
||||
|
||||
private void HandleMoveAnimation()
|
||||
{
|
||||
var unitMovement = parent.GetComponent<UnitMovementModule>();
|
||||
if (!unitMovement.isMoving)
|
||||
{
|
||||
_animator.SetFloat(VelocityMagnitude, 0f);
|
||||
_animator.SetFloat(DirectionX, 0f);
|
||||
_animator.SetFloat(DirectionZ, 0f);
|
||||
_animator.SetFloat(MoveAnimSpeed, 1f);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: tutaj powinno jakoś fajnie brać prędkość TYLKO od ruchu, a nie całego velocity
|
||||
var relVelocity = parent.transform.InverseTransformDirection(parent.rigidbody.linearVelocity.SetY(0));
|
||||
var moveAnimSpeed = relVelocity.magnitude < _runSpeed ? relVelocity.magnitude / _runSpeed
|
||||
: relVelocity.magnitude > _sprintSpeed ? relVelocity.magnitude / _sprintSpeed
|
||||
: 1f;
|
||||
var moveRelativeDirection = relVelocity.normalized;
|
||||
|
||||
_animator.SetFloat(VelocityMagnitude, relVelocity.magnitude);
|
||||
_animator.SetFloat(DirectionX, moveRelativeDirection.x);
|
||||
_animator.SetFloat(DirectionZ, moveRelativeDirection.z);
|
||||
_animator.SetFloat(MoveAnimSpeed, moveAnimSpeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2196f6792fb64519a9c02abec4cad026
|
||||
timeCreated: 1767463517
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a57c1cd622649c0ab87034a023c1ae6
|
||||
timeCreated: 1766851215
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46cdb0a558204291b86aab452972e23e
|
||||
timeCreated: 1766851215
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using RPGCore.Movement.ObjectModules.UnitMovement.Events;
|
||||
using RPGCore.ObjectModules.ActionObjectModule;
|
||||
using RPGCore.ObjectModules.EventObjectModule;
|
||||
using RPGCore.StatusEffect.ObjectModules.StatusObjectModule;
|
||||
using RPGCoreCommon.Settings;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.Movement.ObjectModules.UnitMovement.Actions
|
||||
{
|
||||
public class DirectionMoveAction : BaseActionParallel
|
||||
{
|
||||
private readonly Func<Vector3> _directionGetter;
|
||||
|
||||
private StatusDefinitionSO _runStatusDefinitionSO;
|
||||
private StatusModule _unitStatus;
|
||||
private UnitMovementModule _unitMovement;
|
||||
|
||||
public DirectionMoveAction(Func<Vector3> directionGetter)
|
||||
{
|
||||
_directionGetter = directionGetter;
|
||||
}
|
||||
|
||||
public override void CanDoIt()
|
||||
{
|
||||
Check(
|
||||
unit.GetComponent<StatusModule>().IsControllable(),
|
||||
UnitIsBusyMessage);
|
||||
}
|
||||
|
||||
protected override void OnDoIt()
|
||||
{
|
||||
_runStatusDefinitionSO = SettingsManager.Get<MovementSettings>().runStatusDefinitionSO;
|
||||
_unitStatus = unit.GetComponent<StatusModule>();
|
||||
_unitMovement = unit.GetComponent<UnitMovementModule>();
|
||||
|
||||
var moveStartEvent = new MoveStartEvent{ unit = unit };
|
||||
|
||||
unit.events.InvokeBefore(moveStartEvent);
|
||||
Check(!moveStartEvent.isPrevented, ActionWasPreventedMessage);
|
||||
unit.events.InvokeAfter(moveStartEvent);
|
||||
|
||||
unit.StartCoroutine(MoveCoroutine());
|
||||
}
|
||||
|
||||
private IEnumerator MoveCoroutine()
|
||||
{
|
||||
while (state == ActionState.Running)
|
||||
{
|
||||
_unitMovement.moveInput = _directionGetter();
|
||||
yield return new WaitForFixedUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnEndIt()
|
||||
{
|
||||
_unitMovement.moveInput = Vector2.zero;
|
||||
_unitStatus.Remove(_runStatusDefinitionSO);
|
||||
unit.events.Invoke(new MoveEndEvent{ unit = unit });
|
||||
}
|
||||
|
||||
protected override void OnCancelIt()
|
||||
{
|
||||
OnEndIt();
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e1c38778027246d1afc2fa822bc9826b
|
||||
timeCreated: 1766851215
|
||||
@@ -0,0 +1,36 @@
|
||||
using RPGCore.Movement.ObjectModules.UnitMovement.Events;
|
||||
using RPGCore.ObjectModules.ActionObjectModule;
|
||||
using RPGCore.ObjectModules.EventObjectModule;
|
||||
using RPGCore.StatusEffect.ObjectModules.StatusObjectModule;
|
||||
using RPGCoreCommon.Helpers;
|
||||
|
||||
namespace RPGCore.Movement.ObjectModules.UnitMovement.Actions
|
||||
{
|
||||
public class JumpAction : BaseActionParallel
|
||||
{
|
||||
public override void CanDoIt()
|
||||
{
|
||||
Check(
|
||||
unit.GetComponent<StatusModule>().IsControllable(),
|
||||
UnitIsBusyMessage);
|
||||
Check(
|
||||
unit.GetComponent<UnitMovementModule>().isOnGround,
|
||||
"Unit is not on ground.");
|
||||
}
|
||||
|
||||
protected override void OnDoIt()
|
||||
{
|
||||
unit.rigidbody.linearVelocity = unit.rigidbody.linearVelocity.SetY(unit.GetComponent<UnitMovementModule>().jumpPower);
|
||||
unit.events.Invoke(new JumpEvent { unit = unit });
|
||||
EndIt();
|
||||
}
|
||||
|
||||
protected override void OnEndIt()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnCancelIt()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc5b6c0c9dbe400ba3fb4f085bd3cc28
|
||||
timeCreated: 1767009824
|
||||
@@ -0,0 +1,82 @@
|
||||
using System.Collections;
|
||||
using RPGCore.Movement.ObjectModules.UnitMovement.Events;
|
||||
using RPGCore.ObjectModules.ActionObjectModule;
|
||||
using RPGCore.ObjectModules.EventObjectModule;
|
||||
using RPGCore.StatusEffect.ObjectModules.StatusObjectModule;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
namespace RPGCore.Movement.ObjectModules.UnitMovement.Actions
|
||||
{
|
||||
public class PointMoveAction : BaseActionParallel
|
||||
{
|
||||
private readonly Vector3 _destination;
|
||||
|
||||
private UnitMovementModule _unitMovement;
|
||||
|
||||
public PointMoveAction(Vector3 destination)
|
||||
{
|
||||
_destination = destination;
|
||||
}
|
||||
|
||||
public override void CanDoIt()
|
||||
{
|
||||
Check(
|
||||
unit.GetComponent<StatusModule>().IsControllable(),
|
||||
UnitIsBusyMessage);
|
||||
}
|
||||
|
||||
protected override void OnDoIt()
|
||||
{
|
||||
_unitMovement = unit.GetComponent<UnitMovementModule>();
|
||||
|
||||
var moveStartEvent = new MoveStartEvent{ unit = unit };
|
||||
|
||||
unit.events.InvokeBefore(moveStartEvent);
|
||||
Check(!moveStartEvent.isPrevented, ActionWasPreventedMessage);
|
||||
unit.events.InvokeAfter(moveStartEvent);
|
||||
|
||||
unit.StartCoroutine(MovePoint_Coroutine());
|
||||
}
|
||||
|
||||
private IEnumerator MovePoint_Coroutine()
|
||||
{
|
||||
var agent = unit.GetComponent<NavMeshAgent>();
|
||||
agent.SetDestination(_destination);
|
||||
|
||||
var lastPosition = agent.transform.position;
|
||||
|
||||
while (state == ActionState.Running)
|
||||
{
|
||||
var speed = agent.speed = _unitMovement.moveMaxSpeed;
|
||||
if (agent.remainingDistance <= speed * Time.fixedDeltaTime)
|
||||
{
|
||||
EndIt();
|
||||
yield break;
|
||||
};
|
||||
|
||||
yield return new WaitForFixedUpdate();
|
||||
// unit.GetModule<EventModule>().Invoke(new MoveEvent
|
||||
// {
|
||||
// unit = unit,
|
||||
// lastPosition = lastPosition,
|
||||
// currentPosition = unit.transform.position
|
||||
// });
|
||||
//
|
||||
// lastPosition = unit.transform.position;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnEndIt()
|
||||
{
|
||||
unit.GetComponent<NavMeshAgent>().isStopped = true;
|
||||
unit.GetComponent<NavMeshAgent>().enabled = false;
|
||||
unit.events.Invoke(new MoveEndEvent{ unit = unit });
|
||||
}
|
||||
|
||||
protected override void OnCancelIt()
|
||||
{
|
||||
OnEndIt();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f3f6bf1842e54f00a5a385f9e2e4ba89
|
||||
timeCreated: 1766851215
|
||||
@@ -0,0 +1,121 @@
|
||||
using System.Collections;
|
||||
using RPGCore.ObjectModules.ActionObjectModule;
|
||||
using RPGCore.StatusEffect.ObjectModules.StatusObjectModule;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.Movement.ObjectModules.UnitMovement.Actions
|
||||
{
|
||||
public class RotateAction : BaseActionParallel
|
||||
{
|
||||
private Coroutine _rotateCoroutine;
|
||||
|
||||
private readonly bool _isEndless;
|
||||
private float _targetYaw;
|
||||
private readonly float _rotationSpeed;
|
||||
private readonly bool _isForced;
|
||||
|
||||
public RotateAction(float rotationSpeed = 0)
|
||||
{
|
||||
_isEndless = true;
|
||||
_rotationSpeed = rotationSpeed;
|
||||
}
|
||||
|
||||
public RotateAction(float targetYaw, float rotationSpeed = 0, bool isForced = false)
|
||||
{
|
||||
_targetYaw = targetYaw;
|
||||
_rotationSpeed = rotationSpeed;
|
||||
_isForced = isForced;
|
||||
}
|
||||
|
||||
public RotateAction(Vector3 lookAt, float rotationSpeed = 0, bool isForced = false)
|
||||
{
|
||||
_targetYaw = Quaternion.LookRotation(lookAt - unit.transform.position).eulerAngles.y;
|
||||
_rotationSpeed = rotationSpeed;
|
||||
_isForced = isForced;
|
||||
}
|
||||
|
||||
public void SetYaw(float targetYaw)
|
||||
{
|
||||
if (!_isEndless)
|
||||
{
|
||||
Debug.LogWarning($"Usage {nameof(SetYaw)} available only when rotating is endless.");
|
||||
return;
|
||||
}
|
||||
|
||||
_targetYaw = targetYaw;
|
||||
}
|
||||
|
||||
public override bool IsInRange() => true;
|
||||
|
||||
public override void CanDoIt()
|
||||
{
|
||||
Check(
|
||||
_isForced || unit.GetComponent<StatusModule>().IsControllable(),
|
||||
UnitIsBusyMessage);
|
||||
}
|
||||
|
||||
protected override void OnDoIt()
|
||||
{
|
||||
BeforePerform();
|
||||
|
||||
// Instant rotation
|
||||
if (_rotationSpeed <= 0)
|
||||
{
|
||||
Rotate(_targetYaw);
|
||||
|
||||
AfterPerform();
|
||||
EndIt();
|
||||
return;
|
||||
}
|
||||
|
||||
// Endless rotation
|
||||
if (_isEndless)
|
||||
{
|
||||
_rotateCoroutine = unit.StartCoroutine(RotationEndlessCoroutine());
|
||||
return;
|
||||
}
|
||||
|
||||
// Simple rotation to given yaw
|
||||
_rotateCoroutine = unit.StartCoroutine(RotationCoroutine());
|
||||
}
|
||||
|
||||
private IEnumerator RotationCoroutine()
|
||||
{
|
||||
while (Mathf.Approximately(unit.transform.rotation.eulerAngles.y, _targetYaw) == false)
|
||||
{
|
||||
var deltaYaw = Mathf.DeltaAngle(unit.rigidbody.rotation.eulerAngles.y, _targetYaw);
|
||||
var deltaYawClamped = Mathf.Sign(deltaYaw) * Mathf.Clamp(Mathf.Abs(deltaYaw), 0, Time.fixedDeltaTime * _rotationSpeed);
|
||||
Rotate(unit.rigidbody.rotation.eulerAngles.y + deltaYawClamped);
|
||||
yield return new WaitForFixedUpdate();
|
||||
}
|
||||
|
||||
AfterPerform();
|
||||
EndIt();
|
||||
}
|
||||
|
||||
private IEnumerator RotationEndlessCoroutine()
|
||||
{
|
||||
while (state is ActionState.Running)
|
||||
{
|
||||
var deltaYaw = Mathf.DeltaAngle(unit.rigidbody.rotation.eulerAngles.y, _targetYaw);
|
||||
var deltaYawClamped = Mathf.Sign(deltaYaw) * Mathf.Clamp(Mathf.Abs(deltaYaw), 0, Time.fixedDeltaTime * _rotationSpeed);
|
||||
Rotate(unit.rigidbody.rotation.eulerAngles.y + deltaYawClamped);
|
||||
yield return new WaitForFixedUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private void Rotate(float yaw)
|
||||
{
|
||||
var targetRotation = unit.transform.rotation.eulerAngles;
|
||||
targetRotation.y = yaw;
|
||||
unit.rigidbody.MoveRotation(Quaternion.Euler(targetRotation));
|
||||
}
|
||||
|
||||
protected override void OnEndIt() { }
|
||||
|
||||
protected override void OnCancelIt()
|
||||
{
|
||||
if (_rotateCoroutine != null) unit.StopCoroutine(_rotateCoroutine);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a1273895fb44296a471ede97605bd81
|
||||
timeCreated: 1764945728
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 841fa5826a24421b852fadaa4260f889
|
||||
timeCreated: 1767011733
|
||||
@@ -0,0 +1,10 @@
|
||||
using RPGCore.Core.Objects;
|
||||
using RPGCore.ObjectModules.EventObjectModule;
|
||||
|
||||
namespace RPGCore.Movement.ObjectModules.UnitMovement.Events
|
||||
{
|
||||
public class FallEvent : BaseEvent<UnitObject>
|
||||
{
|
||||
public UnitObject unit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b7fc3e61e8846d48c8868b8253e295d
|
||||
timeCreated: 1767701919
|
||||
@@ -0,0 +1,10 @@
|
||||
using RPGCore.Core.Objects;
|
||||
using RPGCore.ObjectModules.EventObjectModule;
|
||||
|
||||
namespace RPGCore.Movement.ObjectModules.UnitMovement.Events
|
||||
{
|
||||
public class JumpEvent : BaseEvent<UnitObject>
|
||||
{
|
||||
public UnitObject unit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc29c81dd0c64023aa5d8ba1608a5109
|
||||
timeCreated: 1767011739
|
||||
@@ -0,0 +1,10 @@
|
||||
using RPGCore.Core.Objects;
|
||||
using RPGCore.ObjectModules.EventObjectModule;
|
||||
|
||||
namespace RPGCore.Movement.ObjectModules.UnitMovement.Events
|
||||
{
|
||||
public class LandEvent : BaseEvent<UnitObject>
|
||||
{
|
||||
public UnitObject unit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 736b6bd7402549f3954c98dd4b77bd1a
|
||||
timeCreated: 1767693252
|
||||
@@ -0,0 +1,10 @@
|
||||
using RPGCore.Core.Objects;
|
||||
using RPGCore.ObjectModules.EventObjectModule;
|
||||
|
||||
namespace RPGCore.Movement.ObjectModules.UnitMovement.Events
|
||||
{
|
||||
public class MoveEndEvent : BaseEvent<UnitObject>
|
||||
{
|
||||
public UnitObject unit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af2ba162e26047babe441e19fa6a4919
|
||||
timeCreated: 1765976043
|
||||
@@ -0,0 +1,10 @@
|
||||
using RPGCore.Core.Objects;
|
||||
using RPGCore.ObjectModules.EventObjectModule;
|
||||
|
||||
namespace RPGCore.Movement.ObjectModules.UnitMovement.Events
|
||||
{
|
||||
public class MoveStartEvent : BasePreventableEvent<UnitObject>
|
||||
{
|
||||
public UnitObject unit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95e49f3e87aa4cec98bd652309ba90c9
|
||||
timeCreated: 1765976027
|
||||
@@ -0,0 +1,296 @@
|
||||
using System;
|
||||
using RPGCore.Movement.ObjectModules.UnitMovement.Events;
|
||||
using RPGCore.Movement.ObjectModules.UnitMovement.Actions;
|
||||
using RPGCore.Core;
|
||||
using RPGCore.Core.Objects;
|
||||
using RPGCoreCommon.Helpers;
|
||||
using RPGCoreCommon.Settings;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
namespace RPGCore.Movement.ObjectModules.UnitMovement
|
||||
{
|
||||
[Serializable]
|
||||
[RequireComponent(typeof(UnitObject))]
|
||||
[RequireComponent(typeof(CapsuleCollider))]
|
||||
public class UnitMovementModule : ObjectModule<UnitObject>
|
||||
{
|
||||
// SERIALIZED
|
||||
[Header("General")]
|
||||
[SerializeField] public float skin = 0.03f;
|
||||
[SerializeField] public float groundDistance = 0.1f;
|
||||
[SerializeField] public float groundFriction = 5f;
|
||||
[Header("On Ground")]
|
||||
[SerializeField] public float moveMaxSpeed = 3f;
|
||||
[SerializeField] public float moveMaxSlope = 45f;
|
||||
[SerializeField] public float groundAcceleration = 15f;
|
||||
[SerializeField] public float sprintSpeedMultiplier = 2f;
|
||||
[SerializeField] public float sprintMaxDeviation = 67.5f;
|
||||
[SerializeField] public float jumpPower = 5f;
|
||||
[Header("In Air")]
|
||||
[SerializeField] public float airMaxSpeed = 3f;
|
||||
[SerializeField] public float airAcceleration = 1.5f;
|
||||
[SerializeField] public float gravity = 9.81f;
|
||||
[SerializeField] public float fallMaxSpeed = 20f;
|
||||
[Header("(optional) Rotation")]
|
||||
[SerializeField] public float rotationSpeed = 180f;
|
||||
[SerializeField] public UnitMovementRotateType rotateType;
|
||||
|
||||
// RUNTIME
|
||||
private RotateAction _rotateAction;
|
||||
|
||||
public RaycastHit groundHit { get; private set; }
|
||||
public float groundSteepness { get; private set; }
|
||||
public float groundAngle { get; private set; }
|
||||
public bool isOnGround { get; private set; }
|
||||
public float rotateYaw { get; set; }
|
||||
public Vector3 accelerationVector { get; private set; }
|
||||
public Vector3 moveInput { get; set; }
|
||||
public bool isMoving { get; private set; }
|
||||
public bool isSprinting { get; set; }
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
var agent = parent.GetComponent<NavMeshAgent>();
|
||||
agent.enabled = false;
|
||||
agent.speed = moveMaxSpeed;
|
||||
agent.updateRotation = false;
|
||||
|
||||
parent.events.RegisterAfter<MoveStartEvent>(OnMoveStart);
|
||||
parent.events.Register<MoveEndEvent>(OnMoveEnd);
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
CheckGround();
|
||||
HandleGroundFriction();
|
||||
HandleMovement();
|
||||
HandleGravity();
|
||||
|
||||
HandleRotation();
|
||||
}
|
||||
|
||||
private void HandleMovement()
|
||||
{
|
||||
if (moveInput == Vector3.zero)
|
||||
{
|
||||
isSprinting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: ruch rampą w górę i zaczyna się pozioma podłoga - jeśli velocity.y podobne do accelerationVector.y to wtedy snapping?
|
||||
|
||||
CheckForwardSprint();
|
||||
accelerationVector = moveInput * (GetSpeed() * Time.fixedDeltaTime);
|
||||
MovementOnGroundNormal();
|
||||
MovementOnObstacle();
|
||||
|
||||
AddMoveVectorToVelocity();
|
||||
}
|
||||
|
||||
private void CheckForwardSprint()
|
||||
{
|
||||
var unitYaw = parent.rigidbody.rotation.eulerAngles.y;
|
||||
var moveYaw = Mathf.Atan2(moveInput.x, moveInput.z) * Mathf.Rad2Deg;
|
||||
|
||||
if (Mathf.Abs(Mathf.DeltaAngle(unitYaw, moveYaw)) < sprintMaxDeviation) return;
|
||||
|
||||
isSprinting = false;
|
||||
}
|
||||
|
||||
private float GetSpeed()
|
||||
{
|
||||
var speed = isOnGround ? groundAcceleration : airAcceleration;
|
||||
if (isSprinting) speed *= sprintSpeedMultiplier;
|
||||
return speed;
|
||||
}
|
||||
|
||||
private void MovementOnGroundNormal()
|
||||
{
|
||||
if (groundAngle > moveMaxSlope) return;
|
||||
accelerationVector = Quaternion.FromToRotation(Vector3.up, groundHit.normal) * accelerationVector;
|
||||
}
|
||||
|
||||
private void MovementOnObstacle()
|
||||
{
|
||||
const int limit = 3;
|
||||
|
||||
var currentPosition = parent.rigidbody.position;
|
||||
var tempPosition = currentPosition;
|
||||
var tempDirection = accelerationVector.normalized;
|
||||
var tempDistance = accelerationVector.magnitude;
|
||||
|
||||
for (var i = 1; i <= limit; i++)
|
||||
{
|
||||
var isHit = Physics.CapsuleCast(
|
||||
tempPosition.AddY(parent.unitCollider.radius),
|
||||
tempPosition.AddY(parent.unitCollider.height - parent.unitCollider.radius),
|
||||
parent.unitCollider.radius,
|
||||
tempDirection,
|
||||
out var hit,
|
||||
tempDistance,
|
||||
1 << SettingsManager.Get<CoreSettings>().staticLayer
|
||||
);
|
||||
|
||||
if (!isHit)
|
||||
{
|
||||
// No obstacle found
|
||||
accelerationVector = tempPosition + tempDirection * tempDistance - currentPosition;
|
||||
return;
|
||||
}
|
||||
|
||||
tempPosition += tempDirection * (hit.distance - skin);
|
||||
tempDistance -= hit.distance - skin;
|
||||
|
||||
if (i == limit)
|
||||
{
|
||||
// Checks reached limit - dont check further
|
||||
accelerationVector = tempPosition - currentPosition;
|
||||
return;
|
||||
}
|
||||
|
||||
var planeNormal = hit.normal;
|
||||
if (Vector3.Angle(hit.normal, Vector3.up) > moveMaxSlope) planeNormal = planeNormal.SetY(0).normalized;
|
||||
var projectedVector = Vector3.ProjectOnPlane(tempDirection * tempDistance, planeNormal);
|
||||
|
||||
tempDistance = projectedVector.magnitude;
|
||||
tempDirection = projectedVector.normalized;
|
||||
}
|
||||
|
||||
throw new Exception($"{nameof(MovementOnObstacle)} - this should never be reached!");
|
||||
}
|
||||
|
||||
private void AddMoveVectorToVelocity()
|
||||
{
|
||||
var speed = Vector3.Dot(parent.rigidbody.linearVelocity, accelerationVector.normalized);
|
||||
var maxSpeed = isOnGround ? moveMaxSpeed : airMaxSpeed;
|
||||
if (isSprinting) maxSpeed *= sprintSpeedMultiplier;
|
||||
var possibleSpeed = maxSpeed - speed;
|
||||
|
||||
if (possibleSpeed <= 0) return;
|
||||
|
||||
var moveVectorMultiplier = Mathf.Min(possibleSpeed / accelerationVector.magnitude, 1f);
|
||||
parent.rigidbody.linearVelocity += accelerationVector * moveVectorMultiplier;
|
||||
}
|
||||
|
||||
private void CheckGround()
|
||||
{
|
||||
var groundFound = Physics.SphereCast(
|
||||
parent.transform.position.AddY(parent.unitCollider.radius),
|
||||
parent.unitCollider.radius - skin,
|
||||
Vector3.down,
|
||||
out var hit,
|
||||
skin + groundDistance,
|
||||
1 << SettingsManager.Get<CoreSettings>().staticLayer);
|
||||
groundHit = hit;
|
||||
|
||||
groundAngle = 90f;
|
||||
groundSteepness = 1f;
|
||||
|
||||
if (groundFound)
|
||||
{
|
||||
groundAngle = Vector3.Angle(groundHit.normal, Vector3.up);
|
||||
groundSteepness = 0f;
|
||||
|
||||
if (groundAngle > moveMaxSlope)
|
||||
{
|
||||
groundSteepness = Mathf.InverseLerp(0f, 90f, groundAngle);
|
||||
groundFound = false;
|
||||
}
|
||||
}
|
||||
|
||||
var wasOnGround = isOnGround;
|
||||
isOnGround = groundFound;
|
||||
|
||||
if (wasOnGround && !isOnGround)
|
||||
parent.events.Invoke(new FallEvent { unit = parent });
|
||||
|
||||
if (!wasOnGround && isOnGround)
|
||||
parent.events.Invoke(new LandEvent { unit = parent });
|
||||
}
|
||||
|
||||
private void HandleGravity()
|
||||
{
|
||||
if (groundSteepness <= 0f)
|
||||
{
|
||||
// Ground below - push a little bit towards ground
|
||||
parent.rigidbody.linearVelocity -= groundHit.normal * Time.fixedDeltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Airborne - falling
|
||||
var newVelocity = parent.rigidbody.linearVelocity.AddY(- gravity * Time.fixedDeltaTime);
|
||||
newVelocity.y = Mathf.Max(newVelocity.y, - fallMaxSpeed);
|
||||
parent.rigidbody.linearVelocity = newVelocity;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleGroundFriction()
|
||||
{
|
||||
if (!isOnGround) return;
|
||||
if (groundSteepness >= 0.99f) return;
|
||||
|
||||
var velocity = parent.rigidbody.linearVelocity;
|
||||
velocity -= velocity * (groundFriction * (1 - groundSteepness) * Time.fixedDeltaTime);
|
||||
|
||||
if (Mathf.Abs(velocity.x) < 0.03f) velocity.x = 0f;
|
||||
if (Mathf.Abs(velocity.y) < 0.03f) velocity.y = 0f;
|
||||
if (Mathf.Abs(velocity.z) < 0.03f) velocity.z = 0f;
|
||||
|
||||
parent.rigidbody.linearVelocity = velocity;
|
||||
}
|
||||
|
||||
private void HandleRotation()
|
||||
{
|
||||
// Rotation disabled - wasn't rotating before - do nothing
|
||||
if (rotateType is UnitMovementRotateType.None && _rotateAction == null) return;
|
||||
|
||||
// Rotation enabled - wasn't rotating before - start rotating
|
||||
if (rotateType is not UnitMovementRotateType.None && _rotateAction == null)
|
||||
{
|
||||
_rotateAction = new RotateAction(rotationSpeed);
|
||||
parent.actions.Execute(_rotateAction);
|
||||
}
|
||||
|
||||
// Rotating disabled - was rotating before - stop rotating
|
||||
if (rotateType is UnitMovementRotateType.None && _rotateAction != null)
|
||||
{
|
||||
_rotateAction.CancelIt();
|
||||
_rotateAction = null;
|
||||
}
|
||||
|
||||
if (_rotateAction == null) return;
|
||||
|
||||
switch (rotateType)
|
||||
{
|
||||
case UnitMovementRotateType.MoveDirection:
|
||||
// Rotating enabled - was rotating before - update rotating yaw to movement direction
|
||||
_rotateAction.SetYaw(Quaternion.LookRotation(accelerationVector).eulerAngles.y);
|
||||
break;
|
||||
case UnitMovementRotateType.CustomDirection:
|
||||
// Rotating enabled - was rotating before - update rotating yaw to custom direction
|
||||
_rotateAction.SetYaw(rotateYaw);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnMoveStart(MoveStartEvent ev)
|
||||
{
|
||||
isMoving = true;
|
||||
}
|
||||
|
||||
public void OnMoveEnd(MoveEndEvent ev)
|
||||
{
|
||||
isMoving = false;
|
||||
accelerationVector = Vector3.zero;
|
||||
|
||||
if (_rotateAction != null)
|
||||
{
|
||||
_rotateAction.CancelIt();
|
||||
_rotateAction = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee3a8e7f717c4c93a0a50c4cc63dbfd8
|
||||
timeCreated: 1766851215
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace RPGCore.Movement.ObjectModules.UnitMovement
|
||||
{
|
||||
public enum UnitMovementRotateType
|
||||
{
|
||||
None,
|
||||
MoveDirection,
|
||||
CustomDirection
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08f3aae92c8a45a981b20fbdc3853995
|
||||
timeCreated: 1766851215
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "RPGCore.Movement",
|
||||
"rootNamespace": "RPGCore.Movement",
|
||||
"references": [
|
||||
"RPGCore",
|
||||
"RPGCore.StatusEffect",
|
||||
"RPGCoreCommon.Settings",
|
||||
"RPGCoreCommon.Helpers",
|
||||
"RPGCoreCommon.DynamicValues",
|
||||
"Unity.InputSystem"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 303c9d18d970e6d45b9e4c2e7463d65e
|
||||
timeCreated: 1772973851
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35a260cdc6714e458f1fd2a09a5b6c62
|
||||
timeCreated: 1772991392
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e40e139c6f3461889e756cc52010af8
|
||||
timeCreated: 1766851215
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using RPGCore.Core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace RPGCore.Movement.SceneModules.ActionController
|
||||
{
|
||||
[Serializable]
|
||||
public class ActionCameraModule : SceneModule
|
||||
{
|
||||
[Header("Camera Rotate and Tilt")]
|
||||
[SerializeField, Range(0.01f, 1f)] private float _rotateSensitivity = 0.1f;
|
||||
[SerializeField, Range(0.01f, 1f)] private float _tiltSensitivity = 0.06f;
|
||||
[SerializeField] private Vector2 _tiltLimit = new(20f, 85f); // 10 - 90
|
||||
|
||||
[Header("Camera Zoom")]
|
||||
[SerializeField, Range(0.05f, 1f)] private float _zoomSensitivity = 0.3f;
|
||||
[SerializeField] private Vector2 _zoomLimit = new(2f, 15f); // 1 - 30
|
||||
[SerializeField] private float _zoomCurrent = 8f;
|
||||
|
||||
[Header("Input Actions")]
|
||||
[SerializeField] private InputActionReference _rotateAndTiltInput;
|
||||
[SerializeField] private InputActionReference _zoomInput;
|
||||
|
||||
// RUNTIME
|
||||
private Camera _camera;
|
||||
|
||||
[Header("Camera target position")]
|
||||
public Transform stickTo;
|
||||
[SerializeField] private Vector3 _offset;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_camera = Camera.main;
|
||||
Cursor.lockState = CursorLockMode.Locked;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
RotateAndTiltCamera();
|
||||
ZoomCamera();
|
||||
MoveCamera();
|
||||
}
|
||||
|
||||
private void RotateAndTiltCamera()
|
||||
{
|
||||
if (!_rotateAndTiltInput || !_rotateAndTiltInput.action.inProgress) return;
|
||||
|
||||
var delta = _rotateAndTiltInput.action.ReadValue<Vector2>();
|
||||
var tiltDelta = delta.y * _tiltSensitivity * -1;
|
||||
var rotateDelta = delta.x * _rotateSensitivity;
|
||||
|
||||
var previousEuler = _camera.transform.rotation.eulerAngles;
|
||||
var nextEuler = previousEuler + new Vector3(tiltDelta, rotateDelta, 0);
|
||||
|
||||
if (nextEuler.x < _tiltLimit.x) nextEuler.x = _tiltLimit.x;
|
||||
if (nextEuler.x > _tiltLimit.y) nextEuler.x = _tiltLimit.y;
|
||||
|
||||
_camera.transform.rotation = Quaternion.Euler(nextEuler);
|
||||
}
|
||||
|
||||
private void ZoomCamera()
|
||||
{
|
||||
if (!_zoomInput || !_zoomInput.action.inProgress) return;
|
||||
|
||||
_zoomCurrent -= _zoomInput.action.ReadValue<float>() * _zoomSensitivity;
|
||||
|
||||
if (_zoomCurrent > _zoomLimit.y) _zoomCurrent = _zoomLimit.y;
|
||||
if (_zoomCurrent < _zoomLimit.x) _zoomCurrent = _zoomLimit.x;
|
||||
}
|
||||
|
||||
private void MoveCamera()
|
||||
{
|
||||
var toPosition = stickTo.position - _camera.transform.forward * _zoomCurrent;
|
||||
_camera.transform.position = toPosition + _camera.transform.TransformDirection(_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9460d10ec97244dbb73bb37ab1aec54e
|
||||
timeCreated: 1766851215
|
||||
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using RPGCore.Movement.ObjectModules.UnitMovement;
|
||||
using RPGCore.Core;
|
||||
using RPGCore.Core.Objects;
|
||||
using RPGCore.Movement.ObjectModules.UnitMovement.Actions;
|
||||
using RPGCore.ObjectModules.ActionObjectModule;
|
||||
using RPGCoreCommon.Helpers;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace RPGCore.Movement.SceneModules.ActionController
|
||||
{
|
||||
[Serializable]
|
||||
[RequireComponent(typeof(ActionCameraModule))]
|
||||
public class ActionControllerModule : SceneModule
|
||||
{
|
||||
[SerializeField] private UnitObject _unit;
|
||||
[SerializeField] private InputActionReference _rotateInput;
|
||||
[SerializeField] private InputActionReference _moveInput;
|
||||
[SerializeField] private InputActionReference _moveFasterInput;
|
||||
[SerializeField] private InputActionReference _jumpInput;
|
||||
|
||||
private Camera _camera;
|
||||
private BaseActionParallel _moveAction;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_camera = Camera.main;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
var movementModule = _unit.GetComponent<UnitMovementModule>();
|
||||
|
||||
_rotateInput.action.performed += _ =>
|
||||
{
|
||||
movementModule.rotateYaw = _camera.transform.rotation.eulerAngles.y;
|
||||
};
|
||||
|
||||
_moveInput.action.performed += _ =>
|
||||
{
|
||||
_moveAction = new DirectionMoveAction(GetDirection);
|
||||
_unit.actions.Execute(_moveAction);
|
||||
};
|
||||
_moveInput.action.canceled += _ =>
|
||||
{
|
||||
_moveAction?.CancelIt();
|
||||
_moveAction = null;
|
||||
};
|
||||
|
||||
_moveFasterInput.action.performed += _ => movementModule.isSprinting = true;
|
||||
_moveFasterInput.action.canceled += _ => movementModule.isSprinting = false;
|
||||
|
||||
_jumpInput.action.performed += _ => _unit.actions.Execute(new JumpAction());
|
||||
|
||||
movementModule.rotateType = UnitMovementRotateType.CustomDirection;
|
||||
}
|
||||
|
||||
private Vector3 GetDirection()
|
||||
{
|
||||
var input = _moveInput.action.ReadValue<Vector2>();
|
||||
return _camera.transform.right.SetY(0).normalized * input.x
|
||||
+ _camera.transform.forward.SetY(0).normalized * input.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f54b0d5fd319467da310ffc54509377a
|
||||
timeCreated: 1766851215
|
||||
Reference in New Issue
Block a user