• Advertisement
Sign in to follow this  

Connecting two independent classes

This topic is 1660 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

So I feel as though this could be either in the Game Programming forum or General Programming forum, but posted here out of safety, please move it if need be!

 

One of my on-going problems when writing code for games has been trying to neatly connect two independent systems. Such as that of a player attempting to purchase items from a store. I have a neatly written up player inventory, a store inventory, and a class that kind of acts of intermediary between the two.

 

Classes involved

Player Inventory

 The player inventory is pretty self explanatory, has a few methods to adding/remove objects from a list of items, and a few other things to keep track of capacity and such.

 

Store Inventory

 Same as with the player inventory, minus the need to track capacity

 

Store Browser

 Takes a player inventory and store inventory and has functions for purchasing an item from the store which then does the swap of currency and item(s) between the store and player. 

 

The player will be able to press a UI button that will create an instance of the ShopBrowser and open a window to display a small list of items w/prices. This is the point where I am stuck, how would I go about grabbing an instance of the players inventory and shops inventory for the ShopBrowser without resorting to some type of static/global call? I feel like I'm missing a design pattern or there is an obvious solution and I'm too close to the code to see it, cannot see the forest for the trees so to speak.

 

If there is any code that needs to be posted, please let me know!

Share this post


Link to post
Share on other sites
Advertisement

You need to pass references to the PlayerInventory and StoreInventory objects to the StoreBrowser (or the underlying object which handles just the lists of items to display), usually via the constructor.

Share this post


Link to post
Share on other sites

Thanks for your response, but I believe you mis-read the problem. I'm talking about getting the instance of the player and store when I do the StoreBrowser construction, not passing the instances in when I call new StoreBrowser(...).

Share this post


Link to post
Share on other sites

How does the UI know about the store? The UI needs to activate the button, and display information about the player, so presumably it knows about the player, and the player knows they are at a store? Or does the UI just receive messages about what to display posted by the player? (In which case you should probably have the message which tells the UI the player is at the store contain the references).

Share this post


Link to post
Share on other sites
The ui would be the logical brdge between the two since it should already know of all the components.

Share this post


Link to post
Share on other sites

So it would be considered, good oop practice to give the UI an instance of the player and shops during the initialization of the game?

 

I.e

 

Initialize Player

Initialize Shop(s)

...more initialization

Initialize UI with an instance of pretty much every class that will interact with the UI?

Share this post


Link to post
Share on other sites

No, have objects trigger events which send messages to the UI. The messages can contain information that is cached with the UI element and is valid until some other event cancels it. (So, enter shop->send message to UI player enters shop, leave shop->cancel the activation of the ShopBrowser UI element).

 

The UI should probably know about the player though, although that isn't strictly necessary either.

Share this post


Link to post
Share on other sites

Didn't see your first reply before Apoxol Paradign, so let me answer that before responding to your 3rd post.

 

When the player enters an area with a shop, a UI button on the screen will be enabled allowing them to click it. When clicked (part I'm trying to solve) the StoreBrowse is created with an instance of the player inventory and store inventory passed into its constructor.

 

This originally lead me to believe the UI needs to know about the player and any and all stores - basically store a reference to these objects. However, I feel as though that's not what I should be doing, as the game may have anywhere between 5 and 20 different shops depending on the level size and difficulty set, and having the UI know about all of the shops  (and anything else) seems wrong.

 

Now I understand about sending events but where I'm a bit lost is how would I end up sending a message containing the player inventory and store inventory. I feel like your 3rd post contains the answer but I'm not sure I understand it - I've been in a fog this past week and its been wrecking my ability to think clearly!

Share this post


Link to post
Share on other sites

Have an EnableShopUIMessage which has a reference to the player (or just the inventory) and the shop (or just the contents), both of which you know at the point you enter the shop. You can derive the message from a common base, or you can just build a message packet and cast it when you decode the message (like in raw Win32 messages), or you can use text or a script (with e.g. lists of player items and shop items) instead if you want.

Share this post


Link to post
Share on other sites
Guest Hiwas

As with many of my answers, I might turn this whole thing around and consider it from different angles.  Much like what Paradigm is saying, it seems that whatever event takes place that says "hey, player is near shop" is a logical starting point.  Now, on the other hand, I might not use explicit references to the shop and the inventory but instead when creating the UI button I bind an appropriate callback which contains the information.  Using C++11, I might do it this way:

 

// Assumptions, at the point the "Enter Shop" button is activated:
// this code knows the player and shop instances....
// A ui system that can bind generic callbacks.  In C++ a lambda is used.
// Modify for language/ui/etc as required of course.
MakeEnterShopActive( UISystem& ui, Player& player, Shop& shop )
{
  UI::Button* enterShop = ui.CreateButton( "Enter Shop", ... x,y location, graphics etc ... );
  enterShop->OnClick( [=](){ CreateShopBrowser( player, shop ); } );
  ui->AddButton( enterShop );
}

 

The advantage should be obvious, the item which knows the source instances involved in the interaction supplies the functionality to a UI system which remains completely oblivious to any of the game classes.  No special derivations, new structures to define and fill in etc, just a function to be called eventually.  This is a common pattern for Lua, C# and other languages with delegates and functions as variables though not as familiar to C/C++ types.  Of course with C languages it would just be a function pointer and a void* pair passed in as the callback to achieve the same thing which of course applies to many other languages also.

 

Hope this makes sense, it is a bit backwards but I believe solves all your goals.

Share this post


Link to post
Share on other sites

@AllEightUp

 

I love that solution and know of quite a few places I can already apply it and some future areas as well. So I'm trying to apply it to solve this current dilemma.

 

However, I still have the problem of supplying the players inventory. The player isn't really a game object or movable object in the game. The game itself is a series of locations and when the user changes locations all that's done is a change of the area and activation of any in-game events that haven't been triggered.

 

The player class is really just a data container of their attributes, their inventory, their status, etc. With a few methods for modifying that data, TakeDamage() for example.

 

So when a new location is loaded a list of events (not to be confused with "Events") are invoked. I derive differing events from a common interface which allows me to stack differing events together. I.E A message box event and then force a combat event.

 

So when the user goes to a location which has a ShopEvent, that ShopEvent is invoked and populates a new shop with items. Then I do what you suggested above with that form of callback, passing in my UI (my UI class is a static class), passing in the shop inventory, and... I don't have the player inventory. So I was able to solve not having an instance of the shop inventory, but I'm lost as to how to incorporate the player.

 

Looking back through my code, I initialize the player, hook up a few Events so the UI can change as the players values are modified(health, etc), and that's it. Unsure of how to get an instance of the player inventory into the ShopEvent.

 

I could modify the base interface that all my game events are derived from, but other events would need to implement that version of Invoke, even if they're never going to use it.

Share this post


Link to post
Share on other sites
Guest Hiwas

Without more insight into your codebase, I can only make general suggestions.  The first and most obvious idea is if anything up the call tree to the load level knows about the player, if so can that object or the player inventory simply be passed down the chain?  If that is not viable or desirable, say it passes through several classes that have no logical reason to know anything about the player inventory or it would require modifying 10 levels of function calls or something like that, I would use a different solution.  Overall, you need to consider that this is a symptom of a common problem.  You have a piece of data defined way over in some other area of code which you need access to in order to make the desired functionality, but from a logical division of classes, there is no direct connection or reason for many of the intervening systems to know about one or the other items which are being tied together.  This is generally called cross cutting and can be a serious pain in the ass.

 

Now, why the details.  Static and global solutions are not great, but sometimes with cross cutting issues they are better than the other options.  With a "level load" function, I most definitely don't want to pass in a specific object like player inventory just to solve this one off issue.  If you keep going down that path as the solution, you end up passing 50 parameters to the function and at any given time only 0 or 1 of those parameters is required.  A static or global is a short circuit for the parameter explosion but it can be as bad or worse, especially if you don't restrict access to the items and they start getting used in other places.  It's 50/50 with the single benefit that you can implement it in 5 minutes and move onto making the game again, it is dirty though, so up to you.

 

With that out of the way, I usually have at some level a "World" instance which represents everything in the world, player(s), camera(s), etc.  That object passed into the load level function makes sense and from that you can get the player information.  If you have any concept such as this, it may be the solution to just pass it down the call chain if possible.  If, on the other hand, it is still not viable to pass down the call chain a single question remains.  Is there any possibility that your could would EVER have two world objects?  If not, a singleton may be the solution.  I know, knee jerk reaction, singletons are EVIL!  Yes and no.  Overuse of singletons and the phoenix pattern are evil.  A well controlled and limited use of singletons still solves a lot of issues and if you use only accessors and functions, it can be controllable and clean.  I know I'll probably get talked at evilly for this suggestion, but it is a tool like any other, smashing your thumb with a hammer is not the hammers fault, yer the dumby that swung the thing.  :)

Share this post


Link to post
Share on other sites

in CAVEMAN, there's an object types database, and a data structure called a "stuff list" (an inventory list). players and "stores" both use stuff lists for inventory. for transactions, there's a "trade module" which takes pointers to two stuff lists, such as a player and a store stuff list. its all done with just one data structure type (the stuff list) which makes it a good candidate for OO encapsulation.

 

its sounds like your store and player inventories are different types of objects, and you are therefore unsure if your "trade module" method should be part of a player inventory or a store inventory object - or should be a separate object entirely.

 

doing it the oo way, you'd probably want an "inventory list" object with methods like add item, remove item, select item, display list, etc. the player and each store would have an instance of an "inventory list".

 

then you'd have a hunk of controlling code that would display the lists, make the calls to select the item to purchase, do the checks for sufficient funds and encumbrance, and make the calls to remove money from player's inventory, add money to the store's inventory, remove the item from store's inventory, and add the item to the player's inventory.

 

all this would probably be wrapped up in a single "inventory list" class, with "conduct transaction" as one of its methods. you'd have to come up with a convention as to which inventory list was the "host" (the one whose "conduct transaction" method you call) and which was the "guest" - the list the host is trading with. the guest's list would be passed to the host as a parameter. 

 

or you could put the put the controlling code in its own class and pass it two inventory lists.

Share this post


Link to post
Share on other sites

Again without knowing the code base, but hearing the problem and having decades of experience...

 

Why does it need to know about the shop at all?

 

It seems like you have an event:  A character is near an object.  That event gets broadcast for the UI.

 

Then you have an interaction, and quite likely your game already has several hundred interactions, and that "shop at the store" interaction gets run.

 

 

The object (which happens to be a store) receives the event that a character is near. If the character has an inventory, make the interaction available for the UI; if the character does not have an inventory or if the shop's inventory is empty, do not make it available.

 

Naturally your system is generic and has thousands of specialized interaction instances. The player sees the interaction on the UI, and chooses it, which launches that one interaction. When that particular interaction to buy from the shopping object is run, it plays the little animation and then runs Target.ShowShoppingDialog(Actor);

 

The shopping object (which is the target) generates a list of what we'll call StoreItem objects. Each StoreItem contains a name, a base price, a thumbnail image ID, the number of items available (including an 'infinite' option), a custom data pointer that gets passed back, a function to create the object, and anything else you may need.  Exactly how the object comes up with the list is specific to the object.  Some store-like objects, such as a shelf, may have objects placed on it and later removed. Other objects (like a rock mine) may have infinite number of the objects.  Or it may have a mix, with a limited number of some objects and an infinite number of another object.  Either way, you have a list of store items to sell.

 

Then you pull up the shopping UI, and pass it the customer, the list of items to sell, the destination inventory, and any other things the UI needs.  The UI code does all the validation, such as ensuring the player can afford all their items, that they don't buy too many items for their inventory, that they limit the number to what is actually in stock, and so on.  When the UI dismisses it has a callback, as most good UI systems do.

 

Very roughly you get this type of code:

struct StoreItem {
 string Name;  /* Unlocalized name of object to create */
 string BasePrice;
 ThumbnailKey Thumbnail;
 s32 AvailableCount;  /* note: use -1 for 'infinite' available. */
 void* CustomData;  
 Function <void*, GameObject> CreateStoreObject; /* Creates the object using the custom data, returns the new object which the UI code places in the Actor's inventory */
 ...
}

class SomeKindOfStore : GameObject {
  std::vector<StoreItem> GetItemsForSale( Actor actor) { 
    ... /* Make the list */ 
  } 
  class BuyStuffAtStore : Interaction < Actor, SomeKindOfStore > {
    bool Run () {
      ... /* Do starting animations and whatever else */
      float markup = 0.0f;
      float sale = 0.0f;      
      return UI::StartShoppingDialog( actor, target.GetItemsForSale(actor), markup, sale, FinishedCallback);
    }
    void FinishedCallback( Actor actor) {
        ... /* Play some finishing animations */
    }
  }
}
Edited by frob

Share this post


Link to post
Share on other sites


Then you pull up the shopping UI, and pass it the customer, the list of items to sell, the destination inventory, and any other things the UI needs.  The UI code does all the validation, such as ensuring the player can afford all their items, that they don't buy too many items for their inventory, that they limit the number to what is actually in stock, and so on.  When the UI dismisses it has a callback, as most good UI systems do.

 

this would be the equivalent of the "transaction module" in CAVEMAN.

 

looks like in the OP's case, his UI is the logical choice for the "conduct transaction" controlling code.

 

which really makes sense. don't think of the UI as a screen, think of it as a "transaction conducting machine" that just happens to display some pick menus, message boxes, etc. in the process of conducting a transaction. so it would seem that a "transaction conductor" class is called for. probably a class with no data and just methods.

Share this post


Link to post
Share on other sites

Those last 4 posts were so insightful I've solved my problem. I had to go back and refactor most of my UI (which allowed me to delete a lot of gunk code) and it works beautifully now.

 

Thank you for the suggestions and advice! I'm a better programmer for it.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement