Jump to content
  • Advertisement

GC : Explosive Balls (game state)

GoliathForge

1840 views

In order to stay out of trouble, I'm going back to work. :) 

Previous Blog Entry for this game challenge round.

Today's topic is my drop in game state manager system for moving between game screens such as an introduction sequence, main menu hierarchy, game play and first say on app termination. I don't remember where I initially found this gem because it has been quite a number of years that I've been reusing this. The concept is simple though. Manage screens as a stack. Push a screen pointer to the back to become active and pop to activate the previous. So how do we get a system like this up and running? The following are sloppy source grab copies. Two objects start us out, First a manager and an abstract base class. From the abstract, we derive the game screen objects. Each screen object handles their own behavior not knowing anything about the others.

// file : GameState.cpp

#include "olcPixelGameEngine.h" // <-- yours renderer may be different
#include "GameState.h"
#include "States/intro.h"
#include "States/game.h"
#include "States/menu.h"

using namespace Core;

std::shared_ptr<States::Intro>  introState;
std::shared_ptr<States::Menu>   menuState;
std::shared_ptr<States::Game>   gameState;

void StateManager::initialize(olc::PixelGameEngine* engine, vec2 clientSize)
{
	this->engine = engine;
	introState = std::make_shared<States::Intro>(this, clientSize);
	menuState = std::make_shared<States::Menu>(this, clientSize);
	gameState = std::make_shared<States::Game>(this, clientSize);

	states.push_back(menuState);

#ifdef _RELEASE
	states.push_back(introState);
#endif
}


void StateManager::shutdown()
{
	introState->shutdown();
	menuState->shutdown();
	gameState->shutdown();
}

void StateManager::changeState(States::Base_state* _state) { }
void StateManager::pushState(States::Base_state* _state)   { }
void StateManager::popState() { states.pop_back(); }
void StateManager::draw()     { states.back()->draw(); }
void StateManager::update(float deltaTime) { states.back()->update(deltaTime); }

void StateManager::handleEvents(eStateAction _action)
{
	switch (_action) {
	case eStateAction::pause:
		if (states.back()->name == "game_state")
			states.pop_back();
		break;
	case eStateAction::play:
		if (states.back()->name == "menu_state") {
			int w = engine->GetDrawTargetWidth();
			int h = engine->GetDrawTargetHeight();
			
			RECT rect = { 0, 0, w, h }; // <-- I know --^
			states.push_back(gameState);
		}
		break;
	case eStateAction::win:
		if (states.back()->name == "game_state")
		{   MessageBox(0, "you win", "", 0);
			//winState->winnerName = gameState->getWinnerName();
			//winState->winningTime = gameState->getTimeString();
			//states.push_back(winState);
		}
		break;
	case eStateAction::loose:
		if (states.back()->name == "game_state") {
		    MessageBox(0, "you loose", "", 0);
			//states.push_back(scoreState);
		}
		break;
	case eStateAction::quit:
		if (states.back()->name == "menu_state")
			PostQuitMessage(0);
		if (states.back()->name == "game_state") {
			::ShowCursor(true); // umm, you're counting me aren't you
			::ClipCursor(nullptr);
			states.pop_back();
		}
		if (states.back()->name == "score_state")
			states.pop_back();
		if (states.back()->name == "win_state") {
			// todo : record score
			gameState->reset();
			states.pop_back();
		}
		break;
	} // end switch(action)
}

 

 

 

 

 

 

 

 

 

With the manager out of the way, the base class for the stack-able objects.

// file : base_state.h

#ifndef _engine_core_states_base_state_h_
#define _engine_core_states_base_state_h_

#include <olcpixelgameengine.h>
#include <string>

namespace Core {
	namespace States {

		// abstract
		class Base_state
		{
		public:
			Base_state(void* _manager, std::string _name, vec2 _clientSize)
			{
				pManager = _manager;
				name = _name;
				clientSize = _clientSize;
			}

			virtual void initialize() = 0;
			virtual void shutdown() = 0;

			virtual void pause() = 0;
			virtual void resume() = 0;

			virtual void handleEvents() = 0;
			virtual void update(float _deltaTime) = 0;
			virtual void draw() = 0;s

			void* pManager;   // que sara sara
			std::string name; // todo : refactor string to numeric id? :)
			vec2 clientSize;
          
		protected:
			Base_state() { /* empty constructor */ }
		};
	}
}

#endif

Correct me if I'm wrong (please) but of all the reasons to use polymorphism, this case is one of the better (subtyping). To be able to keep like items that are different in a common container.  The derived class header would be as expected. Base class overloads in place plus specific functions and variables that would be required to make the new object behave as it will. Nothing new. I'll admit, I'm trapped in the common cycle that most online tutorials mold where the loop is divided into update / draw / check state / repeat. Is it correct? <shrug> So all this may be familiar and you hate it, or it's new and reader be cautious. The basic take away here is the stack concept.

On the main loop side, we declare a Core::StateManager instance and initialize. During the loop, update and draw calls to the manager fire. I feel I don't need to list any additional modules but will list an example derived menu class.

