Updated: Aug 22, 2019
It's time for the third installment of my discourse on UO's core gameplay loop, collect treasure. Treasure is the currency of the game, and you'll need an awful lot of it to purchase all of the subs, levels, and upgrades from the in-game shop. In fact, balancing the treasure collection rate and the prices of said items has been and will continue to be one of the greatest challenges for me, and I'll be relying on playtest feedback to help with that as well. Of course, there's another option, with microtransactions... I don't particularly want to take an ethical stand on that issue here, but it's a known staple of mobile gaming and UO will indeed have the option. But I will say that, whenever possible, I will make earning bonus treasure an option by playing a consecutive number of days or watching video ads, ways that do not cost the player but still encourage further play and make me money.
But I digress; this post is about how I implemented the collection system. To do so, the main things I needed to consider were 1) what was being spawned, 2) how often, and most importantly, 3) where. I also knew that one of the available sub upgrades was a "treasure detector", so 1 and to some extent 2 would tie into that.
I started with 2. If you remember from last time, I'm spawning my level one section at a time, with each section holding three gates. Because of the relative widths of each, I decided the best approach was to spawn treasure on a per-section basis, with a minimum of one and maximum of three items per section. This frequency just "felt right", but the important part is that the section is responsible for the spawning, not the gates. If I had done the latter, all the section could decide was whether or not to spawn something, without having any knowledge of what was going on in the other sections around it. I could have tried to transform the percentage chances so they worked better for per-section, but there still would have been a noticeable difference. For one, there would no longer be guaranteed one treasure item per every three, and for another, the distances between spawns seemed "less averaged" (if that makes any sense) when I tried it that way.
Now, one continued problem with this approach was that I was doing the actual spawn positions within the section completely randomly. If you're thinking ahead, you'll notice an even bigger problem that I'll address shortly, but one thing I noticed immediately with testing was that the treasure always seemed to get bunched up: two coins would spawn right next to each other, and then there would be nothing for the whole rest of the section. It's like my random positioning algorithm wasn't working at all. After some headscratching and brainstorming solutions (one of which was creating subsections within the section and limiting spawn to one per each of those), I realized that there was a much better way: just keep a static distance counter as the game was being played, and spawn a treasure item every x-distance. "x" could be determined by the level of the treasure detector, and the only thing left to fiddle with was "when" to spawn, in terms of "how far ahead of the sub should there be treasure at any given time". I still tied this into the section, so each time a new section spawns, I also spawn enough treasure to fill past the end of it, done by comparing my current distance counter and the spawn position of the section. Follow that?
The constant spawn distance also made 1 a little easier to implement, because instead of trying to do complicated percentage math which took into account both treasure type and whether any treasure should be spawned at all, I now know that there will always be a spawn every x-distance, and just use a given set of percentages to determine what it will be. These percentages also change depending on the treasure detector's level. The result is that a better treasure detector results in something being spawned more often, and a higher chance that it will be something more valuable...the same as the spaghetti code I was originally describing, but much easier to follow and maintain in the future.
And now for the elephant in the room, number 3. I once worked on a platformer game, a 2D infinite runner that had dynamic level generation, literally sets of platforms that spawned ahead of you to jump on as you continued moving forward. The way that collectibles spawned in that game was...not very well. It was all completely random, almost always leading to them being spawned on top of the platforms or other obstacles..."on top of" from a 2D perspective, making them impossible to collect. As soon as I pointed this out to someone, the response was "Oh, well you can fix that by manually adding safe spawn points to every single one of the platform set prefabs". That was not something I wanted to repeat for UO, and anyway I would have to backtrack on my above comments and make the spawn based on gates after all, since all obstacles live in their gates. But if I didn't do this, wouldn't I have the same problem as the platformer? Maybe not... After some quick tests with colliders (if spawnLocation == insideOtherObstacle, PickNewSpawnLocation()) which didn't accomplish much besides just complicating all the physics interactions in the game, I started to wonder if maybe the overlap wasn't such a bad thing after all. Remember that the first half of the core gameplay loop is about dodging obstacles to stay alive, so by keeping completely random spawn, I could incidentally create situations where I was almost daring the player to see how close they would get in order to nab that treasure item. And another point the platformer didn't have: the UO submarine has a torpedo weapon that can destroy anything it hits, so you could easily blast the obstacle away and collect the treasure safely. Of course, that powerful ability comes with a decent recharge time, so if you use it to get treasure, you may not have it when you need to clear an obstacle you really can't avoid otherwise...
That's twice when dealing with treasure collection that the simplest solution ended up being the best for my purposes. I'm pretty pleased with how that worked out, and I think the way the parts of the core gameplay loop function and interact make for some pretty solid gameplay overall.