using System.Collections; using System.Collections.Generic; using UnityEngine; using BITKit; using BITFALL.Entites; using KinematicCharacterController; using BITKit.StateMachine; using UnityEngine.InputSystem; using BITKit.Animations; using BITKit.Sensors; using BITKit.Entities; namespace BITFALL.Entites.KinematicMovementStates { [System.Serializable] public class Movement : KinematicMovementState { public UnityAnimator animator; [SerializeReference, SubclassSelector] public IClosePoint closePoint; public override void OnStateEnter(IState old) { base.OnStateEnter(old); } public override void OnStateExit(IState old, IState newState) { base.OnStateExit(old, newState); } public override void BeforeCharacterUpdate(float deltaTime) { if (controller.runState.shouldBe && controller.runState.being is false) { controller.runState.Release(); controller .entity .Invoke(new OnRunCallback() { started = true }); } if (controller.moveInput.z is 0 || controller.runState.shouldBe is false) { var input = controller.entity.Get(nameof(controller.OnRun)); if (input.performed) { } else { controller.runState.Reset(); controller .entity .Invoke(new OnRunCallback() { canceled = true }); } } if (controller.jumpState.shouldBe) { if (closePoint.TryGetClosePoint(out var pos)) { controller.matchTargetPosition = pos; TransitionState(); controller.jumpState.Reset(); } else if (controller.groundState.being is false) { controller.jumpState.Reset(); } } else { controller.jumpState.Reset(); } controller.crouchState.Release(); } public override void AfterCharacterUpdate(float deltaTime) { var veloctiy = motor.BaseVelocity; veloctiy.y = 0; var sqrMagnitude = veloctiy.sqrMagnitude; var nextState = (controller.runState.being, controller.crouchState.being, controller.groundState.being) switch { (_, _, false) => "Air", (_, true, _) => "Crouch", (true, _, _) => "Run", _ => "Movement", }; if (animator[0].stateName != nextState) { switch (animator[0].fullStateName) { case "Run.Move": if (nextState is "Movement") { //if (sqrMagnitude < 0.5f) { animator.Play("Movement.Stop"); break; } } goto default; default: animator.CrossFade(nextState, 0.1f); break; } } if (controller.runState) { controller .entity .Invoke(new OnRunCallback() { updated = true }); } } public override void UpdateRotation(ref Quaternion currentRotation, float deltaTime) { var projectRotation = Quaternion.LookRotation(Vector3.ProjectOnPlane(controller.cameraRoot.forward, Vector3.up)); var rotSpeed = 720; currentRotation = Quaternion.RotateTowards( currentRotation, projectRotation, rotSpeed * deltaTime ); } public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime) { var _moveVelocity = Quaternion.Euler(controller.lookInput) * controller.moveInput; float currentVelocityMagnitude = currentVelocity.magnitude; Vector3 effectiveGroundNormal = motor.GroundingStatus.GroundNormal; if (currentVelocityMagnitude > 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; } } if (motor.GroundingStatus.FoundAnyGround) { if (controller.jumpState.shouldBe) { controller.jumpState.Reset(); motor.ForceUnground(); currentVelocity += Vector3.up * 8; controller.entity.Invoke(Constant.Animation.Play, controller._jump); } Vector3 inputRight = Vector3.Cross(_moveVelocity, motor.CharacterUp); Vector3 reorientedInput = Vector3.Cross(effectiveGroundNormal, inputRight).normalized * _moveVelocity.magnitude; Vector3 targetMovementVelocity = reorientedInput * controller._moveSpeed; // Smooth movement Velocity currentVelocity = Vector3.Lerp(currentVelocity, targetMovementVelocity, 1f - Mathf.Exp(-16 * deltaTime)); } // Air movement else { // Add move input if (_moveVelocity.sqrMagnitude > 0f) { Vector3 addedVelocity = _moveVelocity * 3 * deltaTime; Vector3 currentVelocityOnInputsPlane = Vector3.ProjectOnPlane(currentVelocity, motor.CharacterUp); // Limit air velocity from inputs if (currentVelocityOnInputsPlane.magnitude < 5) { // clamp addedVel to make total vel not exceed max vel on inputs plane Vector3 newTotal = Vector3.ClampMagnitude(currentVelocityOnInputsPlane + addedVelocity, 5); addedVelocity = newTotal - currentVelocityOnInputsPlane; } else { // Make sure added vel doesn't go in the direction of the already-exceeding velocity if (Vector3.Dot(currentVelocityOnInputsPlane, addedVelocity) > 0f) { addedVelocity = Vector3.ProjectOnPlane(addedVelocity, currentVelocityOnInputsPlane.normalized); } } // Prevent air-climbing sloped walls if (motor.GroundingStatus.FoundAnyGround) { if (Vector3.Dot(currentVelocity + addedVelocity, addedVelocity) > 0f) { Vector3 perpenticularObstructionNormal = Vector3.Cross(Vector3.Cross(motor.CharacterUp, motor.GroundingStatus.GroundNormal), motor.CharacterUp).normalized; addedVelocity = Vector3.ProjectOnPlane(addedVelocity, perpenticularObstructionNormal); } } // Apply added velocity currentVelocity += addedVelocity; controller.currentGravity = Mathf.Min(controller.currentGravity, motor.Velocity.y); } // Gravity currentVelocity += -Vector3.up * 30 * deltaTime; // Drag currentVelocity *= (1f / (1f + (0.1f * deltaTime))); } } public override void OnCancelMovement(IMovementCancelAction action) { switch (action) { case CancelMovement: break; case CancelRunOrSprint: controller.runState.shouldBe = false; break; } } } }