|
|
@@ -0,0 +1,588 @@ |
|
|
using System; |
|
|
using System.Collections.Generic; |
|
|
using KinematicCharacterController; |
|
|
using Twokinds.Assets._Game.Scripts.Movement; |
|
|
using Twokinds.Assets._Game.Scripts.MovementKine.Actions; |
|
|
using Twokinds.Assets._Game.Scripts.MovementKine.Stances; |
|
|
using Unity.Entities; |
|
|
using UnityEngine; |
|
|
using UnityEngine.Events; |
|
|
|
|
|
namespace Twokinds.Assets._Game.Scripts.MovementKine |
|
|
{ |
|
|
public class KineMovement : BaseCharacterController |
|
|
{ |
|
|
public Transform MovementTransform { get { return transform; } } |
|
|
public Vector3 MovementInput |
|
|
{ |
|
|
get { return _movementInput; } |
|
|
} |
|
|
|
|
|
public bool Walk { get { return _walk; } } |
|
|
|
|
|
//order by priority. Top has say, second overlays with top mask |
|
|
public List<BaseStanceKine> Stance { get; private set; } |
|
|
|
|
|
public Vector3[] LastGoodGround = new Vector3[3]; |
|
|
|
|
|
public event UnityAction<ActionEffects> OnTriggerAction; |
|
|
public event UnityAction<ActionEffects> OnEndAction; |
|
|
|
|
|
public int ComboMax { get; private set; } |
|
|
public AttackType AttackType { get; private set; } |
|
|
public bool TwoHands { get; private set; } |
|
|
|
|
|
public float MoveSpeed |
|
|
{ |
|
|
get |
|
|
{ |
|
|
if (Stance.Count < 1) |
|
|
{ |
|
|
return _moveSpeed; |
|
|
} |
|
|
|
|
|
var speed = Stance[0].MovementSpeed; |
|
|
for (int cnt = 1; cnt < Stance.Count; cnt++) |
|
|
{ |
|
|
if (Stance[cnt].MovementSpeed < speed) |
|
|
{ |
|
|
speed = Stance[cnt].MovementSpeed; |
|
|
} |
|
|
} |
|
|
|
|
|
return _moveSpeed * speed; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
public Vector3 LocalVelocity |
|
|
{ |
|
|
get { return _localVelocity; } |
|
|
} |
|
|
|
|
|
public float ReverseAdjust |
|
|
{ |
|
|
get |
|
|
{ |
|
|
if (Stance.Count < 1) |
|
|
{ |
|
|
return .33f; |
|
|
} |
|
|
|
|
|
var speed = Stance[0].ReverseSpeed; |
|
|
for (int cnt = 1; cnt < Stance.Count; cnt++) |
|
|
{ |
|
|
if (Stance[cnt].ReverseSpeed < speed) |
|
|
{ |
|
|
speed = Stance[cnt].ReverseSpeed; |
|
|
} |
|
|
} |
|
|
|
|
|
return speed; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
public BaseActionKine Action |
|
|
{ |
|
|
get { return _action; } |
|
|
} |
|
|
|
|
|
public Animator Animator; |
|
|
|
|
|
|
|
|
public Transform CameraTarget; |
|
|
|
|
|
[SerializeField] |
|
|
private DefaultStance _defaultStance = DefaultStance.Standard; |
|
|
|
|
|
private Type _defaultStanceType; |
|
|
private int _jumpCount = 1; |
|
|
|
|
|
private BaseActionKine _action; |
|
|
|
|
|
|
|
|
|
|
|
[Header("Movement")] |
|
|
[SerializeField] |
|
|
float _moveSpeed = 10; |
|
|
float _stableMovementSharpness = 15; |
|
|
|
|
|
[SerializeField] |
|
|
private bool _walk; |
|
|
|
|
|
[SerializeField] |
|
|
private Vector3 _desiredRotation; |
|
|
[SerializeField] |
|
|
private Vector3 _rotationInput; |
|
|
|
|
|
[SerializeField] |
|
|
private Vector3 _movementInput; |
|
|
|
|
|
private Vector3 _localVelocity; |
|
|
|
|
|
public List<Collider> IgnoredColliders = new List<Collider>(); |
|
|
|
|
|
|
|
|
[Header("Air Movement")] |
|
|
public float MaxAirMoveSpeed = 10f; |
|
|
public float AirAccelerationSpeed = 5f; |
|
|
public float Drag = 0.1f; |
|
|
public Vector3 Gravity = new Vector3(0, -30f, 0); |
|
|
|
|
|
[Header("Network Settings")] |
|
|
public bool Networked; |
|
|
|
|
|
|
|
|
private float _cameraUpdateFollowEnd = 0; |
|
|
|
|
|
void Awake() |
|
|
{ |
|
|
Stance = new List<BaseStanceKine>(); |
|
|
SetMaxCombo(3); |
|
|
|
|
|
if (CameraTarget == null) |
|
|
{ |
|
|
CameraTarget = new GameObject("Camera Target").transform; |
|
|
CameraTarget.parent = transform; |
|
|
CameraTarget.localPosition = Vector3.up; |
|
|
CameraTarget.localRotation = Quaternion.identity; |
|
|
} |
|
|
} |
|
|
|
|
|
// Use this for initialization |
|
|
void Start () { |
|
|
|
|
|
BaseStanceKine defStance; |
|
|
if (_defaultStance == DefaultStance.Horse) |
|
|
{ |
|
|
//defStance = new HorseStanceKine(this); |
|
|
defStance = new HorseStance(this); |
|
|
_defaultStanceType = defStance.GetType(); |
|
|
} |
|
|
else |
|
|
{ |
|
|
//standard |
|
|
defStance = new StandardStanceKine(this); |
|
|
} |
|
|
|
|
|
_defaultStanceType = defStance.GetType(); |
|
|
Debug.Log(_defaultStanceType.ToString()); |
|
|
SetStance(defStance); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
// Update is called once per frame |
|
|
void Update () { |
|
|
|
|
|
if (Motor.GroundingStatus.IsStableOnGround && (Action == null || Action.PreventJumpReset == false)) |
|
|
{ |
|
|
if (_jumpCount != 1) |
|
|
{ |
|
|
Debug.Log("Resetting jump counter"); |
|
|
} |
|
|
|
|
|
_jumpCount = 1; |
|
|
} |
|
|
else if (Motor.GroundingStatus.IsStableOnGround == false && Motor.BaseVelocity.y < -.15f && StanceActive<FallStance>() == false) |
|
|
{ |
|
|
//fall animation |
|
|
SetStance(new FallStance(this)); |
|
|
} |
|
|
|
|
|
if (_action != null) |
|
|
{ |
|
|
_action.Update(); |
|
|
return; |
|
|
} |
|
|
|
|
|
for (int cnt = 0; cnt < Stance.Count; cnt++) |
|
|
{ |
|
|
Stance[cnt].Update(); |
|
|
if (!Stance[cnt].AllowSub) |
|
|
{ |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
public bool StanceActive<T>() where T : BaseStanceKine |
|
|
{ |
|
|
for (int cnt = 0; cnt < Stance.Count; cnt++) |
|
|
{ |
|
|
if (Stance[cnt] is T) |
|
|
{ |
|
|
return true; |
|
|
} |
|
|
} |
|
|
|
|
|
return false; |
|
|
} |
|
|
|
|
|
public void SetStance(BaseStanceKine stance) |
|
|
{ |
|
|
|
|
|
Stance.Add(stance); |
|
|
Stance.Sort((s1, s2) => s1.Priority.CompareTo(s2.Priority)); |
|
|
} |
|
|
|
|
|
/// <summary> |
|
|
/// |
|
|
/// </summary> |
|
|
/// <typeparam name="T"></typeparam> |
|
|
/// <param name="stance"></param> |
|
|
/// <returns>True if Active, fals if not</returns> |
|
|
public bool ToggleStance<T>(T stance = null) where T : BaseStanceKine |
|
|
{ |
|
|
if (typeof(T) == _defaultStanceType) |
|
|
return true; |
|
|
|
|
|
for (int cnt = 0; cnt < Stance.Count; cnt++) |
|
|
{ |
|
|
if (Stance[cnt] is T) |
|
|
{ |
|
|
Stance.RemoveAt(cnt); |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
if (stance == null) |
|
|
{ |
|
|
return false; |
|
|
} |
|
|
|
|
|
SetStance(stance); |
|
|
return true; |
|
|
} |
|
|
|
|
|
|
|
|
public override void UpdateRotation(ref Quaternion currentRotation, float deltaTime) |
|
|
{ |
|
|
|
|
|
if (Time.time < _cameraUpdateFollowEnd) |
|
|
{ |
|
|
|
|
|
if (_action != null && _action.LockRotation) |
|
|
{ |
|
|
_desiredRotation = currentRotation.eulerAngles; |
|
|
} |
|
|
|
|
|
float orientationSharpness = 10; |
|
|
var rot = Quaternion.Euler(_desiredRotation) * Vector3.forward; |
|
|
// Smoothly interpolate from current to target look direction |
|
|
Vector3 smoothedLookInputDirection = Vector3 |
|
|
.Slerp(Motor.CharacterForward, rot, 1 - Mathf.Exp(-orientationSharpness * deltaTime)).normalized; |
|
|
|
|
|
// Set the current rotation (which will be used by the KinematicCharacterMotor) |
|
|
currentRotation = Quaternion.LookRotation(smoothedLookInputDirection, Motor.CharacterUp); |
|
|
} |
|
|
} |
|
|
|
|
|
public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime) |
|
|
{ |
|
|
Vector3 targetMovementVelocity = Vector3.zero; |
|
|
|
|
|
|
|
|
// Ground movement |
|
|
if (Motor.GroundingStatus.IsStableOnGround) |
|
|
{ |
|
|
|
|
|
SaveGroundPoint(Motor.transform.position); |
|
|
|
|
|
var movementInput = _movementInput; |
|
|
|
|
|
|
|
|
if (_action != null && _action.LockMovement) |
|
|
{ |
|
|
movementInput = Vector3.zero; |
|
|
} |
|
|
|
|
|
if (Stance[0].LimitStrafe) |
|
|
{ |
|
|
movementInput.x = 0; |
|
|
} |
|
|
|
|
|
|
|
|
if (_walk) |
|
|
{ |
|
|
movementInput /= 3f; |
|
|
|
|
|
//movementInput.x = Mathf.Clamp(movementInput.x, -.3f, .3f); |
|
|
//movementInput.y = Mathf.Clamp(movementInput.y, -.3f, .3f); |
|
|
//movementInput.z = Mathf.Clamp(movementInput.z, -.3f, .3f); |
|
|
} |
|
|
|
|
|
//move in direction of desired rotation |
|
|
movementInput = Quaternion.Euler(_desiredRotation) * movementInput; |
|
|
|
|
|
Vector3 effectiveGroundNormal = Motor.GroundingStatus.GroundNormal; |
|
|
if (currentVelocity.sqrMagnitude > 0f && Motor.GroundingStatus.SnappingPrevented) |
|
|
{ |
|
|
// Take the normal from where we're coming from |
|
|
Vector3 groundPointToCharacter = Motor.TransientPosition - Motor.GroundingStatus.GroundPoint; |
|
|
if (Vector3.Dot(currentVelocity, groundPointToCharacter) >= 0f) |
|
|
{ |
|
|
effectiveGroundNormal = Motor.GroundingStatus.OuterGroundNormal; |
|
|
} |
|
|
else |
|
|
{ |
|
|
effectiveGroundNormal = Motor.GroundingStatus.InnerGroundNormal; |
|
|
} |
|
|
} |
|
|
|
|
|
// Reorient velocity on slope |
|
|
currentVelocity = Motor.GetDirectionTangentToSurface(currentVelocity, effectiveGroundNormal) * currentVelocity.magnitude; |
|
|
|
|
|
// Calculate target velocity |
|
|
Vector3 inputRight = Vector3.Cross(movementInput, Motor.CharacterUp); |
|
|
//Debug.Log(inputRight); |
|
|
|
|
|
Vector3 reorientedInput = Vector3.Cross(effectiveGroundNormal, inputRight).normalized * movementInput.magnitude; |
|
|
targetMovementVelocity = reorientedInput * MoveSpeed; |
|
|
|
|
|
var localVel = Motor.Transform.InverseTransformDirection(targetMovementVelocity); |
|
|
localVel.z = Mathf.Clamp(localVel.z, - _moveSpeed * ReverseAdjust, MoveSpeed); |
|
|
|
|
|
_localVelocity = localVel; |
|
|
targetMovementVelocity = Motor.Transform.TransformDirection(localVel); |
|
|
|
|
|
// Smooth movement Velocity |
|
|
currentVelocity = Vector3.Lerp(currentVelocity, targetMovementVelocity, 1 - Mathf.Exp(-_stableMovementSharpness * deltaTime)); |
|
|
} |
|
|
// Air movement |
|
|
else |
|
|
{ |
|
|
//move in direction of desired rotation |
|
|
var movementInput = Quaternion.Euler(_desiredRotation) * _movementInput; |
|
|
if (_action != null && _action.LockMovement) |
|
|
{ |
|
|
movementInput = Vector3.zero; |
|
|
} |
|
|
|
|
|
// Add move input |
|
|
if (movementInput.sqrMagnitude > 0f) |
|
|
{ |
|
|
targetMovementVelocity = movementInput * MaxAirMoveSpeed; |
|
|
|
|
|
// Prevent climbing on un-stable slopes with air movement |
|
|
if (Motor.GroundingStatus.FoundAnyGround) |
|
|
{ |
|
|
Vector3 perpenticularObstructionNormal = Vector3.Cross(Vector3.Cross(Motor.CharacterUp, Motor.GroundingStatus.GroundNormal), Motor.CharacterUp).normalized; |
|
|
targetMovementVelocity = Vector3.ProjectOnPlane(targetMovementVelocity, perpenticularObstructionNormal); |
|
|
} |
|
|
|
|
|
_localVelocity = targetMovementVelocity; |
|
|
|
|
|
Vector3 velocityDiff = Vector3.ProjectOnPlane(targetMovementVelocity - currentVelocity, Gravity); |
|
|
currentVelocity += velocityDiff * AirAccelerationSpeed * deltaTime; |
|
|
} |
|
|
|
|
|
// Gravity |
|
|
currentVelocity += Gravity * deltaTime; |
|
|
|
|
|
if (currentVelocity.y < 0) |
|
|
{ |
|
|
if (!Physics.Raycast(new Ray(transform.position, Vector3.down), 10000, ~(1 << 12))) |
|
|
{ |
|
|
//stop falling |
|
|
currentVelocity.y = 0; |
|
|
} |
|
|
} |
|
|
// Drag |
|
|
currentVelocity *= (1f / (1f + (Drag * deltaTime))); |
|
|
|
|
|
} |
|
|
|
|
|
if (_action != null) |
|
|
{ |
|
|
_action.Trigger(ref currentVelocity); |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
private void SaveGroundPoint(Vector3 position) |
|
|
{ |
|
|
if(Mathf.Abs((LastGoodGround[0] - position).sqrMagnitude) < 2) |
|
|
return; |
|
|
|
|
|
//maybe use a queue in the future |
|
|
//skip the last |
|
|
for (int cnt = LastGoodGround.Length - 2; cnt >= 0; cnt--) |
|
|
{ |
|
|
LastGoodGround[cnt + 1] = LastGoodGround[cnt]; |
|
|
} |
|
|
|
|
|
LastGoodGround[0] = position; |
|
|
} |
|
|
|
|
|
public override void BeforeCharacterUpdate(float deltaTime) |
|
|
{ |
|
|
} |
|
|
|
|
|
public override void PostGroundingUpdate(float deltaTime) |
|
|
{ |
|
|
} |
|
|
|
|
|
public override void AfterCharacterUpdate(float deltaTime) |
|
|
{ |
|
|
} |
|
|
|
|
|
public override bool IsColliderValidForCollisions(Collider coll) |
|
|
{ |
|
|
if (IgnoredColliders.Count == 0) |
|
|
{ |
|
|
return true; |
|
|
} |
|
|
|
|
|
if (IgnoredColliders.Contains(coll)) |
|
|
{ |
|
|
return false; |
|
|
} |
|
|
return true; |
|
|
} |
|
|
|
|
|
public override void OnGroundHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, ref HitStabilityReport hitStabilityReport) |
|
|
{ |
|
|
} |
|
|
|
|
|
public override void OnMovementHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, |
|
|
ref HitStabilityReport hitStabilityReport) |
|
|
{ |
|
|
} |
|
|
|
|
|
public override void ProcessHitStabilityReport(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, Vector3 atCharacterPosition, |
|
|
Quaternion atCharacterRotation, ref HitStabilityReport hitStabilityReport) |
|
|
{ |
|
|
} |
|
|
|
|
|
public void SetInput(Vector3 input, Vector3 rotationInput, Vector3 desiredRotation) |
|
|
{ |
|
|
rotationInput.x = 0; |
|
|
rotationInput.z = 0; |
|
|
desiredRotation.x = 0; |
|
|
desiredRotation.z = 0; |
|
|
_desiredRotation = desiredRotation; |
|
|
_rotationInput = rotationInput; |
|
|
|
|
|
//input = Quaternion.Euler(_desiredRotation) * input; |
|
|
Vector3 moveInputVector = Vector3.ClampMagnitude(new Vector3(input.x, 0f, input.z), 1f); |
|
|
|
|
|
_movementInput = moveInputVector; |
|
|
|
|
|
if (_rotationInput.magnitude > 0) |
|
|
{ |
|
|
_cameraUpdateFollowEnd = Time.time + 0.25f; |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public void ToggleCrouch(bool? crouch = null) |
|
|
{ |
|
|
if (!crouch.HasValue) |
|
|
{ |
|
|
ToggleStance(new SneakStance(this)); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (crouch.Value) |
|
|
{ |
|
|
ToggleStance(new SneakStance(this)); |
|
|
|
|
|
} |
|
|
|
|
|
ToggleStance<SneakStance>(null); |
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
public void TriggerJump() |
|
|
{ |
|
|
if (_jumpCount < 1) |
|
|
return; |
|
|
|
|
|
if (_action != null) |
|
|
{ |
|
|
if (!_action.Interruptable) |
|
|
return; |
|
|
|
|
|
_action.Interrupt(); |
|
|
} |
|
|
|
|
|
_action = new JumpAction(this); |
|
|
_jumpCount--; |
|
|
} |
|
|
|
|
|
public void SetAction(BaseActionKine action) |
|
|
{ |
|
|
if (_action != null) |
|
|
{ |
|
|
_action.Interrupt(); |
|
|
} |
|
|
|
|
|
|
|
|
_action = action; |
|
|
|
|
|
SetupActionEvents(); |
|
|
if (_action == null) |
|
|
{ |
|
|
//_motion.ChangeAnimation(_motion.Animation); |
|
|
} |
|
|
} |
|
|
|
|
|
void SetupActionEvents() |
|
|
{ |
|
|
if(_action == null) |
|
|
return; |
|
|
|
|
|
_action.OnTriggerAction += OnTriggerAction; |
|
|
_action.OnEndAction += OnEndAction; |
|
|
} |
|
|
|
|
|
void RemoveActionEvents() |
|
|
{ |
|
|
if (_action == null) |
|
|
return; |
|
|
|
|
|
_action.OnTriggerAction -= OnTriggerAction; |
|
|
_action.OnEndAction -= OnEndAction; |
|
|
} |
|
|
|
|
|
public void ToggleWalk(bool walk) |
|
|
{ |
|
|
_walk = walk; |
|
|
|
|
|
} |
|
|
|
|
|
public void RemoveAction(BaseActionKine action) |
|
|
{ |
|
|
if(action == null) |
|
|
return; |
|
|
|
|
|
if (action.Equals(_action)) |
|
|
{ |
|
|
_action.Reset(); |
|
|
} |
|
|
|
|
|
Debug.Log($"Removed Action: {action.GetType()}"); |
|
|
|
|
|
RemoveActionEvents(); |
|
|
|
|
|
_action = null; |
|
|
} |
|
|
|
|
|
public void SetMaxCombo(int comboMax) |
|
|
{ |
|
|
ComboMax = comboMax; |
|
|
|
|
|
} |
|
|
|
|
|
public void WeaponAttackType(AttackType attackType, bool twoHands) |
|
|
{ |
|
|
AttackType = attackType; |
|
|
TwoHands = twoHands; |
|
|
} |
|
|
} |
|
|
} |