Design Advice

General help: new users, installing the Ecere SDK, using the IDE to compile and run applications.
Post Reply
mothdragon
Posts: 28
Joined: Mon Jan 18, 2010 11:56 pm

Design Advice

Post by mothdragon »

Hi guys, I'm working on a Maze game right now, and having a hard time shaking my procedural history (and my DOS history)... Trying ever so hard to embrace this OOP philosophy, but just can't seem to get it... So I thought I'd ask for some design advice....

In my head the general thinking for this is that I've got the main game menu, you know "Start Game", "Options" etc... and then you launch into the game which in turn has a Pause menu... In the past, I've always had the menu on the screen, then wiped the screen and placed the game on that screen... Now, times have changed, and new ways have emerged. So is it best to have a different Window for all of these things? Or should they all go in the same Window, erasing the previous contents as I go?

And if I do use different windows, do I make my Main menu(Form) the programs entry point? Or do I have a GuiApplication which does that?

Further more, how do I get a Button in the Main Menu, to invoke another Form?
--==[[ Mothdragon ]]==--
- - - - - - - - - - - - - - - - -
Everything New is Old, Everything Old is New. Nothing exists, and it's all here.
jerome
Site Admin
Posts: 608
Joined: Sat Jan 16, 2010 11:16 pm

Re: Design Advice

Post by jerome »

Hi Charlie!

The idea of a 'main menu' game (or other app) state versus a 'in game' state (and paused game state) is still a common concept, and can work well with OO design and Ecere. In fact, the Ecere GUI was originally designed specifically to build in-game menus (because it can run in accelerated 3D graphics mode: OpenGL (or 3Dfx Glide at the time! :D). You could even have a translucent 'paused state' menu on top of a faded out paused view of the game.

Although these are different states, they would normally be within the same window frame of the user's desktop (Or sometimes full-screen in the case of games). However, for ease of doing the layout of the contents for these different states, I would recommend using different classes deriving from the Ecere 'Window' class to describe them. The windows can then either be destroyed or be made invisible.

With the Ecere GUI, there is a distinction between the objects being Created/Destroyed in the GUI (Done with Window::Create() and Window::Destroy()) vs the eC object being constructed/destructed (the memory being allocated/freed, and the object being set up as an instance of the Window class). This allows you to destroy a window, yet keeping the object around, for recreating it later (Calling Create() again after Destroy() has been called). This makes it an alternative to toggling visibility, depending on how everything is set up in your application (e.g. if you want to wipe out all graphics resources when the state is not showing, you'd likely go with Create()/Destroy(), vs if you want to keep them around and want quick state change you would toggle visibility instead). Create()/Destroy() invokes the whole graphics resources loading/unloading system (OnLoadGraphics/OnUnloadGraphics, BitmapResources, FontResources, etc.)

Regarding the entry point: the Entry point of an Ecere application is the class deriving from the Application class in the executable. If there is no Application class in the executable, it will then look in shared libraries (e.g. ecere.dll/libecere.so), and if it finds one there (e.g. GuiApplication) it will make use of it. If you are making use of the Ecere GUI or networking system in Ecere, you should inherit from GuiApplication rather than Application directly. Then you can override Init(), Cycle() (called continuously throughout the program execution) and Terminate(). Alternatively, for more control you can override Main() and set up your own main loop.

So if you haven't derived any class off GuiApplication, but created windows, these forms will be automatically created as part of the initialization (unless they set autoCreate = false). But the 'entry point' really is the GuiApplication class's Main() method, defined inside the Ecere shared library.

How would a button in the Main menu invoke another form... Well if you want the main menu to go away all-together, you would first make it (from its NotifyClicked event -- a virtual method overridden in the Button instance) do a Destroy(0) to get rid of the main menu. Then you would create the new form... All instances could be global instances, with the forms not showing yet having autoCreate = false. Then when exiting that form it could again destroy itself, and create the main menu. It could be done with setting the visible property as well.

Hope this helps!

Regards,

Jerome
mothdragon
Posts: 28
Joined: Mon Jan 18, 2010 11:56 pm

Re: Design Advice

Post by mothdragon »

So then if I'm keeping all of my program within one Window, how do I get the contents of the different forms to be in the same Window?
--==[[ Mothdragon ]]==--
- - - - - - - - - - - - - - - - -
Everything New is Old, Everything Old is New. Nothing exists, and it's all here.
jerome
Site Admin
Posts: 608
Joined: Sat Jan 16, 2010 11:16 pm

Re: Design Advice

Post by jerome »

What you do is you put all these forms inside a top level 'frame' window.

I put this sample together for you:

Code: Select all

import "ecere"
 
class MainFrame : Window
{
   displayDriver = "OpenGL";
   caption = "Game Menu Demo";
   background = black;
   foreground = white;
   borderStyle = sizable;
   hasMaximize = true;
   hasMinimize = true;
   hasClose = true;
   clientSize = { 632, 438 };
 
   MainMenu mainMenu { this };
   GameViewport game { this };
   GameMenu gameMenu { this };
}
 
class MainMenu : Window
{
   background = gray;
   font = { "Comic Sans MS", 30 };
   anchor = { left = 0, top = 0, right = 0, bottom = 0 };
 
   Button btnPlay 
   {      
      this, caption = "Play Game", p, anchor = { top = 0.4 };
 
      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
      {
         Destroy(0);
         mainFrame.game.Create();
         return true;
      }
   };
   Button btnQuit
   {
      this, caption = "Quit Game", q, anchor = { top = 0.6 };
 
      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
      {
         mainFrame.Destroy(0);
         return true;
      }
   };
}
 
class GameViewport : Window
{
   autoCreate = false;
   opacity = 0;
   anchor = { 0, 0, 0, 0 };
   font = { "Comic Sans MS", 12 };
 
   int t;
 
   Timer timer
   {
      this, 0.1, true;
 
      bool DelayExpired()
      {
         t++;
         Update(null);
         return true;
      }
   };
 
   void OnRedraw(Surface surface)
   {
      ColorHSV color { h = (t*4) % 360, 100, 100 };
      ColorKey keys[2] = { { color, 0.0f }, { white, 1.0f } };
      surface.Gradient(keys, 2, 0.5f, vertical, 0,0, clientSize.w-1, clientSize.h-1);
      surface.WriteTextf(0, 0, "Escape for game menu...");
   }
 
   bool OnKeyHit(Key key, unichar ch)
   {
      switch(key)
      {
         case escape:
            // Pause the game
            timer.started = false;
            mainFrame.gameMenu.Create();
            break;
      }
      return true;
   }
}
 
class GameMenu : Window
{
   autoCreate = false;
   background = black;
   opacity = 0.5;
   drawBehind = true;
   anchor = { 0, 0, 0, 0 };
   font = { "Comic Sans MS", 30 };
 
   Button btnBackToGame 
   {      
      this, caption = "Back to Game", escape, anchor = { top = 0.4 };
 
      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
      {
         // Unpause the game
         mainFrame.game.timer.started = true;
         Destroy(0);
         return true;
      }
   };
   Button btnBackToMenu 
   {      
      this, caption = "Quit to Main Menu", q, anchor = { top = 0.6 };
 
      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
      {
         Destroy(0);
         mainFrame.mainMenu.Create();
         return true;
      }
   };
}
 
MainFrame mainFrame {};
Regards,

Jerome
Attachments
menuSystem.ec
(2.6 KiB) Downloaded 1294 times
Post Reply