Learning GameStates

Started by
4 comments, last by Tangletail 8 years, 6 months ago

Hi,

I'm looking at using game states in my programming So I went looking and found Lazy Foo's tutorial on states.

I found I could understand it even though I still don't like pointers! smile.png

Here is a small program I have made to learn how to use game states. I understand if I want to know the history of what state I have been in I could use a stack, At this stage I don't need a stack to keep track of each state. my games are not big enough yet.

Here is some code


int main()
{
	GameState *CurrentState = NULL;
	CurrentState = new Title();
	bool ExitGame = false;
	while (ExitGame == false)
	{
		CurrentState->ProcessEvents();
		CurrentState->Update();
		CurrentState->Render();

		if(CurrentState->GetNextState() != -1)
		{
			if (CurrentState->GetNextState() == GameStates::ExitScreen)
			{
				delete CurrentState;
				ExitGame = true;
			}
			else if (CurrentState->GetNextState() == GameStates::SplashScreen)
			{
				delete CurrentState;
				CurrentState = new Splash();
			}
			else if (CurrentState->GetNextState() == GameStates::TitleScreen)
			{
				delete CurrentState;
				CurrentState = new Title();
			}
			else if (CurrentState->GetNextState() == GameStates::HighScoreScreen)
			{
				delete CurrentState;
				CurrentState = new HighScores();
			}
		}
	}
	return 0;
}

And here is one of the states....

Title.H


#pragma once
#include "GameState.h"
#include "SFML/Graphics.hpp"
#include <iostream>
class Title : public GameState
{
public:
	Title();
	void ProcessEvents();
	void Update();
	void Render();
	~Title();
};

Title::Title()
{
	std::cout << "Title" << std::endl;
	m_NextState = -1;
}
void Title::Update()
{
}
void Title::ProcessEvents()
{
	if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
	{
		m_NextState = GameStates::ExitScreen;
	}
	if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
	{
		m_NextState = GameStates::SplashScreen;
	}
	if (sf::Keyboard::isKeyPressed(sf::Keyboard::H))
	{
		m_NextState = GameStates::HighScoreScreen;
	}
}

void Title::Render()
{
}

Title::~Title()
{

}

This is the first time I have used the virtual keyword in C++.

This is my base gamestate class.


#pragma once
enum GameStates
{
	NullGamestate,
	SplashScreen,
	TitleScreen,
	HighScoreScreen,
	ExitScreen
};

class GameState
{
public:
	virtual void Update();
	virtual void ProcessEvents();
	virtual void Render();
	int GetNextState();
	virtual ~GameState();
	int m_NextState;
};

My question is this.

Am I using this correctly? In a way I look at it like each state, like TitleScreen, Highscores etc is its own program. It could be one state is Pacman, and another state is space invaders etc.

Another question also is How would I be able to use this to create a Pause state in my gameplay?

Is it something like when Paused is pressed through out gameplay, currently I destroy the previous state. But If I am pausing the game I want to take it back to exactly the same state as it was previously. Is that where I would have to put my gameplay state into a stack, run my pause state, then when the player unpauses, delete the pause state completely, and pop the gameplay state off the stack, which has saved all the variables like player position and what was on the screen at the time.

Short question for above would be if I pushed the gameplay state onto a stack, ran the pause state, then popped the gameplay state back as the current state, then it would continue where it left off? (In theory anyway!).

I actually need a stack at least for just the gameplay so I can pause don't I? :)

Does this sound like I am understanding this?

Thanks for reading

werdy666

Advertisement

Think of a stack of states.

Rather than just having one active state and nothing else, you push the new active state onto the top of the stack and when exiting that state you can pop the active state off the top, returning to what was active previously.

You should allow a way to wipe the stack though, otherwise for example you can't exit without first retuning to the menu. However this might be what you want.

Something else to consider is the ability to run more than one state at once, to do this give each state an update and draw method, and call all active states in your game loop. This would allow you to update multiplayer games in your playing state for example whilst a pause menu is visible. Without this ALL game operations stop whilst paused.

Give it some thought and let me know if this helps!

You are mixing jobs between the state manager and the state factory.
You should also have somewhere in there a game class, which I suspect would end up being mixed in as well if you continue this way.

This site should give you better insight into keeping things separated. It is a more modular approach to the same task.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

werdy666, what L. Spiro said is correct, I can't think of a more elegant way of managing the game states.

In the case you need help, here follows a simple implementation of a state machine class that allows you to switch between game states at run-time. It was based on L. Spiro articles and I'm still using it today to create demos and testbeds:

https://github.com/irlanrobson/IR-Engine/tree/master/IREngine

Study the CGame, CState, and the CStateFactory classes. Also, make sure you delay the game-state switch in the game. Otherwise you'll be walking on invalid memory lands.

Thanks for the link L. Spiro. I will catch up on some reading and learning!

I will check out your source code too Irlan :)

?

You are mixing jobs between the state manager and the state factory.
You should also have somewhere in there a game class, which I suspect would end up being mixed in as well if you continue this way.

This site should give you better insight into keeping things separated. It is a more modular approach to the same task.


L. Spiro

L spiro is just the god of Engine design. Honestly, I wish I knew about that post before I decided to kill some management effort on my project.

Currently, my state implementation is actually a version of polymorphism and DX9 swap chains. My engine's loop is in Lua. But I can translate it to C++

Basically. My states are in separate classes that derives from one super class.


