Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Splash Screens and Setup Screens Part of Event loop?

This thread is locked; no one can reply to it. rss feed Print
Splash Screens and Setup Screens Part of Event loop?
AceBlkwell
Member #13,038
July 2011
avatar

Just thinking in advance. I've got my simple game operational, granted without any safe guards for the moment. Eventually I'm going to want to allow for an entry screen and/or setup screen for player names.

My question, when implementing screens like instruction screens or game setup screens do you run that through a separate loop? I envision a main loop with the gameplay loop called from that loop. When the game was over or player quits, you would return to the main loop responsible for the opening screen?

Is that a common approach or a problematic approach? Thanks

DanielH
Member #934
January 2001
avatar

You can create another variable, game_state.

Some options:

You can make separate functions that handle events for certain game states or have one function, but it gets confusing.

I don't know how well you are at reading code, but I have a simple tictactoe game. It has game state. There is an opening screen with a menu. You can escape during gameplay back to the menu and the menu changes (from new game to continue game).

I kept it to one function for the logic, but multiple functions for drawing the screen depending on game state.

Link here

In the game there are numerous game states.

    enum class GameState
    {
      Undefined,
      Initializing,
      TitleScreen,
      HelpScreen,
      AboutScreen,
      Shuttingdown,
      PlayerXTurn,
      PlayerOTurn,
      PlayerXWin,
      PlayerOWin,
      Tie
    };

most of the logic is triggered by mouse or key up. It gets messy. I only show you to show the mess.

#SelectExpand
1case ALLEGRO_EVENT_KEY_UP: 2 { 3 this->m_input.OnEventKeyUp(event.keyboard.keycode); 4 5 if (event.keyboard.keycode == ALLEGRO_KEY_OPENBRACE) 6 { 7 Theme::incTheme(); 8 } 9 if (event.keyboard.keycode == ALLEGRO_KEY_CLOSEBRACE) 10 { 11 Theme::decTheme(); 12 } 13 14 this->m_dirty = true; 15 switch (this->m_gameState) 16 { 17 case GameState::PlayerXWin: 18 case GameState::PlayerOWin: 19 { 20 if (event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) 21 { 22 this->m_grid.reset(); 23 this->m_gameState = GameState::TitleScreen; 24 } 25 } break; 26 case GameState::PlayerXTurn: 27 case GameState::PlayerOTurn: 28 { 29 if (event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) 30 { 31 this->m_gameState = GameState::TitleScreen; 32 } 33 } break; 34 35 case GameState::HelpScreen: 36 case GameState::AboutScreen: 37 { 38 if (event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) 39 { 40 this->m_gameState = GameState::TitleScreen; 41 } 42 } break; 43 44 case GameState::TitleScreen: 45 { 46 if (event.keyboard.keycode == ALLEGRO_KEY_ENTER || 47 event.keyboard.keycode == ALLEGRO_KEY_PAD_ENTER) 48 { 49 switch (this->m_option) 50 { 51 case App::TitleOptions::New: 52 { 53 this->m_gameState = GameState::PlayerXTurn; 54 al_show_mouse_cursor(this->m_display); 55 } break; 56 case App::TitleOptions::Help: 57 { 58 this->m_gameState = GameState::HelpScreen; 59 } break; 60 case App::TitleOptions::About: 61 { 62 this->m_gameState = GameState::AboutScreen; 63 } break; 64 case App::TitleOptions::Quit: 65 { 66 this->m_kill = true; 67 } break; 68 } 69 } 70 71 if (event.keyboard.keycode == ALLEGRO_KEY_UP || 72 event.keyboard.keycode == ALLEGRO_KEY_PAD_8) 73 { 74 if (this->m_option == App::TitleOptions::New) 75 { 76 this->m_option = App::TitleOptions::Quit; 77 } 78 else 79 { 80 this->m_option = App::TitleOptions(static_cast<int32_t>(this->m_option) - 1); 81 } 82 } 83 if (event.keyboard.keycode == ALLEGRO_KEY_DOWN || 84 event.keyboard.keycode == ALLEGRO_KEY_PAD_2) 85 { 86 if (this->m_option == App::TitleOptions::Quit) 87 { 88 this->m_option = App::TitleOptions::New; 89 } 90 else 91 { 92 this->m_option = App::TitleOptions(static_cast<int32_t>(this->m_option) + 1); 93 } 94 } 95 if (event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) 96 { 97 this->m_kill = true; 98 } 99 } break; 100 case GameState::Tie: 101 { 102 if (event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) 103 { 104 this->m_gameState = GameState::TitleScreen; 105 this->m_grid.reset(); 106 } 107 } break; 108 default: break; 109 } 110 } break;

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

