Ejemplo n.º 1
0
    private void ChangeForm(PlayerForm form, bool instant = false)
    {
        IsChangingForm = true;
        CurrentForm    = form;

        GD.Print("Changing form to " + form.ToString());

        if (!SceneCache.ContainsKey(CurrentForm))
        {
            SceneCache.Add(CurrentForm, GD.Load <PackedScene>(Constants.FilePath.PLAYER_FORM_SCENES + CurrentForm.ToString() + ".tscn"));
        }
        CurrentScene = SceneCache[CurrentForm];

        string frameCacheKey = SpriteFolderName + "/" + CurrentForm.ToString();

        if (!FrameCache.ContainsKey(frameCacheKey))
        {
            FrameCache.Add(frameCacheKey, GD.Load <SpriteFrames>(Constants.FilePath.PLAYER_FRAMES + SpriteFolderName + "/" + CurrentForm.ToString() + ".tres"));
        }
        CurrentFrames = FrameCache[frameCacheKey];

        Player oldFormScene = GetChildCount() > 0 ? GetChildOrNull <Player>(0) : null;

        oldFormScene?.SetName("QueuedForDeletion");

        if (oldFormScene != null)
        {
            PreviousTransform = oldFormScene.GetGlobalTransform();
            PreviousVelocity  = oldFormScene.Velocity;

            AnimatedSprite3D sprite = GetNode <AnimatedSprite3D>(new NodePath("QueuedForDeletion/PlayerSprite"));
            PreviousSpriteFlipped = sprite.IsFlippedH();

            if (!instant)
            {
                //Create blinking state transition
                TransitionFrames.Clear("Transition");

                TransitionFrames.AddFrame("Transition", sprite.Frames.GetFrame(Player.PlayerAnimation.IDLE, 0), 0);
                TransitionFrames.AddFrame("Transition", CurrentFrames.GetFrame(Player.PlayerAnimation.IDLE, 0), 1);

                TransitionSprite.SetGlobalTransform(sprite.GetGlobalTransform().Translated(-Transform.origin));
                TransitionSprite.SetFlipH(sprite.FlipH);
                TransitionSprite.SetPixelSize(sprite.GetPixelSize());
                TransitionSprite.Play("Transition");
            }

            oldFormScene.SetProcess(false);
            oldFormScene.SetPhysicsProcess(false);
            oldFormScene.QueueFree();
        }

        if (instant)
        {
            FinishFormChange(form);
            return;
        }

        AddChild(TransitionSprite);
    }
Ejemplo n.º 2
0
    // ================================================================

    public override void _Ready()
    {
        timerMove = GetNode <Timer>("TimerMove");
        timerStop = GetNode <Timer>("TimerStop");
        spr       = GetNode <AnimatedSprite3D>("Sprite");

        timerMove.WaitTime = (float)GD.RandRange(1.5, 2.8);
        timerMove.Start();
    }
Ejemplo n.º 3
0
    public virtual void SetupPlayer(InputManager input, Transform transform, Vector3 velocity, SpriteFrames spriteFrames, bool spriteFlipped)
    {
        PlayerSprite      = GetNodeOrNull <AnimatedSprite3D>(new NodePath("PlayerSprite"));
        ActorDetectorArea = GetNodeOrNull <Area>(new NodePath("ActorDetector"));
        FloorRayCast      = GetNodeOrNull <RayCast>(new NodePath("FloorRayCast"));

        if (PlayerSprite == null || ActorDetectorArea == null || FloorRayCast == null)
        {
            GD.Print("One or multiple required child nodes could not be found! Some features won't work!");
        }

        InputManager        = input;
        GlobalTransform     = transform;
        Velocity            = velocity;
        PlayerSprite.Frames = spriteFrames;
        PlayerSprite.FlipH  = spriteFlipped;

        SetPlayerCollisionShape(GetDefaultCollisionShape());
    }
