GPWiki.org
GPWiki.org
It is currently Wed Jun 19, 2013 2:38 pm

All times are UTC




Post new topic Reply to topic  [ 7 posts ] 
Author Message
PostPosted: Tue May 29, 2012 1:23 pm 
Bit Baby

Joined: Tue May 29, 2012 12:50 pm
Posts: 8
Hi,

I'm hoping someone with more experience in this field can help me. I've just started my simple Wizardry-style game, and I've hit a problem. I previously created a simple menu class to create list-style menus, of the kind you might find on a main game menu. So, a menu created with this might look like:

New Game
Continue
Options
Exit

Now, this works just as I need it to. The problem is, during various stages in the game, such as character creation, or in a shop, I'll need interaction between two elements. For example, say I'm in a shop, and I'm browsing the categories of goods. There might be a pane on the left that lists the categories, such as:

Swords
Axes
Spears
Armour
Misc

And when I select one of these categories, the pane on the right would need to update to list the relevant items. Similarly, during character creation, I'd want the user to be able to browse through races/classes on the left, and view a description of the currently highlighted one on the right, and so on.

Now, obviously I can create the list on the left as a simple menu object, but I'm a bit confused on how to get them to "talk" to one another. In fact, I'm a little confused about the UI alltogether. For example, should I design UI elements in a "hardcore" UI class that I can create objects of, or is that overkill for a modest game project? Should I create each screen procedurally using just simple classes?

If you're familiar with Wizardry games, you'll know that much of the game takes place around a UI (namely menus) - visiting shops, recruiting members, browsing inventories, etc. While I can actually create these UI elements without much hassle, the need to make some of them interact with others is proving a nightmare to understand.

While I have a fairly solid knowledge of SDL and C++, I'm a bit green when it comes to proper program design and get stuck figuring out how to structure certain parts of my program.

Anyway, hopefully my ramblings make some sense to someone. I understand that what I'm asking may not make sense, but it's been a real stumbling block for me.

Any help you can provide would be much appreciated.

/rant


Top
 Profile  
 
PostPosted: Tue May 29, 2012 2:17 pm 
Bit Baby

Joined: Tue May 29, 2012 12:50 pm
Posts: 8
Not wanting to reply to my own post, but I was doing some thinking (dangerous, I know) and thought of something.

Would something like this be alright?

Code:
struct item {
   string name;
   string description;
   int price;
};

struct category {
   string name;
   vector<item> items;
};


That way, in my previous "shop" example, the category names would be listed in the left pane, and the member items could be shown on the right.

Please tell me if this is obviously a bad/nooby way of doing things. I think it's fine but.. ;)

thanks again


Top
 Profile  
 
PostPosted: Tue May 29, 2012 8:23 pm 
Dexterous Droid
User avatar

Joined: Wed Aug 18, 2004 7:40 pm
Posts: 3746
Location: South Africa
Yeah, designing your UI code is a bit of a nightmare. You need to very carefully choose how flexible you want the UI to be. If you can rigorously define exactly what you need it to do and not do then you can create a solution that minimally solves just the problem at hand, without adding extra features that you have no need for. This is harder than it might sound but a good first step is to draw all your menus out on paper and decide what the needed elements are.

The solution you propose sounds fine to me. When it comes to UI, I believe in doing the simplest thing possible and not worrying about extensibility. This approach will bite you in the ass if you try to extend the functionality (obviously) but it will save you time working on UI which I frankly find pretty tedious. That being said, I have one important suggestion for your proposed solution: separate all UI stuff from your actual game objects used in game logic. That struct item looks suspiciously close to something you might be using in your game logic.

There is a design pattern called Model-View-Controller, which basically means "separate visualisation of, [and methods to alter] the data from the actual data". In this case, solve the problem in your game logic first. What do you need your items to contain? Now create a method that is able to interpret your in-game objects and display them in the GUI (you can create and populate another, simplified, data structure here for the GUI methods). Creating an interface like that will protect your game logic and means that you know you only have to change code in one place if you alter your underlying game logic data.

_________________
Whatever the mind can conceive and believe, it can achieve


Top
 Profile  
 
PostPosted: Tue May 29, 2012 8:31 pm 
Bytewise

Joined: Sun Oct 16, 2011 3:09 pm
Posts: 282
Location: Here (where else?)
You can also create the menu each time just before you display it, rather than having it exist all the time and update it.
That way, you can look in your data what menu you need, and then create the one you need.

UI interaction is very slow, so it does not hurt to spend CPU time on such things.

_________________
My project: Messing about in FreeRCT, dev blog, and IRC #freerct at oftc.net


