Making an in game shop?

Started by
18 comments, last by Norman Barrows 8 years, 9 months ago

Everyone, I can appreciate a difference of opinion, but why am I being downvoted even though the original poster found my input helpful (and also stated this).

When you do that, you are telling new members of the community that it's not ok to share their experiences or attempt to help someone else on this forum. My posts were on topic and relevant to the needs of the original post. Please do not misuse the system.

There are many different ways to approach a problem, and we should encourage each other to provide multiple solutions. Let the OP decide which method he or she would like to use.


Your original post suggesting inheritance was not voted down (at least not enough to go negative). Your follow-up where you attempted to insist your solution was the better one was.

Inheritance, while a useful tool, is not the right solution for this problem, at least not on its own. As swift showed, it can be used as part of the solution, but it should be mixed with other solutions that result in a better-architected system (i.e. components, data, etc)
Advertisement

Polymorphism is a cool tool but in this situation it would just add more complexity to the code base. For example if you have 15 different boat types and used inheritance to make all these boats then when you need to make a change to how the boat works it may turn out that 15 classes will need to be updated. Swiftcoader is right in thinking that the design of the boat should be broken out into the components. This way if you want to swap a component or add a new feature it would make for a faster assembly of your object and reduce the code footprint. Using composition in this way can leverage how your boat will act and change as you add more code to the design. Knowing the right spots to use inheritance or composition is the key to your design.

In this case the boat class should use composition but then think about the input of how the boat is controlled. This is where interfacing and inheritance will help you create the input for a row boat and the motor boat. But it also leaves room down the road if later you want to add in an AI controller to the boat or even hook up network controlled boats. I did this exact same thing in my current project and connecting the AI was so simple I was surprised and amazed.

In all honesty without knowing a complete plan for the game it is hard to give you the right advice. For a small game that has only a few objects then creating a bunch of classes to deal with it night be fine. But if you want to jump into a large scale inventory system then taking a step back and planning your game objects is in order. Breaking them down into simpler parts that you can then later assemble into game objects might be more appropriate.

The more I learn about polymorphism, the more I want to use it.


That's a common side-effect of learning something new. smile.png 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. laugh.png

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. sad.png

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. tongue.png

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. blink.png

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. ohmy.png

His question was about how he can make an in-game shop. wink.png

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. smile.png

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 wink.png) 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. tongue.png

Something like polymorphism is the more expediant solution, something more like data driven / composition is the more scalable solution. If you know for certain that you'll only want, say, 5 boats, you might not ever suffer the shortcomings of a polymorphism-based approach, and so it can be a reasonable solution. If you know that you'll want some large or vaguely-defined number of boats in the future, a data-driven / composition approach is certainly better.

Where data properties (and even behavior properties) alone are the differences between items, data alone is sufficient to do that. Stuff like, my boat is X speed, and has Y turning radius, and costs this much.

Polymorphism is *sometimes* necessary when you need a common interface among items with functional differences -- that is, different capabilities, not just different properties. Even then, composition can often express that in ways that are more scalable, for example, in Entity-Compoenent sytems (e.g. Unity's GameObject or Javascript's entire object model).

throw table_exception("(? ???)? ? ???");

Thank you guys so much for the help! As what Servant of the Lord said about my question, we kinda did get a little off topic. But I guess I also forgot the original question after reading some of the replies. Since I do not know how many items I plan on selling, it seems best that I fully plan that out first.

I really like the idea of breaking things down into parts as suggested by switfcoder:

Behold, one non-polymorphic boat, and a set of input handlers:
interface PlayerInput {
void processInput(Boat boat);
}

class TapInput extends PlayerInput { // use for rowboat
void processInput(Boat boat) {
if (keyPressed && !lastKeyPressed) {
boat.moveForward();
}
}
}

class HoldInput extends PlayerInput { // used for powerboat
void processInput(Boat boat) {
if (keyPressed) {
boat.moveForward();
}
}
}

final class Boat {
PlayerInput mPlayerInput;
int speed;
int position;

void moveForward() {
position += speed * deltaTime;
}
}


But I'm just a little unsure how this works. Like if it was buying an item from a store switching from tap to hold, what would be executed once it was bought?


Also @Servant_of_the_Lord

I am planning on having multiple stores, but I do not understand your second example that well.

For example, I am not sure what this means because I'm not familiar with the language.


struct Hook { /* etc... */ };
struct Rod { /* etc... */ };
struct Propeller { /* etc... */ };
struct FishingNet { /* etc... */ };
struct Bucket { /* etc... */ };

And how the shop code works. Could you please explain it a little more?

The more I learn about polymorphism, the more I want to use it.


That's a common side-effect of learning something new. smile.png 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.

...

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. tongue.png

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 independent 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.

...

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 wink.png) 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. tongue.png

Maybe I misinterpreted "This response is not useful and does not improve the conversation" when hovering over the down arrow... I'll just take this one lying down. Thank you for understanding that I was a little excited and genuinely trying to help. It's an accomplishment for me to learn a higher level programming concept on my own. Most things are beyond my budget.

Sorry for the derail. sad.png

Edit:

On the bright side, more people are paying attention to this thread/can help the OP find a solution.

It is kinda weird that the downvote button's instructions isn't the same logic as the upvote button.
The upvote is "This post provides useful information to the conversation and demonstrates knowledge of the subject matter". So if someone assumes the downvote button is the opposite without reading the downvote's tooltip, they might think they are supposed to downvote incorrect knowledge instead of incorrect behavior.

Your participation in the community is valuable - I'm enjoying this discussion you started, for instance; and your help in other threads (and your continued help in this thread) is I'm sure appreciated.

I bet there's alot of better programmers than me who are right now chomping at the bit that I recommended against inheritance. And who knows? Once the OP explains more of what he's working on, it might turn out that inheritance is the best choice after all. If that occurs, I'll have to quietly slip into the shadows while I wipe the egg off my face. laugh.png

I am planning on having multiple stores, but I do not understand your second example that well.


Sorry, I forgot you were using Java.

'structs' in C++ are just 'objects'/'classes' that serve no other purpose than to hold related data together. They're basically classes without much more than bare-bone logic. At least, that's how I use them. Technically, in C++ structs and classes are two names for the same thing (baring a minor detail or two).

Essentially what I was suggesting is to make each 'type' of item a separate class without inheritance (i.e. one class for every boat, one class for every hook, and so on). These item classes mostly just hold data without much logic. Though the amount of logic they'd hold would really depend on what type of game you are developing.

I'm assuming, possibly incorrectly, that it's a text based or simple 2D game, but this would work even for a simple 3D game. For a complex 3D game I'd go about it differently.

A 'shop' would be a class that holds arrays of the items for sale (one array for each category of item), including pricing and other relevant information.
Note: I'm separating a shop's data from a shop's interface. I've been talking about data this whole time. The visuals, how it's displayed, how you interact with it, is a different topic.
Same with the items themselves - When I say 'Boat' or 'Bucket', I'm talking about the data representing their differences, not the logic or visuals of them in the game itself (but the logic uses the data to create their differences in behavior).

Here's some questions to help understand what your needs are:
1) Is the game text-based, 2D, or 3D?
2) As far as the player's equipment goes:

A) Does the player have a list of all Buckets he owns?

B) Does the player only "have" the latest/best bucket he's purchased?

C) Is the player's buckets mingled in a single inventory with all his other equipment?

3) After getting a new boat, is there any reason why you'd want to switch back to a previous boat?



Here's some questions to help understand what your needs are:
1) Is the game text-based, 2D, or 3D?
2) As far as the player's equipment goes:
A) Does the player have a list of all Buckets he owns?
B) Does the player only "have" the latest/best bucket he's purchased?
C) Is the player's buckets mingled in a single inventory with all his other equipment?
3) After getting a new boat, is there any reason why you'd want to switch back to a previous boat?

1) It is a 2D game. The items that the player has will have instant effect as there will be no place to hold the items. I could make it so the player can visit the store to swap out previously bought items for no cost.

2,A) He will not have a list as bucket upgrades are direct and provide no trade off, so there would be no reason to switch to an older one.

2,B) He will have the latest bucket he purchased.

2,C) The bucket is not in any inventory. The inventory is meant to hold just caught fish.

3) I am currently unsure if there will be a point of switching, but I'm going to say there will be no reason to downgrade as new boats will always be direct upgrades.

Also I'm starting to understand the idea behind having different classes that hold the item's data. I actually think I was somewhat starting to develop that when I was first trying to make the store.

It sounds like this would be best implemented by simply altering the stats when you make a purchase. The appearance and representation of the "item" can just be treated as another kind of stat, along with things like speed and size, etc.

So for a shop you'd want to create an interface that shows the next tier of things along with their prices. When one is selected and confirmed you just modify the relevant state. For example, your game state may have a list of current stats like so:

boatSpeed = 3;

bucketSize = 2;

fishingLineStrength = 4;

castingPower = 8;

So let's say you want to have the fishing rod as an item that can be upgraded in the shop. You can have an icon that represents the current rod type, and create a set of icons (the actual image files) then give them names like "rod1.png", "rod2.png" and so on. Then add a variable to indicate which rod the player is using:

rodIDNum = 1;

So when the rod ID is 1 you use the rod1 icon. Then in the shop when the player buys a rod upgrade you just increase the rod ID number and then have a function that loads the new icon and sets the castingPower and fishingLineStrength to the values for a level 2 rod.

The same methodology can be used for buckets, boats, etc.

Make sense?

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

what would the best approach to making a shop

your talking about implementing an inventory items system.

general design of an inventory items system usually goes as follows:

first, you have your items, and their basic stats (boat, fishhook, etc, price, weight, etc) - that's one list of info - a "list of classes" you might say. the basic stats might be kept in one big list, with one entry for each item, like a big lookup table. or they might be stored in each instance of the item, initialized by some "creator" function that creates instances of the item. there are other ways to store this "general" info, but these two methods seem to be the common older and newer ways of doing it.

then you have inventory lists - lists of items that someone is carrying or selling, or are stored in a container, etc. this is you second type of list- a "list of instances" you might say.

so you have you master list of item stats, or your item factories that create instances with the stats init'ed correctly. and each player, NPC, merchant, and container has an inventory list, and you'll need an inventory list for items dropped in the world, or one for each chunk/area/level/cell in the world.

when you start the game, you can load the list of item types from disk for data driven design. items can even be script driven to make the game even more data driven design. both may be overkill for small projects.

the player starts with an empty inventory, or whatever you give them. the player will need a way to view their inventory - so you need a "view inventory" screen of some sort.

when the player goes to the store, uses a container, or exchanges items with others, you'll need a way for them to exchange things. so you'll need a "transfer items" screen that lets you transfer items between inventory lists (IE between merchant and player, player and follower, player and container, etc). merchants are no different than any other inventory list, except you exchange money at the same time you transfer items. so you can usually get away with one generic "transfer items" screen for everything.

and that's pretty much it. an item types list, and inventory lists. a "view inventory" screen, and a "transfer items" screen. a "select item" screen is often useful as well.

implementation details depend on the language, syntax, and coding style in question. things like object factories vs a relational database style item_types list, hard coded vs data driven stats, hard coded vs scripted behavior, etc.

in the case of a small game (or any game for that matter), follow the KISS engineering principle (keep it stupid simple), and avoid overkill and over engineering things. do your research, then think ahead to what you'll need by the end of the project, then go with whats fastest and easiest in the long run. if hard coding is easier, forget data driven. but if data driven will save time overall, bite the bullet and do it, even if its extra work in the beginning. and so on for scripting, etc.

an OO implementation of an item_types list design typically yields four classes: item_type (the stats for an item), item_type_list (list of item_types in the game), item_instance (item type, quantity, and quality - IE an entry in an inventory list), and item_instance_list (an inventory list). most methods are list methods, not item_type or item_instance methods.

in a game with very few inventory items where you can only have one of each, you might even forego inventory lists for a simple array or list of booleans such as has_fishhook: true / false. but this could start getting ugly by the time you hit a dozen different types of items.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

This topic is closed to new replies.

Advertisement