Ejemplo n.º 4
0
    public override void _Ready()
    {
        PreviousTransform = GetGlobalTransform();
        PreviousVelocity  = Vector3.Zero;

        TransitionFrames = new SpriteFrames();
        TransitionFrames.AddAnimation("Transition");
        TransitionFrames.SetAnimationSpeed("Transition", 10);

        TransitionSprite = new AnimatedSprite3D();
        TransitionSprite.SetName("TransitionSprite");
        TransitionSprite.SetSpriteFrames(TransitionFrames);
        TransitionSprite.SetCastShadowsSetting(GeometryInstance.ShadowCastingSetting.On);
        TransitionSprite.Transparent = true;
        TransitionSprite.AlphaCut    = SpriteBase3D.AlphaCutMode.OpaquePrepass;

        CurrentForm        = InitialForm;
        PlayerInputManager = new InputManager(PlayerNumber);

        ChangeForm(InitialForm, true);
    }
Ejemplo n.º 5
0
    public override void _EnterTree()
    {
        PlayerSpawners.Add(this);

        PreviousTransform = GlobalTransform;
        PreviousVelocity  = Vector3.Zero;

        TransitionFrames = new SpriteFrames();
        TransitionFrames.AddAnimation("Transition");
        TransitionFrames.SetAnimationSpeed("Transition", 10);

        TransitionSprite             = new AnimatedSprite3D();
        TransitionSprite.Name        = "TransitionSprite";
        TransitionSprite.Frames      = TransitionFrames;
        TransitionSprite.CastShadow  = GeometryInstance.ShadowCastingSetting.On;
        TransitionSprite.Transparent = true;
        TransitionSprite.AlphaCut    = SpriteBase3D.AlphaCutMode.OpaquePrepass;

        CurrentForm        = InitialForm;
        PlayerInputManager = new InputManager(PlayerNumber);

        ChangeForm(InitialForm, true);
    }
Ejemplo n.º 6
0
    public override void AEnterTree() //TODO Try moving everything non-visual to Physics Processing
    {
        PlayerSprite      = GetNodeOrNull(new NodePath("PlayerSprite")) as AnimatedSprite3D;
        ActorDetectorArea = GetNodeOrNull(new NodePath("ActorDetector")) as Area;

        if (PlayerSprite == null || ActorDetectorArea == null)
        {
            GD.PrintErr("One or multiple required child nodes could not be found!");
        }

        //Define states

        StandState = new ActorState(() =>
        { //Enter State
            PlayerSprite.SetAnimation("Idle");
            SnapToGround = true;
        }, (float delta) =>
        { //Process State
        }, (float delta) =>
        { //State Physics Processing
            //Switch to the walking state when directional input is given
            bool sideInput = Mathf.Abs(Input.GetActionStrength("p1_right") - Input.GetActionStrength("p1_left")) > 0;
            if (sideInput)
            {
                ChangeState(WalkState);
            }

            CanFall();
            CanJump();
        }, () =>
        { //Exit State
        });


        WalkState = new ActorState(() =>
        { //Enter State
            PlayerSprite.SetAnimation("Walk");
            SnapToGround = true;
        }, (float delta) =>
        { //Process State
        }, (float delta) =>
        { //State Physics Processing
            //Switch to the standing state when no directional input is given
            float sideInput = Input.GetActionStrength("p1_right") - Input.GetActionStrength("p1_left");
            if (sideInput == 0)
            {
                ChangeState(StandState);
            }

            //Check if the player should be running, if yes go to run state
            bool isRunPressed = Input.GetActionStrength("p1_run") > 0;
            if (isRunPressed)
            {
                ChangeState(RunState);
            }

            //Move the player
            Vector2 movement = new Vector2();
            movement.x       = sideInput * WalkAcceleration;
            //movement.x = Mathf.Abs(Velocity.x) + Mathf.Abs(movement.x) > MaxWalkSpeed ? (MaxWalkSpeed - Mathf.Abs(Velocity.x)) * Mathf.Sign(movement.x) : movement.x;
            ApplyForce2D(movement);

            //Update the walking animation speed
            PlayerSprite.Frames.SetAnimationSpeed("Walk", (Mathf.Abs(Velocity.x) / 2) + 8);

            CanFall();
            CanJump();

            //Enforce walk speed limit
            if (Math.Abs(Velocity.x) >= MaxWalkSpeed)
            {
                Velocity.x = ClampedInterpolation.Lerp(Velocity.x, MaxWalkSpeed * Mathf.Sign(Velocity.x), GetElapsedTimeInState());
            }
        }, () =>
        { //Exit State
        });


        RunState = new ActorState(() =>
        { //Enter State
            PlayerSprite.SetAnimation("Walk");
            SnapToGround = true;
        }, (float delta) =>
        { //Process State
        }, (float delta) =>
        { //State Physics Processing
            //Switch to the standing state when no directional input is given
            float sideInput = Input.GetActionStrength("p1_right") - Input.GetActionStrength("p1_left");
            if (sideInput == 0)
            {
                ChangeState(StandState);
            }

            //Check if the player should be running, if not go back to walk state
            bool isRunPressed = Input.GetActionStrength("p1_run") > 0;
            if (!isRunPressed)
            {
                ChangeState(WalkState);
            }

            //Move the player
            Vector2 movement = new Vector2();
            movement.x       = sideInput * RunAcceleration;
            //movement.x = Mathf.Abs(Velocity.x) + Mathf.Abs(movement.x) > MaxRunSpeed ? (MaxRunSpeed - Mathf.Abs(Velocity.x)) * Mathf.Sign(movement.x) : movement.x;
            ApplyForce2D(movement);

            //Update the walking animation speed
            PlayerSprite.Frames.SetAnimationSpeed("Walk", Mathf.Max((Mathf.Abs(Velocity.x)) + 2, (Mathf.Abs(Velocity.x) / 2) + 8));

            CanFall();
            CanJump();

            //Enforce walk speed limit
            if (Math.Abs(Velocity.x) >= MaxRunSpeed)
            {
                Velocity.x = ClampedInterpolation.Lerp(Velocity.x, MaxRunSpeed * Mathf.Sign(Velocity.x), GetElapsedTimeInState());
            }
        }, () =>
        { //Exit State
        });


        JumpState = new ActorState(() =>
        { //Enter State
            GD.Print("Jump");

            PlayerSprite.SetAnimation("Jump");
            SnapToGround = false;

            JumpSpeedBuffer = Velocity.x;

            //Add the initial force of the jump
            ApplyForce2D(new Vector2(0, JumpSpeed));
        }, (float delta) =>
        { //Process State
        }, (float delta) =>
        { //State Physics Processing
            float jumpTime = GetElapsedTimeInState();
            DebugText.Display("jumpTime", "Jump Time: " + jumpTime);

            //Add some force for extra air time if the jump button is held
            ApplyForce2D(Vector2.Up, Gravity.y * (1 - JumpSustainGravityMultiplier));

            //Allow slight player movement
            float sideInput  = Input.GetActionStrength("p1_right") - Input.GetActionStrength("p1_left");
            Vector2 movement = new Vector2();
            movement.x       = sideInput * (AirAcceleration);
            ApplyForce2D(movement);

            //Exit state if required
            bool jumpPressed = Input.GetActionStrength("p1_jump") > 0;
            if (!jumpPressed || jumpTime > MaxJumpTime)
            {
                ChangeState(FallState);
            }
            if (IsOnFloor())
            {
                ChangeState(StandState);
            }

            //Enforce air speed limit
            float speedLimit = Mathf.Min(Mathf.Abs(JumpSpeedBuffer), MaxRunSpeed);
            if (Mathf.Abs(Velocity.x) >= speedLimit)
            {
                Velocity.x = ClampedInterpolation.Lerp(Velocity.x, speedLimit * Mathf.Sign(Velocity.x), GetElapsedTimeInState());
            }
        }, () =>
        { //Exit State
            DebugText.Remove("jumpTime");
        });


        FallState = new ActorState(() =>
        { //Enter State
            GD.Print("Fall");

            SnapToGround = false;
            PlayerSprite.SetAnimation("Jump");

            JumpSpeedBuffer = Velocity.x;
        }, (float delta) =>
        { //Process State
        }, (float delta) =>
        { //State Physics Processing
            //Allow slight player movement
            float sideInput  = Input.GetActionStrength("p1_right") - Input.GetActionStrength("p1_left");
            Vector2 movement = new Vector2();
            movement.x       = sideInput * (AirAcceleration);
            ApplyForce2D(movement);

            if (IsOnFloor() == true)
            {
                ChangeState(StandState);
            }

            //Enforce air speed limit

            /* if(PreviousState == RunState) {
             *  if (Math.Abs(Velocity.x) > MaxRunSpeed) { Velocity.x = Interpolation.Lerp(Velocity.x, MaxRunSpeed * Mathf.Sign(Velocity.x), GetElapsedTimeInState()); }
             * } else {
             *  if (Math.Abs(Velocity.x) > MaxWalkSpeed) { Velocity.x = Interpolation.Lerp(Velocity.x, MaxWalkSpeed * Mathf.Sign(Velocity.x), GetElapsedTimeInState()); }
             * } */
            float speedLimit = Mathf.Min(Mathf.Abs(JumpSpeedBuffer), MaxRunSpeed);
            if (Mathf.Abs(Velocity.x) >= speedLimit)
            {
                Velocity.x = ClampedInterpolation.Lerp(Velocity.x, speedLimit * Mathf.Sign(Velocity.x), GetElapsedTimeInState());
            }
        }, () =>
        { //Exit State
        });
    }
