Hello Guest

Author Topic: State machines  (Read 3713 times)

0 Members and 1 Guest are viewing this topic.

usdaproved

  • Member
  • Posts: 10
    • View Profile
    • Essay Blog
State machines
« on: November 16, 2013, 02:04:13 PM »
I can't for the life of me figure out how to even begin implementing this.

Maybe I'm just a total derp.

So what I want to do is an addition to the pong game: power ups for the paddles. I want a state where it's just the regular paddle, and a state where the power up is activated, like longer paddle, or faster movement. So what I have setup is, the game now knows who hit the ball last. So when the ball collides with the power up, it will know who to change. So I could just send the signal from within that collision test, right? Well, once I have the state machine setup... so how to do that?
Follow me: https://twitter.com/usdaproved | Read my stuffs: http://usdaproved.tumblr.com/ | Thanks for reading things I write :D

Kyle

  • Administrator
  • Member
  • Posts: 259
    • View Profile
Re: State machines
« Reply #1 on: November 16, 2013, 03:09:29 PM »
There's a StateMachine component in Otter right now that you could use to make this a little easier.

Each state has three methods associated with it: begin, update, and end.  When a state machine switches states, it calls the end method of the current state, followed by the begin method of the new state, and then it will continuously call the update method of the new state until the state is changed again.

A state machine also usually only runs one state at a time, so for a power up system you'd only be able to have one power at any given time.

So maybe for a power up that makes your paddle longer, you'd have an PowerStateLong state for your state machine that keeps track of the PowerState.  In the begin method of this state you could have the paddle run the code to make its graphic and its hitbox bigger.  For a speed up power, you could have a state where in the begin function you set the movement speed variable to something higher.  Also in the exit methods for these states you could set the paddle back to normal, or have a PowerStateNormal that restores the paddle back to normal.

If you want to be able to stack multiple power ups, you might want to look into something more like a component system where you can add powers to the paddles and have the powers affect the paddle.

Does that make sense?

usdaproved

  • Member
  • Posts: 10
    • View Profile
    • Essay Blog
Re: State machines
« Reply #2 on: November 16, 2013, 03:27:30 PM »
Yeah that makes total sense. That is, in fact, what I am trying to do. Use the Otter state machine. I definitely neglected to specify that. How would I use the otter state machine with the paddle, given that I do only want one power up at a time?

See I was thinking something like this:

Code: [Select]
State paddleLengthenedState = new State(EnterState(), UpdateState(), ExitState());
Paddle.addState(paddleLengthenedState);

// Then later on when power up collected
Paddle.State = paddleLengthenedState

This is certainly not the proper way of doing this in your framework, but I hope I am expressing what I was trying to do.

My question that I failed to ask was: how do I use your state machine?
Follow me: https://twitter.com/usdaproved | Read my stuffs: http://usdaproved.tumblr.com/ | Thanks for reading things I write :D

Kyle

  • Administrator
  • Member
  • Posts: 259
    • View Profile
Re: State machines
« Reply #3 on: November 16, 2013, 03:44:21 PM »
Heres an example from All the King's Men for the guards:

First I have ints for each state (I'm planning on making another state machine that uses enums, but this one was ported from my flash framework so it was easier for me to use.)
Code: [Select]
        int
            stateLocked,
            stateAvailable,
            stateControlled,
            stateDead;

Then make the state machine

Code: [Select]
        StateMachine sm = new StateMachine();
Then in the guard's constructor I set up the state machine:

Code: [Select]
            AddComponent(sm);

            sm.AddState("Locked", "Available", "Controlled", "Dead");

            sm.ChangeState(stateLocked);

The AddState() function used there is a special shortcut that uses reflection to create states from the names Locked, Available, Controlled, and Dead.  It will automatically find the functions in the Entity named Enter, Update, and Exit, and also find the ints named stateLocked, stateAvailable, etc.

Heres some example code from the guard that uses the states:

Code: [Select]
        void EnterLocked() {
            Color = Color.Gray;
            CooldownColor = false;
        }

        void UpdateLocked() {
            var distToKing = Util.Distance(X, Y, King.Instance.X, King.Instance.Y);
            if (distToKing < 500) {
                if (Cursor.Instance.HasAvailableButton) {
                    sm.ChangeState(stateAvailable);
                }
            }
            if (Timer == 30) {
                imageButton.Visible = false;
                imageButton.Scale = 1;
            }

            if (Global.GuardsControlled >= 8) {
                if (distToKing > 1200 && distToKing < 1300) {
                    RemoveSelf();
                }
            }
        }

        void EnterAvailable() {
            Color = Color.White;
           
            //get trigger button
            ControlButton = Cursor.Instance.NextAvailableButton;

            var c = Global.PlayerSession.Controller;
            if (ControlButton == c.A) {
                imageButton.Frame = 0 + (Global.UsingJoystick ? 0 : 4);
                OverlayColor = Color.Green;
            }
            else if (ControlButton == c.B) {
                imageButton.Frame = 3 + (Global.UsingJoystick ? 0 : 4);
                OverlayColor = Color.Red;
            }
            else if (ControlButton == c.X) {
                imageButton.Frame = 1 + (Global.UsingJoystick ? 0 : 4);
                OverlayColor = Color.Blue;
            }
            else if (ControlButton == c.Y) {
                imageButton.Frame = 2 + (Global.UsingJoystick ? 0 : 4);
                OverlayColor = Color.Yellow;
            }

            OverlayAlpha = 0.75f;

            imageButton.Visible = true;
            imageButton.Scale = 2;
            Tween(imageButton, new { ScaleX = 1, ScaleY = 1 }, 30).Ease(Ease.ElasticOut);
            Tween(this, new { OverlayAlpha = 0.2f }, 30);

            CooldownColor = true;

        }

        void UpdateAvailable() {
            var distToKing = Util.Distance(X, Y, King.Instance.X, King.Instance.Y);
            if (distToKing >= 500) {
                sm.ChangeState(stateLocked);
            }
            if (ControlButton != null) {
                if (ControlButton.Pressed) {
                    sm.ChangeState(stateControlled);
                }
            }
            if (sm.Timer > 30) {
                OverlayAlpha = Util.SinScaleClamp((sm.Timer - 30) * 2, 0.1f, 0.3f);
            }
        }

        void ExitAvailable() {
            if (ControlButton != null) {
                Cursor.Instance.AddAvailableButton(ControlButton);
            }
            Tween(this, new { OverlayAlpha = 0 }, 30);
            Tween(imageButton, new { ScaleX = 0, ScaleY = 0 }, 30);
        }

        void EnterControlled() {
            TakeControl();
            Camera.Instance.AddTarget(this);
            Camera.Instance.AddTarget(King.Instance);
            Camera.Instance.AddTarget(King.Instance);
            Camera.Instance.RemoveTarget(Cursor.Instance);
            Tween(imageButton, new { ScaleX = 0, ScaleY = 0 }, 30).Ease(Ease.BackOut);
            PopScaleY = 2;
            PopScaleX = 0.5f;
            OverlayAlpha = 1;
            Tween(this, new { PopScaleX = 1, PopScaleY = 1 }, 60).Ease(Ease.ElasticOut);
            Tween(this, new { OverlayAlpha = 0 }, 60);
            Effects.ColorFlash(OverlayColor);
        }

        void UpdateControlled() {
            if (sm.Timer == 30) {
                imageButton.Visible = false;
                imageButton.Scale = 1;
            }

            var distToKing = Util.Distance(X, Y, King.Instance.X, King.Instance.Y);
            if (CancelButton.Pressed) {
                LoseControl();
                sm.ChangeState(stateLocked);
            }

            if (distToKing > 800) {
                var vecToKing = new Vector2(King.Instance.X - X, King.Instance.Y - Y);
                vecToKing.Normalize(200);
                PushSpeedX += (int)vecToKing.X;
                PushSpeedY += (int)vecToKing.Y;
            }

            if (Global.PlayerSession.Controller.R2.Pressed) {
                SoundEffects.Run.Play();
            }

            if (Math.Abs(Movement.Speed.X) > 0 || Math.Abs(Movement.Speed.Y) > 0) {
                walkTimer +=  Util.Distance(0, 0, Movement.Speed.X, Movement.Speed.Y) * 0.1f;
                if (walkTimer > 500) {
                    walkTimer -= 500;
                    if (walkStepLeft) {
                        SoundEffects.RunStep1.Play();
                    }
                    else {
                        SoundEffects.RunStep2.Play();

                    }
                    walkStepLeft = !walkStepLeft;
                }
            }
           
        }

        void ExitControlled() {
            Camera.Instance.RemoveTarget(this);
            Camera.Instance.RemoveTarget(King.Instance);
            Camera.Instance.RemoveTarget(King.Instance);
        }

Does that help at all?

usdaproved

  • Member
  • Posts: 10
    • View Profile
    • Essay Blog
Re: State machines
« Reply #4 on: November 16, 2013, 03:55:34 PM »
Perfect! This is exactly what I needed. I feel as though I could learn quite a bit from All the King's Men source code. Thanks for being so helpful, I will definitely continue to learn your framework.
Follow me: https://twitter.com/usdaproved | Read my stuffs: http://usdaproved.tumblr.com/ | Thanks for reading things I write :D

Kyle

  • Administrator
  • Member
  • Posts: 259
    • View Profile
Re: State machines
« Reply #5 on: November 16, 2013, 04:29:22 PM »
I'm planning on releasing the source sometime soon, hopefully next week ;D