Class BaseState {

virtual void Update()
virtual void Info()        //Called for debugging. Prints a String that tells me what state the update is currently in.
void SetNextState( BaseState* state )


private
BaseState* _next_State*

}

When the game loops, it uses a system of two pointers. Each state runs through it's process, then at the end tells the pointer to point to it's self if not told other wise.

When it recieves an event to swap to another state, the game will continue to run through the update, set the pointer we are swapping too, and then we are in a new state. The process repeats.


Class StateChain {

void editNextChain( BaseState* state)
void Update (int deltaTime)
void Reset()    //Resets our states so we can reuse animations, or opening sequences.
void SwapChain()

private
bool _is_state_changed

BaseState* Update
BaseState* Next()

}

void StateChain::EditNextChain() {
if (!_is_state_changed){
    _is_state_changed = true;
    set swapped chain.
}

I also have a small system that lets me combine states together. After all. Most of your game can just be seen as a combination of states. It enters a state where it collects UI input. It enters a state where it draws to the screen. It enters a state for when we Enter the menu. An inventory screen is just a state. Why not just make it modular? The benefits outweighs the costs.

Obviously, we want to do some complex stuff without programming MORE. Or making code a complete mess to read.

For example. I moved my UI controls into a separate state. This system pumps updates to what ever state we prioritize. So, if we open an inventory, and prevent the user from moving around. We can instead remove the controls from the main update. Add the inventory state to the front. And the main update to the end. The result is controls are now attached to the inventory state, and the world still updates around him.

OR! The hero died in his quest to save the princess. We want the screen to turn dark and go to the game over screen. The game plays an animation of him falling to the ground, and quickly adds a state. With each loop, the screen gets darker and darker while the world is updating around you. When the game is finally over. The main game state is removed, and you are just presented with the game over state, and your dialogue options. The main game state is no longer updating. If the player goes to main menu, we just say...

GameChain->EditNextChain(PlayerControlls->SetNextState(MainMenu.Reset()))

GameLoop


StateChain* GameChain = new StateChain();
Create your state pointers.
PreMake certain states.

while (!terminating) {

GameChain->Update(dt);
GameChain->Swap();
Render();
}

The system was actually inspired by the Stratergy and Chain of Responsibility pattern. I just named it the "Mixer Master", after a highway in Dallas, TX. Side note. Obviously this code I wrote on it's own is bug prone, as I skipped details.

z4JTqvK.jpg

In my actual system. I have two pointers in each state. When we tell the StateChain to change our next state, it informs it that when we are done with our current update, we clear our first pointer in all states, then swap to the second pointer. When we call setNextState(), it actually sets the second pointer. There is also the append and remove functions which allows me to remove menus from the loop without destroying the entire chain. Append will add a new state into the chain. Remove will only remove the state that called it.

GOTCHAS:

The order of updates could potentially matter a lot. For example. if you want a drag and drop system for your inventory system. The location of your mouse matters a lot. If the mouse is over your GUI, you need to disable actions for the game. Depending on how you implemented the system, your inventory will need to come first.

Implementing everything into separate states could lead to some problems. Only do this for systems that you know should be in a state. For example. It will barely make sense to implement a quest system into a state. BUT, adding your render system into a state isn't really a good move. It's better left outside of the chain. Same with NPCs, items, ect.

A lot of your stuff will be global singletons. If this is a problem to you, you can add your own methods. But really, this shouldn't be too much of a problem, given that in a game loop, a lot of your data needs to be accessed someway some how. This just means you have to add floating functions, or needless indirection.

BENEFITS:

Your game is now broken up into reusable parts that you can chain together for various effects.
Your update loop is more dynamic. Benefit of Polymorphism

Less code rewriting. BONUS! I want to fade out the screen? Just stick Effect Fade Out into my state chain!

Code is really well organized now. Into separate files even!

Swapping states is as easy peasy as calling a string of functions.

Cons

Pointers are evil. Not really much of a con. The performance cost behind it isn't severe enough for it to be a problem. Not unless you got a really stupid OS. If you suck at using raw pointers. Yeah it's gonna be a problem to write it yourself.

Virtual Functions. A common argument is that you shouldn't need them. And they hurt performance. The performance hit isn't really severe enough to matter. A lot of game engines actually uses virtual functions. It's software engineering at it's finest.

Singleton: I really got nothing to say that would defeat this con. You could technically abstract this sonovagun so badly it doesn't know who it is anymore. Singletons aren't necessarily evil... and there will be times when the singleton is pretty much the best option. The reality is... for my purpose, I really don't care. I don't expect the genre this game engine is being made for to actually require more than one state chain. Nor for threads to actually update it. Plus the addition of fail safes built into the StateChain class. Sooo.... YAGNI (You Aint Gonna Need It). Don't over-engineer for something the software is not meant for.

CREATE A MIXER MASTER TODAY! Not really, you are free to do your own thing.

EDIT: Looking at this design... I just realized with a bit of restructuring... I could actually make it where each menu is literally a separate state with it's own logic. Each time I open a menu, I can just append it to the end of the state, and it's drawn onto the screen in the order of it's state.

But that really doesn't matter if you have a draw system that doesn't give a crap about how you submit draw calls.

If you don't feel like fooling around with linked lists, you could use a vector. But this could cause some horrible stutters as the user opens more states. But it will die down when he gets to the max number of states opened at once.

This topic is closed to new replies.

Advertisement