Ejemplo n.º 7
0
    public override void AReady()
    {
        PlayerSprite      = GetNodeOrNull(new NodePath("PlayerSprite")) as AnimatedSprite3D;
        ActorDetectorArea = GetNodeOrNull(new NodePath("ActorDetector")) as Area;
        FloorRayCast      = GetNodeOrNull(new NodePath("FloorRayCast")) as RayCast;

        if (PlayerSprite == null || ActorDetectorArea == null || FloorRayCast == null)
        {
            GD.PrintErr("One or multiple required child nodes could not be found! Some features won't work!");
        }

        InputManager = new InputManager(PlayerNumber);

        StandState = new ActorState(() =>
        { //Enter State
            SnapToGround = true;
        }, (float delta) =>
        { //Process State
            if (Mathf.Abs(Velocity.x) > 0.5f)
            {
                PlayerSprite.SetAnimation(PlayerAnimation.WALK);
                PlayerSprite.Frames.SetAnimationSpeed(PlayerAnimation.WALK, (Mathf.Abs(Velocity.x)) + 5);
            }
            else
            {
                PlayerSprite.SetAnimation(PlayerAnimation.IDLE);
            }
        }, (float delta) =>
        { //State Physics Processing
            if (InputManager.DirectionalInput.x != 0)
            {
                ChangeState(WalkState);
            }
            CanFall();
            CanJump();
            CanCrouch();
        }, () =>
        { //Exit State
        });

        WalkState = new ActorState(() =>
        { //Enter State
            SetSpeedLimit(WalkSpeed);
            SnapToGround = true;
        }, (float delta) =>
        { //Process State
            if (Mathf.Sign(Velocity.x) == Mathf.Sign(InputManager.DirectionalInput.x) || Velocity.x == 0)
            {
                PlayerSprite.SetAnimation(PlayerAnimation.WALK);
                PlayerSprite.Frames.SetAnimationSpeed(PlayerAnimation.WALK, (Mathf.Abs(Velocity.x)) + 5);
            }
            else
            {
                PlayerSprite.SetAnimation(PlayerAnimation.TURN);
            }
        }, (float delta) =>
        { //State Physics Processing
            if (InputManager.DirectionalInput.x == 0)
            {
                ChangeState(StandState);
            }
            if (InputManager.RunPressed || InputManager.AltRunPressed)
            {
                ChangeState(RunState);
            }
            CanFall();
            CanJump();
            CanCrouch();

            float force = InputManager.DirectionalInput.x * WalkAcceleration;
            ApplyForce2D(new Vector2(force, 0));
        }, () =>
        { //Exit State
        });

        RunState = new ActorState(() =>
        { //Enter State
            SetSpeedLimit(RunSpeed);
            SnapToGround = true;
        }, (float delta) =>
        { //Process State
            if (Mathf.Sign(Velocity.x) == Mathf.Sign(InputManager.DirectionalInput.x) || Velocity.x == 0)
            {
                PlayerSprite.SetAnimation(PlayerAnimation.WALK);
                PlayerSprite.Frames.SetAnimationSpeed(PlayerAnimation.WALK, (Mathf.Abs(Velocity.x)) + 7);
            }
            else
            {
                PlayerSprite.SetAnimation(PlayerAnimation.TURN);
            }
        }, (float delta) =>
        { //State Physics Processing
            if (InputManager.DirectionalInput.x == 0)
            {
                ChangeState(StandState);
            }
            if (!InputManager.RunPressed && !InputManager.AltRunPressed)
            {
                ChangeState(WalkState);
            }
            if (GetElapsedTimeInState() > LongRunTime)
            {
                ChangeState(LongRunState);
            }
            CanFall();
            CanJump();
            CanCrouch();

            float force = InputManager.DirectionalInput.x * RunAcceleration;
            ApplyForce2D(new Vector2(force, 0));
        }, () =>
        { //Exit State
        });

        LongRunState = new ActorState(() =>
        { //Enter State
            PlayerSprite.SetAnimation(PlayerAnimation.LONG_RUN);
            SetSpeedLimit(LongRunSpeed);
            SnapToGround = true;
        }, (float delta) =>
        { //Process State
            if (Mathf.Sign(Velocity.x) == Mathf.Sign(InputManager.DirectionalInput.x) || Velocity.x == 0)
            {
                PlayerSprite.SetAnimation(PlayerAnimation.LONG_RUN);
                PlayerSprite.Frames.SetAnimationSpeed(PlayerAnimation.LONG_RUN, (Mathf.Abs(Velocity.x)) + 10);
            }
            else
            {
                PlayerSprite.SetAnimation(PlayerAnimation.TURN);
            }
        }, (float delta) =>
        { //State Physics Processing
            if (InputManager.DirectionalInput.x == 0)
            {
                ChangeState(StandState);
            }
            if (!InputManager.RunPressed && !InputManager.AltRunPressed || Mathf.Sign(Velocity.x) != Mathf.Sign(InputManager.DirectionalInput.x))
            {
                ChangeState(WalkState);
            }
            CanFall();
            CanJump();
            CanCrouch();

            float force = InputManager.DirectionalInput.x * LongRunAcceleration;
            ApplyForce2D(new Vector2(force, 0));
        }, () =>
        { //Exit State
        });

        JumpState = new ActorState(() =>
        { //Enter State
            SnapToGround = false;

            float speed = Mathf.Abs(Velocity.x);
            if (speed > RunSpeed)
            {
                ApplyForce2D(new Vector2(0, LongRunJumpForce));
                PlayerSprite.SetAnimation(PlayerAnimation.HIGH_JUMP);
            }
            else if (speed > 0.5f)
            {
                ApplyForce2D(new Vector2(0, WalkJumpForce));
                PlayerSprite.SetAnimation(PlayerAnimation.JUMP);
            }
            else if (PreviousState == CrouchState)
            {
                ApplyForce2D(new Vector2(0, IdleJumpForce));
                PlayerSprite.SetAnimation(PlayerAnimation.CROUCH);
                SetPlayerCollisionShape(PlayerCollisionShape.SMALL);
            }
            else
            {
                ApplyForce2D(new Vector2(0, IdleJumpForce));
                PlayerSprite.SetAnimation(PlayerAnimation.JUMP);
            }
        }, (float delta) =>
        { //Process State
          //DebugText.Display("P" + PlayerNumber + "_JumpSusTime", "P" + PlayerNumber + " Jump Sustain Time: " + (MaxJumpSustainTime - GetElapsedTimeInState()).ToString());
        }, (float delta) =>
        { //State Physics Processing
            if (IsOnFloor())
            {
                ChangeState(StandState);
            }
            if ((!InputManager.JumpPressed && !InputManager.AltJumpPressed) || GetElapsedTimeInState() > MaxJumpSustainTime)
            {
                ChangeState(FallState);
            }

            if (SpeedLimit >= LongRunSpeed && (InputManager.RunPressed || InputManager.AltRunPressed))
            {
                SetSpeedLimit(LongRunSpeed);
            }
            else if (InputManager.RunPressed || InputManager.AltRunPressed)
            {
                SetSpeedLimit(RunSpeed);
            }
            else
            {
                SetSpeedLimit(WalkSpeed);
            }

            //Add some force for extra air time if the jump button is held
            ApplyForce2D(Vector2.Up, Gravity.y * (1 - JumpSustainGravityMultiplier));

            float force = InputManager.DirectionalInput.x * AirHorizontalAcceleration;
            ApplyForce2D(new Vector2(force, 0));
        }, () =>
        { //Exit State
            //DebugText.Remove("P" + PlayerNumber + "_JumpSusTime");
            SetPlayerCollisionShape(GetDefaultCollisionShape());
        });

        SpinJumpState = new ActorState(() =>
        { //Enter State
            SnapToGround = false;
            PlayerSprite.SetAnimation(PlayerAnimation.SPIN_JUMP);

            float speed = Mathf.Abs(Velocity.x);
            if (speed > RunSpeed)
            {
                ApplyForce2D(new Vector2(0, LongRunJumpForce));
            }
            else if (speed > 0.5f)
            {
                ApplyForce2D(new Vector2(0, WalkJumpForce));
            }
            else if (PreviousState == CrouchState)
            {
                ApplyForce2D(new Vector2(0, IdleJumpForce));
            }
            else
            {
                ApplyForce2D(new Vector2(0, IdleJumpForce));
            }
        }, (float delta) =>
        { //Process State
          //DebugText.Display("P" + PlayerNumber + "_JumpSusTime", "P" + PlayerNumber + " Jump Sustain Time: " + (MaxJumpSustainTime - GetElapsedTimeInState()).ToString());
        }, (float delta) =>
        { //State Physics Processing
            if (IsOnFloor())
            {
                ChangeState(StandState);
            }
            if ((!InputManager.JumpPressed && !InputManager.AltJumpPressed) || GetElapsedTimeInState() > MaxJumpSustainTime)
            {
                ChangeState(FallState);
            }

            if (SpeedLimit >= LongRunSpeed && (InputManager.RunPressed || InputManager.AltRunPressed))
            {
                SetSpeedLimit(LongRunSpeed);
            }
            else if (InputManager.RunPressed || InputManager.AltRunPressed)
            {
                SetSpeedLimit(RunSpeed);
            }
            else
            {
                SetSpeedLimit(WalkSpeed);
            }

            //Add some force for extra air time if the jump button is held
            ApplyForce2D(Vector2.Up, Gravity.y * (1 - JumpSustainGravityMultiplier));

            float force = InputManager.DirectionalInput.x * AirHorizontalAcceleration;
            ApplyForce2D(new Vector2(force, 0));
        }, () =>
        { //Exit State
          //DebugText.Remove("P" + PlayerNumber + "_JumpSusTime");
        });

        FallState = new ActorState(() =>
        { //Enter State
            if (InputManager.DirectionalInput.y < CrouchInputThreshold)
            {
                PlayerSprite.SetAnimation(PlayerAnimation.CROUCH);
                SetPlayerCollisionShape(PlayerCollisionShape.SMALL);
            }
            else if (PreviousState == SpinJumpState)
            {
                PlayerSprite.SetAnimation(PlayerAnimation.SPIN_JUMP);
            }
            else
            {
                PlayerSprite.SetAnimation(PlayerAnimation.FALL);
            }
            SnapToGround = false;
        }, (float delta) =>
        { //Process State
        }, (float delta) =>
        { //State Physics Processing
            if (IsOnFloor())
            {
                if (InputManager.DirectionalInput.y < CrouchInputThreshold)
                {
                    ChangeState(CrouchState);
                }
                else
                {
                    ChangeState(StandState);
                }
            }

            if (SpeedLimit >= LongRunSpeed && (InputManager.RunPressed || InputManager.AltRunPressed))
            {
                SetSpeedLimit(LongRunSpeed);
            }
            else if (InputManager.RunPressed || InputManager.AltRunPressed)
            {
                SetSpeedLimit(RunSpeed);
            }
            else
            {
                SetSpeedLimit(WalkSpeed);
            }

            float force = InputManager.DirectionalInput.x * AirHorizontalAcceleration;
            ApplyForce2D(new Vector2(force, 0));
        }, () =>
        { //Exit State
            SetPlayerCollisionShape(GetDefaultCollisionShape());
        });

        CrouchState = new ActorState(() =>
        { //Enter State
            SnapToGround = true;

            var angle = Mathf.Rad2Deg(Mathf.Acos(FloorRayCast.GetCollisionNormal().Dot(FloorNormal)));
            if (angle >= SlideMinAngle)
            {
                ChangeState(SlideState);
                return;
            }

            PlayerSprite.SetAnimation(PlayerAnimation.CROUCH);
            SetPlayerCollisionShape(PlayerCollisionShape.SMALL);
        }, (float delta) =>
        { //Process State
        }, (float delta) =>
        { //State Physics Processing
            Transform slightlyRight;
            slightlyRight.origin    = Transform.origin;
            slightlyRight.basis     = Transform.basis;
            slightlyRight.origin.x += 0.4f;

            Transform slightlyLeft;
            slightlyLeft.origin    = Transform.origin;
            slightlyLeft.basis     = Transform.basis;
            slightlyLeft.origin.x -= 0.4f;

            if (!TestMove(Transform, new Vector3(0, 0.5f, 0)))
            {
                if (InputManager.DirectionalInput.y >= CrouchInputThreshold)
                {
                    ChangeState(StandState);
                }
                CanJump();
                CanFall();
            }
            else if (!TestMove(slightlyRight, new Vector3(0, 0.5f, 0)))
            {
                ApplyForce2D(new Vector2(0.4f, 0));
            }
            else if (!TestMove(slightlyLeft, new Vector3(0, 0.5f, 0)))
            {
                ApplyForce2D(new Vector2(-0.4f, 0));
            }
            else
            {
                if (InputManager.JumpJustPressed || InputManager.AltJumpJustPressed)
                {
                    ApplyForce2D(new Vector2(CrouchBoostForce.x * InputManager.DirectionalInput.x, CrouchBoostForce.y));
                }
            }
        }, () =>
        { //Exit State
            SetPlayerCollisionShape(GetDefaultCollisionShape());
        });

        SlideState = new ActorState(() =>
        { //Enter State
            SetSpeedLimit(SlideSpeed);
            SnapToGround = true;

            PlayerSprite.SetAnimation(PlayerAnimation.SLIDE);
            autoPlayerFacing = false;
        }, (float delta) =>
        { //Process State
            //Manually flip the sprite according to the movement direction
            if (Velocity.x > 0)
            {
                PlayerSprite.SetFlipH(false);
            }
            else if (Velocity.x < 0)
            {
                PlayerSprite.SetFlipH(true);
            }
        }, (float delta) =>
        { //State Physics Processing
            Vector3 normal = FloorRayCast.GetCollisionNormal();
            float angle    = Mathf.Rad2Deg(Mathf.Acos(normal.Dot(FloorNormal)));
            if (angle < SlideMinAngle)
            {
                ChangeState(InputManager.DirectionalInput.y < CrouchInputThreshold ? CrouchState : StandState);
            }
            CanFall();
            CanJump();

            float force = Mathf.Sign(normal.x) * SlideAcceleration;
            ApplyForce2D(new Vector2(force, 0));
        }, () =>
        { //Exit State
            autoPlayerFacing = true;
        });
    }