This is easily handled with a little inheritance and a screen pointer. Have a base Scene class with a few virtual functions to override like so :

For instance, here is my Scene class for the latest game I am working on :

#SelectExpand
1#ifndef Scene_HPP 2#define Scene_HPP 3 4#include "Eagle/Events.hpp" 5#include "Eagle/AnimationBase.hpp" 6 7class EagleGraphicsContext; 8 9enum SCENE_STATUS { 10 SCENE_NOTREADY = 0, 11 SCENE_READY = 1, 12 SCENE_RUNNING = 2, 13 SCENE_COMPLETE = 4, 14 SCENE_ERROR = 8 15}; 16 17#define NUM_SCENES 3 18 19class Scene : public AnimationBase { 20 21protected : 22 bool redraw; 23 bool quit; 24 bool complete; 25 SCENE_STATUS status; 26 27 void OnComplete() {complete = true;} 28 29public : 30 Scene(); 31 32 virtual ~Scene() {} 33 34 virtual bool Init(EagleGraphicsContext* win)=0; 35 36 virtual SCENE_STATUS HandleEvent(EagleEvent ev)=0; 37 virtual SCENE_STATUS Update(double dt)=0; 38 virtual void Display(EagleGraphicsContext* win)=0; 39 40 bool Redraw(); 41 bool Quit(); 42 bool Complete(); 43 SCENE_STATUS Status(); 44 45 virtual void Reset(); 46}; 47 48#endif // Scene_HPP

It lays the framework to derive new classes of scene. One for each screen you want to have, because each will have different inputs, handle events differently, and draw different things. That's what the virtual functions are for.

8-)

Dizzy Egg
Member #10,824
March 2009
avatar

Damn Edgar that makes we want to start making a new game! :D

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

AceBlkwell
Member #13,038
July 2011
avatar

Thanks for the input all.

I'm not very familiar with virtual functions Edgar but sounds like I need to learn.

Daniel, I'm assuming all of this runs within the same while loop?

I was looking over my last game using A4, It didn't work with events but I think the concept will work the same. Previously I used main to call all other functions. IE Title screen. Instruction screen, Playing screen. Outside of calling inits and declaring arrays/variables, main was less than 20 lines of code. I used while loops to keep the game running with in the different functions but stopped for readkeys. I think that will be the major difference.

Again I appreciate the input.

DanielH
Member #934
January 2001
avatar

Polymorphism

#SelectExpand
1class Base 2{ 3public: 4 Base(); 5 virtual ~Base(); 6 virtual void draw(); 7}; 8 9class TitleScreen : public Base 10{ 11public: 12 TitleScreen(); 13 ~TitleScreen(); 14 void draw() 15}; 16 17 18class MenuScreen : public Base 19{ 20public: 21 MenuScreen(); 22 ~MenuScreen(); 23 void draw() 24};

Each subclass inherits everything from the base class. Then you can have one Base class variable. Each subclass can have it's own logic, drawing, event handling.

TitleScreen* title = new TitleScreen();
MenuScreen* menu = new MenuScreen();
Base* base = nullptr;

Depending which game state you are in, you assign base to that subclass.

base = (Base*)title;
base->draw();

Then switch if game state switches.

base = (Base*)menu;
base->draw();

Virtual tells the compiler to use the function of the child class if there is one. Otherwise use the function of the base class. The functions of the subclass is optional.

You can force it by declaring your virtual function like this

class Base
{
public:
    Base();
    virtual ~Base();
    virtual void draw() = 0;
};

Not each child class is required to have it's own draw function.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

https://cplusplus.com/doc/tutorial/classes/ 8-)

The virtual keyword is just a signal to the compiler and base classes that they may override the behavior of this function. If it is a pure absolute function (virtual and ends with )=0; ) then it must be implemented in the derived class before it can be instantiated.

AceBlkwell
Member #13,038
July 2011
avatar

I'll have to read up on it some more. I'm familiar with base classes and inheritance from the stand point that I've read up on them. I've even done a few exercises. My limited used of classes isn't much different than structs. Well other than private and public. As such I typically use structs. I guess it's time to graduate on.

DanielH
Member #934
January 2001
avatar

It has it's uses. Don't for get protected keyword also.

Here is my dialog class, which is a subclass of a widget. It's abstract, meaning you can't create a Dialog.