Top
 Profile  
 
PostPosted: Sat Jun 02, 2012 4:33 pm 
Bit Baby

Joined: Tue May 29, 2012 12:50 pm
Posts: 8
Thank you both for your replies.

I am quickly realising that I have very little idea on how to actually solve common game-related problems. To make matters worse, I can't see how one actually learns this (as it doesn't really seem to be taught). As a total beginner, I always thought that creating programs was all about learning the syntax... but now I'm more comfortable with the syntax, I realise there's much more to it.

Anyway, I tried out the method I proposed before, and now have a class that contains categories (and items) within it.

There are two main problems I've had with it, and forgive me if my explanations don't make any sense:

1.) I'm not sure where to call certain functions from. For example, I create a new menu object (used for shops) in my main code, and then access members of that through it, as below

Code:
shop myShop; //create the shop object (surprise!)

void gameEventFunction() { //the current game state's event handler, handles everything this state uses, not just the menu
     myShop.event();
}

void gameRenderFunction() { //current game state's rendering function, again, handles everything I might want to draw in current state
     myShop.draw();
}


As you can see (hopefully), I'm calling members of the shop from within the respective parts of the main game loop, i.e the rendering/event handling is done by the class' functions and not the main functions. Is this the usual way of doing things? Would you normally create functions within the class and then call them as above during the main event/render/logic phases of your main game loop?


2.) I'm having to access members in a very long-winded way (And I'm certain I'll show my inexperience here). Below example taken from the class member functions

Code:
//a chunk of my key event handler within the shop class
void shopClass::keyPressed(SDLKey sym) {
   switch(sym) {
      case SDLK_UP: //UP arrow key was pressed
                        //notice the *horrible* way I'm accessing nested data in the statements below
         if(aShop.currentCategory > 0 && (aShop.categories[aShop.currentCategory].isSelected == false)) {
            aShop.currentCategory--; //this variable stores the currently selected menu category
         } else if(aShop.categories[aShop.currentCategory].isSelected) {
            aShop.categories[aShop.currentCategory].currentItem--;
         }
         break;
   }
}



I absolutely feel this is an awful way of doing things, but I'm not sure of another way.

Basically, I have a "currentCategory" variable within the class that keeps track of the currently highlighted category. I then use the value of this variable to decide which child "item" elements to display on the other side.

In turn, each "category" contains a variable (bool) that marks whether the category has been selected or not (return key pressed) - this is used to decide when to draw the child items mentioned above, and basically hands over control to the right-side (which lists items instead of categories). Currently, I'm handling key events with the format below (over-simplified for ease of understanding:

Code:
switch(keycode) {
     case UP:
          if(browseCategories) { //the user is choosing a category to view
                 //handle events on the left side
          } else if(browseItems) { //the user has chosen a category and now wants to list the items within it
                 //hand events on the right side
          }
          break;
}


is this a bad way to approach it? I couldn't think of an alternative.

Apologies for the lengthy post again.

Ollie


Top
 Profile  
 
PostPosted: Sat Jun 02, 2012 4:47 pm 
Funky Monkey

Joined: Thu Sep 09, 2004 1:17 pm
Posts: 1567
Location: burrowed
If it works it cannot be that bad ;)
There are always solutions that might be cleaner, faster, easier to read or have other drawbacks.

Rule of thumb is: if it works and you don't know how to do it better, leave it at that. Once you learned a way to improve it and you're not satisfied by your current solution, change it. Else you'll be stuck improving uncritical features wasting a lot of time with it :P

_________________
Long pork is people!

wzl's burrow


Top
 Profile  
 
PostPosted: Sat Jun 02, 2012 6:43 pm 
Bytewise

Joined: Sun Oct 16, 2011 3:09 pm
Posts: 282
Location: Here (where else?)
ollieisamuppet wrote:
I am quickly realising that I have very little idea on how to actually solve common game-related problems. To make matters worse, I can't see how one actually learns this (as it doesn't really seem to be taught). As a total beginner, I always thought that creating programs was all about learning the syntax... but now I'm more comfortable with the syntax, I realise there's much more to it.
Welcome to the world of programming :)
You are right that syntax is just the first hurdle, and it's the simplest one, as there is usually exactly one way to do it right.

In general, in programming, there are infinitely many solutions for every problem, and they are all valid, although some are better than others with respect to certain properties.
For the next period, just finding a solution (any solution) is challenging enough for you :)