#include "menu.h"
#include "../GameState.h"

using namespace Core::States;


void Menu::initialize()
{
	rectPlay = { 360, 140, 440, 160 }; // 80 x 20
	rectQuit = { 360, 165, 440, 185 }; // need more data or buttons are a known size
}

void Menu::shutdown() { }
void Menu::pause() { }
void Menu::resume() { }
void Menu::handleEvents() { }
void Menu::update(float _deltaTime) { }

void Menu::draw()
{
	olc::Pixel colorDefault = olc::Pixel(255, 255, 255);
	olc::Pixel colorHover = olc::Pixel(255, 255, 0);

	//                                                                                           _
	//  .---  todo : don't like handling user input inside a draw routine...fix me  ----.    \_(O,O)_
	//  V                                                                               V        ~   \
	

	// ------------------- Game Menu -------------------------
	Core::StateManager* manager = (StateManager*)pManager;
	olc::PixelGameEngine* e = manager->engine;

	POINT cursorPos;
	cursorPos.x = manager->engine->GetMouseX();
	cursorPos.y = manager->engine->GetMouseY();

	e->DrawRect(rectPlay.left, rectPlay.top, rectPlay.right - rectPlay.left, rectPlay.bottom - rectPlay.top);
	e->DrawRect(rectQuit.left, rectQuit.top, rectQuit.right - rectQuit.left, rectQuit.bottom - rectQuit.top);

	std::string cursorInfo = "CursorPos ";
	char buf[16] = "";
	_itoa_s(int(cursorPos.x), buf, 10);
	e->DrawString(20, 20, std::string(buf));
	_itoa_s(int(cursorPos.y), buf, 10);
	e->DrawString(70, 20, std::string(buf));

	vec2 textOffset = { 25.f, 6.f };
	int x = int(rectPlay.left + textOffset.x);
	int y = int(rectPlay.top + textOffset.y);

	// Play -------------------------------------------------
	olc::Pixel textColor = colorDefault;
	if (::PtInRect(&rectPlay, cursorPos))
	{	textColor = colorHover;
		if(e->GetMouse(0).bPressed)
			manager->handleEvents(Core::eStateAction::play);
	}
	e->DrawString(x, y, "Play", textColor);
		
	// Quit --------------------------------------------------
	x = int(rectQuit.left + textOffset.x);
	y = int(rectQuit.top + textOffset.y);
	textColor = colorDefault;
	if (::PtInRect(&rectQuit, cursorPos))
	{	textColor = colorHover;
		if(manager->engine->GetMouse(0).bPressed)
		{	if (MessageBox(nullptr, "Are you sure you want to quit?", "Serious?", MB_YESNO) == IDYES)
				exit(0);
		}
	}
	manager->engine->DrawString(x, y, "Quit", textColor);
}

The game screen state would be similar but behave as what the main game play would be. Every valid screen pointer lives the life of the application but only the one at the back of the stack is active at a given time. This has served me well for adding game state transitions in custom work. 

Input and drawing are engine responsibilities so there is a member pointer to contend with in the manager. Perhaps not the best approach, but I don't know of alternatives other than a hard global or worse, singleton. But a subclass gets this functionality through the manager held engine pointer. Crappy note to end on perhaps, but that's where I am. It works, I called it good and have been reusing a number of times. Tips from the leet are always appreciated assuming my words are better than a hodgepodge of nonsense.   

 

edit log

  • 2019_05_03 Code cleanup


6 Comments


Recommended Comments

It turns out the pixel api thing has some neat extension headers dealing with sprite transforms. The 'engine' has static access so the necessity to pass an engine pointer to draw above can be refactored out. What these tools don't have is sprite animation. I'm happy for this. Don't try to do everything, do that put pixel thing you do and do it well. Modulate this... LOL...I'm on it. 

Share this comment


Link to comment

I'm going to skip a blog week and just throw up a video progress. This week was character/animation controller. Nice to start thinking about the action bits. Thanks for playing.

 

Share this comment


Link to comment

Wow very cool! I like how your movement adapts to the terrain changes. :) Great work!

Share this comment


Link to comment

Lots to do. I feel I have a decent setup ready now and some basic communication going on. For the sound system, I went with a companion module in the series I'm currently following. It was nice after cleaning up the implementation and tweaking it slightly to act as I wanted. Copied and tweaked a couple of instruments, wrote two bars of music that worked well wrapped, turned an instrument concept into a game effect and we were off and running. Still focusing on the challenge requirements because, well my game idea is a little weak but that is not the point. :) Thank you for the challenge opportunity. I know some decent game play will come out if I stick with the basic ground rules. (and maybe a twist in there somewhere) head scratching over here. :D  

But here's what my input strings look like for my music format.

