Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

Racecar Bed Retrospective (Post-Mortem)

A topic by Scoot o' Speed Studios created Nov 04, 2021 Views: 134
Viewing posts 1 to 1
Submitted

TL;DR. Began iterating on Skill Creation GUI. Got the basic Skill Creation gameplay loop working. Offloaded game data to Google Sheets. Spent too much time playing an MMO.

I prefer using the term "retrospective" since post-mortem implies the project is dead. Our game is nowhere near being dead and is still being created. Instead, I'm reflecting on a specific period of the project.

Intro

I (Josh), am 1/3 of Scoot o' Speed Studios. I'm our programmer and lead designer. This is reflective of my (Josh) experience in October, so I don't speak for the whole team.

Our current project is Racecar Bed. A Social Sim + RPG with time management and a friendship system similar to Persona 3/4/5. Oh, and you can make your combat skills. My main goal for Devtober was to complete the basic Skill Creation gameplay loop.



Background

I went into Devtober with a basic turn-based combat system with health, sp (mana), party members, and leveling. What I didn't have was the skill creation system; one of the main hooks and mechanics that sets us apart from Persona. 

The *rough* idea is to receive (legally distinct) toy blocks as a reward for spending your limited days hanging out with different people. The blocks can have different attributes like increasing/multiplying healing/damage, lowering sp cost, increasing target count, etc. 

Using those bricks, you can piece together a unique skill that is both uniquely representative of your friendships and devastating in battle. These skills can affect both enemies and party members depending on the block placement. So there can be tradeoffs to make when making a unique build.

In September, I had started working on the GUI (Graphical User Interface) of placing blocks on top of each other in a 5x2 grid.

GUI

I continued the GUI work by supporting block widths between 1-n. Programming blocks to find the nearest open slot with a block below and then adjusting the rest of the block to shift over to the nearest gap was pretty difficult. I envy the children who make playing with blocks look so easy

Then towards the end of the month, I worked on polishing the GUI. 

I wanted a real-time preview of what the block-built move does. I tried doing a running total of damage and sp cost next to each side of the move's blocks, but things got crammed real fast.


I ended up ditching the “stats per row” idea and consolidated the move’s stats to the left 3rd of the screen. Since I’d like a similar panel to show up when hovering over the move in combat, I figured this would help bridge the gap between the two. So less time for a player trying to imagine what it would look like in combat, and more time building it. 

Each block has an associated sp cost, so I started experimenting with showing that. However, this version looks cluttered. Especially with all the block text. I pivoted to icons instead of move names. As for the sp costs, I’d like to be able to hover over a block and have a tiny panel the length of the block showing how much it costs. But I’ll do that later.


I began working on contextually showing the fields if their values weren’t equal to zero (if damage is 0, don’t show it at all). I didn’t finish it in time for Devtober, but I do like where it’s heading. 

Move Calculations

A bit later in the month, I worked on calculating the skill based on block placement. They actually were much easier to do than the GUI (thankfully). Each block overrides an abstract class called ABrick (I've been calling the blocks bricks in programming). This class has an sp cost and an abstract method with a signature 

public SkillMove ApplyModification(SkillMove skillMove)

The move being calculated is passed in, gets modified in whichever the class pleases, and is returned. Every block inherits this class allowing for a chain of modifications. Since the brick grid is set up as a 2D List, it's just a matter of iterating through it. The party and enemy skillMoves are split down the middle, so it goes through the first half, then the second half.

The code's super clean and adding a new block type is as simple as inheriting the ABrick class and overriding the ApplyModification method.

While moves that have multiple actions are interesting, they did cause a lot of refactoring work. The "move" was initially programmed as having a single action. But having multiple actions affected move saving/loading, target selection/deselection for both the AI and Player. The interesting (well, interesting to me) part of this was how some of the older code was easily adapted.

Moves are played out in combat by first selecting 1 or more targets from a pool of characters (enemies, party, all), then performing the move's animation for their target. The current implementation is a tween using a custom animation curve and an animation key (or timestamp) used as a callback of when to trigger the collision on the targeted characters. Then using an OnComplete call to trigger the next turn in combat. 

What makes this easy, is that it can iterate through a list of moves, feeding each move's targeting group for selection, then chaining each callback to the next move. Then the final move calls the next turn method. So for that piece, to make a move with multiple "moves", all I had to do was add a couple for loops.

Google Sheets

I nabbed a Unity Asset from Humble Bundle called Databox. Databox provides a framework for saving and loading game data. My favorite feature is its integration with Google Sheets to pull data directly from it. Being an RPG with stats and leveling, I wanted to visualize this data as much as I can, so offloading my table data to sheets was a goal of mine. 

My pipedream is to be able to predict a player's level range based on the number required and optional enemy encounters so I can tune early game stats/XP/level curves without being unaware of late-game snowball effects. Of course, it won't be a total replacement for good ol' manual testing, but it's certainly a start.

As for Devtober, I created a sheet of moves with damage, target count, and sp cost fields.

I also made two sheets assigning the moves to different characters. One for the party members, and one for the enemies. The enemies also got a percentage chance of using each move. Down the line, I'd like to work on stronger enemy decision-making, but this is a great start. Then I created an editor script to pull the data and set the character prefab's moves from a <string, ScriptableObject> dictionary of moves.

Ultimately, I think I spent too much time trying to create a catch-all table of data. Maybe it will be a huge help down the line, but there are certainly tradeoffs from it. It forced me to solidify fields so prototyping different combat mechanics may be difficult down the line if they don’t fit this table skeleton. However, doing this later in the process could have slowed down development to a grinding halt. Only time will tell on this one.

What Went Wrong

Ok, I got a free New World key and played the heck out of it at launch. Other than that, everything I worked on went pretty smoothly. My Devtober goals were to complete the full skill creation loop, load game data from Google Sheets, add battle status effects, and finish 3 boss fights. I got the first two done, and began the work of designing status effects, but fell into an interesting rabbit hole of how to make turn-based combat engaging. Check out this article on randomness in turn-based combat. Oh and this great video on team role archetypes in combat. Super interesting and inspiring!

What I Learned

Say no to MMO’s. Also, UI for a unique system requires lots of iteration since there isn’t a commonly used framework. I’m certainly not done with it yet. But it also means there are some cool possibilities to explore. I’m sure I’ll learn more about how my decisions this month will affect the project down line later.

If you’d like to support us, the best way to help us out is by Wishlisting Racecar Bed on Steam