Basically you learn programming by doing. Code stuff, see how it breaks, try to come up with a better solution. Also, read code made by others, ask around for things you feel can be done better (like you do below), read books about software engineering and about data structures.
Coding in an open source project is another option. Adding or fixing to existing code is (often) an order of magnitude simpler than creating new code from scratch. Also, there are often others nearby that know about the code and/or how to code. In addition, you learn about version control systems, and about issue trackers, which is always useful.

ollieisamuppet wrote:
There are two main problems I've had with it, and forgive me if my explanations don't make any sense:
No need to ask forgiveness. We are well aware of the limitations of the medium, so trust us that we will ask counter-questions if we don't understand what you mean.
The other way around is also fine, if we answer you in ways you don't understand, just ask for clarification. Perhaps we think we understand what you say, but we are wrong without realizing it.


ollieisamuppet wrote:
1.) I'm not sure where to call certain functions from.
This seems to be related to the question at the bottom, I'll answer it there

ollieisamuppet wrote:
2.) I'm having to access members in a very long-winded way (And I'm certain I'll show my inexperience here). Below example taken from the class member functions
Indeed it shows, but that's ok. You get your problems across very well and that's what counts.

ollieisamuppet wrote:
Code:
//a chunk of my key event handler within the shop class
void shopClass::keyPressed(SDLKey sym) {
   switch(sym) {
      case SDLK_UP: //UP arrow key was pressed
                        //notice the *horrible* way I'm accessing nested data in the statements below
         if(aShop.currentCategory > 0 && (aShop.categories[aShop.currentCategory].isSelected == false)) {
            aShop.currentCategory--; //this variable stores the currently selected menu category
         } else if(aShop.categories[aShop.currentCategory].isSelected) {
            aShop.categories[aShop.currentCategory].currentItem--;
         }
         break;
   }
}

Ok, first a general remark. You have these nice abstractions 'gameFooFunction' above, I'd suggest you do the same with the input side. The drawback of your current solution is that if you ever want to switch keys or input device, you have to rewrite this function. If you break the function in logical actions, the action of selecting the previous entry in the shop is no longer directly connected to the input key. For example
Code:
switch (sym) {
   case SDLK_UP:   myShop.SelectPreviousEntry(); break;
   case SDLK_DOWN: myShop.SelectNextEntry(); break;
   ...
(I start functions with an upper-case letter to make them easier to differentiate from variables)
This also makes your input switch statement nicely short :)

As to your question, I am very much wondering what 'aShop' is, in particular, isn't it 'this' in your method? I think it is (or should be), but for demonstration purposes, I am going to assume it's not.
Code:
void ShopClass::SelectPreviousEntry() {
    const Category &currentCat = aShop.categories[aShop.currentCategory];
    if (!currentCat.isSelected) { // Never test boolean values with == or !=. Instead, use "if (b) ..." or "if (!b) ..."
        if (aShop.currentCategory > 0) aShop.currentCategory--;
    } else {
        // currentCat.isSelected holds here, no need to test.
        currentCat.currentItem--; // or  currentCat.DecrementItem();
    }
}
I introduced a temporary reference variable that I used as short hand. Also, by testing just one thing at a time, and nesting the 'if' you can drop an entire test.

ollieisamuppet wrote:
I absolutely feel this is an awful way of doing things, but I'm not sure of another way.
Well, it works, that's the main thing. Recognizing it is not the best code is the second step. As you see more code, you'll get better at this (and get horrified of how bad your old code is).

ollieisamuppet wrote:
Currently, I'm handling key events with the format below (over-simplified for ease of understanding:
Code:
switch(keycode) {
     case UP:
          if(browseCategories) { //the user is choosing a category to view
                 //handle events on the left side
          } else if(browseItems) { //the user has chosen a category and now wants to list the items within it
                 //hand events on the right side
          }
          break;
}

I'd put this code in a generic 'gameFooFunction' so your switch does not get 5 pages long.

If you have many screens that are mostly independent, you may want to consider to create a class for each screen (that is "it handles the screen"), as in
Code:
CategoryBrowseScreen browseCategories;
ItemBrowseScreen browseItems;
Screen *currentScreen;
That is, a 'Screen' base class, and a derived screen singleton for each screen that you have. "currentScreen->gameFoo" then accesses Foo for the currently displayed screen.

This is however just a way to wrap different screens into a class, it won't save you much coding, as each screen is probably quite unique.
In that respect, your if/else statement is an equivalent solution, except sharing parts between different screens may be a bit easier. (In other words, your solution is better for sharing, while 'my' solution gives tighter controkl what each screen can access.)

_________________
My project: Messing about in FreeRCT, dev blog, and IRC #freerct at oftc.net


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC


Who is online

Users browsing this forum: Majestic-12 [Bot] and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group