The more I learn about polymorphism, the more I want to use it.
That's a common side-effect of learning something new. The problem is, after I learn something new, I want to use it in all the places were it's not necessary. I learn how to use something, but it still takes me awhile more before I learn where it should be used.
First functions, then classes, then inheritance, then polymorphism (which is different than inheritance), then templates, then various design patterns, function pointers, pointers-to-members, and etc... Whenever I learn how to use a new hammer, I try to hammer in every screw I come across.
I definitely recommend making a class for each item. It will be much easier to manage and understand down the road, not to mention easier to expand when you want to add more items to your game.
I recommend the exact opposite - in my opinion, this is not the appropriate place to use inheritance, and will over complicate an otherwise simple task.
It's not the end of the world, though, and will still work.
I found your post friendly and helpful, and also excitedly passionate about something you've still learning about. That happens to me alot - it happens to all of us, as we get nerd-glee from something cool and interesting.
People weren't downvoting your post because it wouldn't work. People were downvoting it because it wasn't a good way of solving it. I mean, sure, there are almost always 'better' ways of coding something. But according to our various personal standards of quality, some methods fall into the 'not good enough' or 'bad' zones, and some get labeled 'acceptable' or 'good' or 'great' depend on how far above our dividing line it goes.
If someone says, "Hey, this is a good way of solving problem X", and the rest of us know it's not a "good" way of solving it, then we say so. You're a programmer who's learning fast - don't be discouraged when someone shows you a better way (though I know it's hard for me personally not to get discouraged or irritated when told my knowledge is wrong). Even as a skilled programmer, I frequently am learning new things, and often in discussion someone corrects or adjusts my previous knowledge where it wasn't yet fully formed.
Consider the following example (it does compile, if you want to test it yourself):
...
But this is alot more straightforward example:
class Boat
{
public:
Boat(int topSpeed) : topSpeed(topSpeed) { }
int Get_TopSpeed() const { return topSpeed; }
private:
int topSpeed = 10; //Default value.
};
int main()
{
// It works something like this.
const Boat StarterBoat(15);
const Boat MegaBoat(30);
Boat playerBoat = StarterBoat;
playerBoat.Get_TopSpeed(); //Returns 15
playerBoat = MegaBoat;
playerBoat.Get_TopSpeed(); //Returns 30
return 0;
}
What's the difference between a black dog and a white dog? Only the data, not the behavior. Both are instances of a single class 'dog'.
Even in many cases of behavior, you often times want to just use data to solve it. Inheritance is very good and very useful. I wouldn't say it's a 'last resort' by any means... but it shouldn't be my first, second, or even third choice. It's several places down the list of what I first turn to.
For example, what's the difference in real life between two black dogs? Well, their actual AI behavior might be different... but it'd be very similar. They can be modelled with the same logic (code), by passing in different parameters (data) to that logic. We don't need "HungryDog" and "SleepyDog" (and "HungryAndSleepDog"), and we don't even need "AggressiveDog" and "FriendlyDog". Instead, we pass in 'hunger' data to the "Dog", and the dog's behavior is the result of the same logic (code) operating on different input (data).
Same with RocketLauncherEnemy and MachineGunEnemy. The differences between them (behavior and otherwise) can just be the result of the same code operating on different parameters.
Sometimes you do need different logic though (for example, a flying enemy). But... (and this is an important insight) logic is also a type of data.
You can pass in chunks of logic to customize class instances as well (think function pointers, for example, or delegating behavior to composited components)
But this is not the OP's original question anyway.
His question was about how he can make an in-game shop.
The items in the shop aren't required to be the same structures/classes used to implement the rest of the gameplay. You can model the shop items differently.
There are many different ways to do it (some good, some not as good). Here's two that I'd recommend, depending on the gameplay of the game.
If there is a single shop in the game, and there is a clear progression of equipment, I'd store some of the shop details (like price) into the items themselves:
//-----------------------------
struct Boat
{
unsigned price; //How much this boat costs in the shop.
unsigned maxSpeed;
};
std::vector<Boat> Boats; //All the boats in the game.
//-----------------------------
struct Hook { /* etc... */ };
std::vector<Hook> Hooks;
//-----------------------------
struct Rod { /* etc... */ };
std::vector<Rod> Rods;
//-----------------------------
struct Propeller { /* etc... */ };
std::vector<Propeller> Rods;
//-----------------------------
struct FishingNet { /* etc... */ };
std::vector<FishingNet> FishingNets;
//-----------------------------
struct Bucket { /* etc... */ };
std::vector<Bucket> Buckets;
//-----------------------------
class Player
{
public:
//We're just storing the 'level', which is used to index into the vectors holding the actual structs.
//No need for these to be private - at least, not yet.
size_t currentBoatLevel = 0;
size_t currentHookLevel = 0;
size_t currentRodLevel = 0;
size_t currentPropellerLevel = 0;
size_t currentFishingNetLevel = 0;
size_t currentBucketLevel = 0;
public:
//...functions and logic...
};
(Note: In this example, my vectors are global. In my actual code, I probably wouldn't have them as globals - I'd have them as members of a struct that gets passed around as needed. However, if they remained global, it wiouldn't be the end of the world, because in this situation, they are constant through the lifetime of the game's execution, after the game's initialization occurs. This could be better modeled, but not in a single codebox).
If the game is likely to hold different shops carrying different items or the same items at different prices or if items have tradeoffs and aren't directly above or below each other i usefulness, something like this might be preferable:
struct Boat
{
unsigned maxSpeed;
};
struct Hook { /* etc... */ };
struct Rod { /* etc... */ };
struct Propeller { /* etc... */ };
struct FishingNet { /* etc... */ };
struct Bucket { /* etc... */ };
struct ShopItem_Boat
{
unsigned cost = 0;
std::string description;
Image appearance;
Boat boat;
};
struct Shop
{
std::vector<ShopItem_Boat> boats;
std::vector<ShopItem_Hook> hooks;
std::vector<ShopItem_Rod> rods;
//...etc...
};
class Player
{
public:
//No need for these to be private - at least, not yet.
//This time we're storing actual items directly.
Boat currentBoat;
Hook currentHook;
Rod currentRod;
Propeller currentPropeller;
FishingNet currentNet;
Bucket currentBucket;
public:
//...functions and logic...
};
You might be tricked into thinking that my code here is "worse" because it uses basic, simple, "beginner-level" features of the language. Don't be deceived, it's a better way of doing it. Even when you learn to use a tool like a drill for specialty purposes, an experienced carpenter still frequently reaches for his hammer - frequently enough that he carries it with him on his belt.
In my own projects, I use inheritance alot (mostly for polymorphism). I also use "not inheritance" even more.
And in case you are curious about the variables being publicly accessible, some parts of my projects use getter and setter functions. Other parts have member variables exposed publicly. Which one is better design?
Answer: One is better design in some circumstances, another is better design in other circumstances.
Same thing with inheritance - inheritance is useful and good design in many circumstances. Part of the greatest growth in programming (and in carpentry ) I've had is from learning not just how to use a tool, but when to use it - and when to avoid it.
I've upvoted your posts for being helpful. Sometimes people downvote to mark incorrect information, they don't mean to downvote your helpfulness or participation in the conversation, only the idea presented. Or to put it another way, some people rate your data, some rate behavior.