#SelectExpand
1namespace Wind 2{ 3 class Dialog : public Widget 4 { 5 public: 6 Dialog(); 7 virtual ~Dialog(); 8 9 virtual bool OnStart() = 0; 10 virtual bool OnStop() = 0; 11 virtual bool OnInitialize() = 0; 12 virtual bool OnShutDown() = 0; 13 virtual bool OnUpdate(int32_t tick_count) = 0; 14 virtual bool OnRender(Allegro::Display& display) = 0; 15 16 virtual bool OnJoystickAxis(const Allegro::Event& event); 17 virtual bool OnJoystickButtonDown(const Allegro::Event& event); 18 virtual bool OnJoystickButtonUp(const Allegro::Event& event); 19 virtual bool OnJoystickConfiguration(const Allegro::Event& event); 20 virtual bool OnKeyDown(const Allegro::Event& event); 21 virtual bool OnKeyUp(const Allegro::Event& event); 22 virtual bool OnKeyChar(const Allegro::Event& event); 23 virtual bool OnMouseClick(const Allegro::Event& event); 24 virtual bool OnMouseAxes(const Allegro::Event& event); 25 virtual bool OnMouseButtonDown(const Allegro::Event& event); 26 virtual bool OnMouseButtonUp(const Allegro::Event& event); 27 virtual bool OnMouseEnterDisplay(const Allegro::Event& event); 28 virtual bool OnMouseLeaveDisplay(const Allegro::Event& event); 29 virtual bool OnMouseWarped(const Allegro::Event& event); 30 virtual bool OnDisplayResize(const Allegro::Event& event); 31 virtual bool OnDisplayClose(const Allegro::Event& event); 32 virtual bool OnDisplayLost(const Allegro::Event& event); 33 virtual bool OnDisplayFound(const Allegro::Event& event); 34 virtual bool OnDisplaySwitchIn(const Allegro::Event& event); 35 virtual bool OnDisplaySwitchOut(const Allegro::Event& event); 36 virtual void NotifyParent(int32_t n, void* data); 37 38 void AddWidget(Widget* widget); 39 virtual Widget* GetWidget(int32_t x, int32_t y); 40 41 protected: 42 43 Dialog(const Dialog& Dialog) = delete; 44 Dialog& operator = (const Dialog& Dialog) = delete; 45 std::vector<Dialog*> m_children; 46 std::vector<Widget*> m_widgets; 47 }; 48}

My starting dialog. I don't use most of the events in this class. I left them in there, but commented them out.

#SelectExpand
1 class Game : public Wind::Dialog 2 { 3 public: 4 Game(); 5 ~Game(); 6 7 bool OnStart(); 8 bool OnStop(); 9 bool OnInitialize(); 10 bool OnShutDown(); 11 bool OnUpdate(int32_t tick_count); 12 bool OnRender(Allegro::Display& display); 13 14 //bool OnJoystickAxis(const Allegro::Event& event); 15 //bool OnJoystickButtonDown(const Allegro::Event& event); 16 //bool OnJoystickButtonUp(const Allegro::Event& event); 17 //bool OnJoystickConfiguration(const Allegro::Event& event); 18 //bool OnKeyDown(const Allegro::Event& event); 19 //bool OnKeyUp(const Allegro::Event& event); 20 //bool OnKeyChar(const Allegro::Event& event); 21 //bool OnMouseClick(const Allegro::Event& event); 22 //bool OnMouseDoubleClick(const Allegro::Event& event); 23 //bool OnMouseAxes(const Allegro::Event& event); 24 //bool OnMouseButtonDown(const Allegro::Event& event); 25 //bool OnMouseButtonUp(const Allegro::Event& event); 26 //bool OnMouseEnterDisplay(const Allegro::Event& event); 27 //bool OnMouseLeaveDisplay(const Allegro::Event& event); 28 //bool OnMouseWarped(const Allegro::Event& event); 29 bool OnDisplayResize(const Allegro::Event& event); 30 bool OnDisplayClose(const Allegro::Event& event); 31 //bool OnDisplayLost(const Allegro::Event& event); 32 //bool OnDisplayFound(const Allegro::Event& event); 33 //bool OnDisplaySwitchIn(const Allegro::Event& event); 34 //bool OnDisplaySwitchOut(const Allegro::Event& event); 35 void NotifyParent(int32_t n, void* data); 36 37 private: 38 };

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Java's one listener class for every event is total over-engineering. In my opinion so is having an OnEvent method for every event type. It's simple enough to let a widget or a dialog or a screen or an object to handle a single event and modify it's behavior based on the type.

DanielH
Member #934
January 2001
avatar

In your opinion, you can keep to yourself.

AceBlkwell
Member #13,038
July 2011
avatar

In any case, talking with you guys lets me know how much I really don't know ;D.
I appreciate the input.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Go to: