Market Forces

Posted by on Jul 13, 2015 in Game Development

Central to the idea of a “space trading game” is the presence of something to trade. The most basic idea is that you as a player must find the lowest price on a specific item, buy as much of that as you can afford/hold, and then fly it to another location where you sell it for more — hopefully much more — than you paid for it. In a simulation, it can’t be the case where you bounce between two nearby stations ferrying goods back and forth until you’ve amassed a fortune, so the system needs to implement a kind of rough supply-and-demand system so that stations with lower inventory will buy for more cash, while stations with higher inventories will offer less cash. Behind the scenes, processes run to simulate other traders, moving goods around to shift buy/sell conditions at all stations.

I’ve gone over this system about 100 times at this point, but I still haven’t quite mastered the art of market simulation. Ideally, I should be creating dummy data and then simulating it with a spreadsheet or some quick scripting tests. I am cursed with being a visual developer; I need to see something in front of me in some semblance of how I picture it in my mind, and that usually leads me to taking a detour into the world of UI design.

The “new” Unity UI is better than the old system at least because it’s visual. I can drag elements onto the screen and position them, wiring up actions to event handlers to make stuff happen. Unfortunately, the new system is sometimes pretty esoteric if you’re not internalizing it’s quirks. Scale and position have all thrown me for a loop recently, and I’m always just one slip of the mouse away from getting myself hopelessly tweaked in a way that I struggle to recover from, because now a panel is too big or the font is too jagged. I learned today that using childTransform.SetParent(parentTransform, false) is better than the standard transform.parent = ? when dealing with the UI because the former method respects scale and position, whereas the later does not. Thankfully, I learned that quickly. Other questions haven’t been as forthcoming with satisfactory answers.

Like other iterations I’ve worked on in the past, this market panel takes the inventory of a specific station’s commodity (trading goods) market and lists them. Unlike previous attempts, this is an integrated list. Previous attempts had been working under the assumption that I needed to have two panels: one for the shop inventory, and one for the player inventory. But that was unwieldy and required me to duplicate code, once for the player, once for the shop. And then they had to talk to one another to keep each other updated. Yeesh.

My decision to use a central data clearinghouse is paying off by helping to fix this issue. Now I’m taking more of an Elite: Dangerous method as opposed to the traditional RPG shop method. The list of commodities at any shop shows one row for each item. Each row contains the name of the commodity, it’s calculated price, the quantity in the shop, and a quantity that the player is hauling. The SHOP and HOLD columns will have buttons pointing at one another (SHOP > and < HOLD). Those buttons equate to “transfer one unit of this item in the direction of the arrow when I click that arrow”.


Testing the new commodity market layout (WIP).

What’s happening under the hood (eventually) is this:

  1. Clicking the arrow (not shown in the image above) updates either a SHOP_CART or PLAYER_CART collection with the quantity. The SHOP_CART is used when the player is selling, and the PLAYER_CART is used when the player is buying.
  2. The quantity of the item in the SHOP is calculated based on the quantity of the shop, and plus or minus the values in the CART collections. When a player is buying from the shop, the shop quantity should be diminished. But the carts are there to hold items until the transaction is complete; therefor, we need to take the shop quantity and subtract the PLAYER_CART amount from that value. We’re essentially putting that in escrow until the player decides to check out, or abandon the cart.
  3. At the same time, the value in the HOLD is the current count of that item in the player inventory, plus the amount in PLAYER_CART. So to the player, it looks like she’s bought — but not finalized — a quantity from the shop (seen the number go down) and transferred it to her ship (seen the quantity go up).
  4. All of this takes place simply by updating various data collections, and the display of the labels on each row are updated in the instance’s UPDATE statement. Polling the data collection runs pretty fast. In previous attempts I tried to have GameObjects communicate changes to one another, and that was a pain in the ass. This method updates data and lets the instanced shop item row handle the display. It’s much easier.
  5. When the player is done, she can click CHECKOUT. At that point, the quantity will be formally deducted from the shop using the SHOP_CART, and the the quantity in the hold will be incremented using the PLAYER_CART. Also, cash will be debited/credited accordingly. The carts will then be cleared.

One thing I’ve always wanted to do was to add an extra layer not usually found in trading sims, and that’s history.  I know that EVE Online has a kind of trading history, so players can see what the historical high and low prices were for specific items. In a single player game like mine, though, I think that data would be for edification, maybe for light analysis. Obviously you can see the difference in price between toilet paper and palladium, but what’s the profit margin? Is it better to haul toilet paper in one sector, even if palladium is available? This is why I need to work weight or mass into each item, and to create different classes of cargo holds, so you might be able to jam in more soft rolls of TP than you can carry in palladium bars.

First things first: I want to make the player inventory, a cargo hold view, and then get the buying and selling working.