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 ¤tCat = 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.)