// Author : mark kughler (goliath forge)
	// Lick : Do Diddley      |_............_||_............_||_............_||_............_|
	std::string strKick    = "X..X....X.......X..X.....X......X........X......";
	std::string strSnare   = "......X.......X.......X.......X.......X.......X.";
	std::string strHiHat   = "..X.X............XX.........X......X........X...";
	std::string strCymb    = ".........................e..X..................X";
	std::string strBell    = "......c...e...g.................................";  
	std::string strHarmon3 = "b.............c.......b...............b.......e.";
	std::string strHarmon2 = "e.....................e.....d...........g...c.c.";
	std::string strHarmon  = "............................a.......a.b.........";
	// lower case notes (c major) - upper case sharp
	// c = c d e f g a b c
	// d = d e F g a b C d
	// e = e F G a b C D e
	// f = f g a A c d e f
	// g = g a b c d e F g
	// a = a b C d e F G a
	// b = b C D e F G A b

Moving Forward...

Edited by GoliathForge

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
  • Advertisement
  • Blog Entries

  • Similar Content

    • By _WeirdCat_
      So i am about to read a 8bit pixel and want to convert the value to float (0..1) range
      Like this
      unsigned char r = get_pixel_from_img(x, y);
      Now do i divide it by 255 or 256?
      float fval = float ( r ) / x;
    • By Eternal Paladin
      Hello everybody,
      I'm a solo developer / writer /designer currently working on a story-based RPG set in the modern era. I'm at the concept stage of game and also just looking for anybody who would be interested in helping me out! Any and all aid will be greatly appreciated. 

      Please message me or add me on discord (Merlin#2647) if interested, thanks!
    • By tamlam
      Hello all,
      I am trying to do animation by rolling a cube (1x1x1) along x_Axis.  I just want to roll this cube 90 degrees along the edge (parallel with y_Axis) in three times, then stop. 
      Then rolling within 45 degrees. then stop.
      When I use OpenGL to do animation, it always rolls in cycle time. I do not know how to make the animation with different actions as I draw in the below image.
      Please give me advice or useful links to reference. Thanks a lot.
      rollingCUbe.bmp
    • By babaliaris
      There are two projects. One is called VampEngine (a Shared Library) and the other Application (A console application).
      The VampEngine contains a VampEngine::Core class. This function contains a void VampEngine::Core::MainLoop() method.
      There are also two extern C functions void *Vamp_Core_Constructor() and Vamp_Core_MainLoop(void *obj)
      Now, this is what happens:
      The client is calling void *Vamp_Core_Constructor() in order to create a VampEngine::Core which lives inside the Shared Library. And also casts this void object into a Core object.
      The void VampEngine::Core::MainLoop() is being implemented both in the Shared Library and in the client. The client's MainLoop() is just a wrapper for calling the extern c function Vamp_Core_MainLoop(void *obj)
      The code compiles and links in both Windows and Linux. When you run it on Windows, MainLoop() (shared library's implementation) is getting called, but on Linux, there is a recursive call in the client's MainLoop() implementation.
      The behavior in each Operating System can be shown below:


      You can check the project (It's small, I just started it ) On my GitHub Repo . Also, I have a Premake script if you want to check the code for yourself. You are most interested in the files:
      API.h  EntryPoint.h  Application.hpp  Core.cpp  Core.h  Core.hpp  Main.cpp
       
       
      This might explain the problem in a better way:
      The Core class has two implementations of the Core::MainLoop(). One is implemented inside the DLL and the other in the client. The client's implementation is just a wrapper which calls a c extern function (which lives in the dll) which actually calls the MainLoop() implemented inside the DLL. 

      Now in visual c++, because I'm only exporting that c extern function, Core::MainLoop() acts in a polymorphic way. The client calls his implementation of Core::MainLoop() which calls the extern function and eventually the Core::MainLoop() inside the dll runs.

      On linux, I believe by default all the symbols are getting exported. And this is the behavior I saw using a debugger:

      The client calls Core::MainLoop() , this calls the extern function, the extern function calls again Core::MainLoop() but instead of running the dll implementation it actually runs the the clients implementation. It's like the clients Core::MainLoop() is calling itself over and over again!
       
      Conclusion:
      I located the above behavior using gdb debugger on Linux. I just noticed the client's implementation was getting called in a recursive way instead of acting polymorphically ending up calling the implementation of the dll's MainLoop()
    • By Jinxie
      Coders needed urgently for a small project. Perfect for experience and C.V

      Have you just graduated from University and in need of experience?
      Would you like something to put on your C.V to show employers that you have worked on a team before?
      Would you like to work with a game just like Fusionfall that is trying to revive itself?

      Well if you are interested look no further!

      Football Superstars is an MMO created in 2008. The game is built around become a football(soccer) superstar and rising the ranks to glory. The game is a very ambitious and was featured on steam back in 2012.

      The game used to be very popular with over 5,000 active players and was renowned around the world as being the first of its kind in the Sporting MMO industry. The game still has a lot of potential but due to the neglect of the game by previous owners the game lacks a developer capable of making impactful changes to the game.

      As the game was created in 2008 with an unique engine created by Monumental Games, it’s a perfect opportunity for aspiring game developers to gain necessary experience in career progression The language requirements are to be discussed with the current game owner(most likely c++)

      If you are interested in this role please feel free to contact me on discord at  surveyfs@gmail.com or on discord at Jinxie#5260.
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!