Today, I'd like to talk to you about the in-game shop. The eventual plan is to offer a way to pay for items using real money, but before I get into the world of microtransactions, I wanted to have the base exchange system working for people who'd prefer to grind (and it's only grinding if you're not having fun, right?)
Let me start with a high-level explanation of the code. My game's save data includes several lists of Boolean values, one for subs, one for levels, and a few others that I'll keep secret for now. These lists are index-matched with my master lists of all the content in the game, so if the first three values of the subs list are all "true", then the game knows to give you those ones as options on the character select screen, while the rest should be available to buy in the shop. With me so far?
Subs, levels, and all the mystery items all extend a special ShopItem class, which just means that they all share a lot of base functionality with each other. In this case, that functionality includes data like their name, sprite image, and description for displaying, plus a price to unlock, of course. When you're looking at the list of all the items in the shop, clicking on one takes you to a second menu that has just that item displayed, with all of its information and the button to actually buy it. (I tested a few different ways of doing this, and the second menu ended up being the best one, especially considering the size of the phone screen and the available space there.) If you buy the item, it's officially unlocked by changing its value in the list to "true", and won't show up anymore when you return to the main shop menu.
Pretty straightforward, right? As usual, the toughest part was making the corresponding visuals. I wanted to allocate as much screen space as possible to the item buttons, and I had to think about the most important information to display on them. The image, obviously, but then I thought the price would be next most relevant; if you need to click through to a second menu to actually buy, it would be nice to see the price beforehand, to determine if the item is affordable before taking the second step. I created a button template that I'm happy with:
Now, here's where things get a little technical, so bear with me. The Unity UI system includes controls called "Layout Groups", which are mostly intended to be used for spacing objects when working with multiple resolutions. Say you have a player-select screen in a multiplayer game, and want each of the players to be allocated an equal amount of space. Since the game (as always) could be run on different sized screens, a layout group would be an easy way to say "expand these items evenly so they fill up the whole space available", and it would do so without needing any extra coding or math on your end. In my case, I'm using a horizontal layout group here for the coin image and price text. I want the two things to fill up the horizontal space of the button, but since the price could be anywhere in the range from the tens to the thousands, I'd need 1) to make the text both expand its percentage of the space and get smaller as more zeroes are added, and 2) the coin to get smaller at the same scale, so it doesn't look weird. By combining Unity's "best fit" setting for the text, and using the layout group with a very specific set of options, this is both possible and works well. Now, the layout group options are a bit of a mystery, and the official docs are useless, but in this case I'd actually recommend just trying things out, because odds are that you can accomplish what you want, but trying to find a "how-to" for your exact use case will be nearly impossible.
I had a couple of different options to display the buttons in the menu. I originally wanted to use layout groups for them also, but what I really wanted was for the buttons to be bigger depending on how many of them there were. So if you've bought everything except one item, that single button takes up the entire space, but otherwise, the buttons are significantly smaller. The first problem with that idea was that the buttons could only get smaller to a point, and since I already have a bunch of sub paintjob ideas, it wouldn't work without some other...something. The second problem was that the auto-expanding buttons were not playing well at all with the template I had set up. It likely had to do with the settings I used to get everything to look good on a single button, that telling the whole parent to expand broke them. The third problem, although not as big, was that I discovered what I believe to be a bug in Unity's auto-spacing algorithm. One of the layout group settings distributes the space between elements "evenly", except what it actually does is put a uniform amount of space on both sides of every item. What you end up with is twice as much space between each set of items as on the absolute ends. Maybe not a dealbreaker depending on the situation (and almost certainly not for a phone screen), but the second problem meant I couldn't continue down this approach anyway. What I ended up doing instead was making separate, static layouts for each possible number of buttons, and spacing them correctly myself.
My idea to solve the first problem was to cap the number of buttons I allow, and if I have more items that that at any given time, I will randomly choose which ones are displayed. The code for doing that is not the cleanest, but I think it's kind-of a unique way of doing it. Every time you go into the shop, you'll see a different set of items, so it's like a mystery assortment. I much prefer this to, say, always taking the first n items from the list, so you can see more of the total assortment faster and start saving up for specific things if you want. And if something you are trying to buy doesn't show up when you go looking for it, you just need to reset the menu for another chance to see it. I can't think of any other games offhand that do this, so it's pretty cool that a problem turned into a unique feature.
I'd show you what the finished menu looks like, but I don't want to spoil some of the special subs!