Tiled Of It - October 2022 Postmortem
State of the project
"Tiled of it" (working title: "Fragment Fuser") was born during the Devtober 2022 jam. I wanted to pick a project I could complete in one month without many sacrifices.
The hot questions were:
- Can I make a card game where you combine cards to make words?
- How do I pick which text goes on each card? (Is it even possible to have a deck of cards that makes hundreds of words?)
- How do I make it fun to play?
For small projects like this, I like to lean on some of my strengths while exploring new curiosities and weaknesses.
The blend was supposed to be:
Lean on familiar stuff:
- Make it a word game
- Add networking support (spoiler: the way I implemented it was less familiar than I had planned)
Explore unfamiliar stuff:
- Make it a card game
- Use Unity UI controls and callbacks in more depth
- Use networking hooks within Unity. I landed on Evan Lindsey's Unity-WebGL-SignalR wrappers.
- Use multiple projects. For example, I'd have a Console app for puzzle simulations and deck generation, a Web app to host the SignalR hub, a Unity library, and a Common library that the other projects referenced.
Since my previous project (Punchy Jam Plant) focused on music and art, I would rely on non-original sound and image assets. For example, I would use Kenney assets for audio and cards.
What went wrong?
My biggest struggles stemmed from making this an online multiplayer game. I have a strong Web and network-related background, but not in this particular stack. I hit a lot of interesting surprises along the way, which ate more time than I had planned.
Off the top of my head, surprises included:
- ASP.NET Core SignalR syntax is different than what I'd used in ASP.NET / .NET Framework. This forced me to take detours, like learning about how ASP.NET Core dependency injection since I couldn't access a singleton.
- Unity seems geared toward single-threaded apps, not async/background-thread stuff. Some SignalR callbacks fired on background threads and led to odd quirks. For example, if I updated a TextMeshProUIGUI.text property in a background thread, the change would not be reflected in-game until the object became dirty some other way (such as by re-positioning it). Life was good once I refactored my logic to queue these commands for the main thread.
- I'm grateful for the Unity-WebGL-SignalR hooks because I wouldn't have been able to create them myself. But I hit some connection-resiliency-related snags. For example, there didn't seem to be a way for the app to learn that there was a connection error. And the WebGL wrapper's callback dictionary was designed to only be used once (via a static Dictionary and Add calls), whereas I would be replacing callbacks over time. I hacked that plumbing in, so users were less likely to encounter a silent network error, and they were able to connect to the server multiple times in the app's lifetime.
It was exciting to overcome each hurdle, and I'm glad I learned a lot, but it took so long.
I didn't rely on stock art as much as I had planned. A few Kenney icons stuck around, but most were replaced by crude drawings. I was also hoping to add more sound effects and music but ran out of time.
I meant to create avatars for each player. I also kept changing the "gameplay" art and never hit something that felt right.
What went right?
The game concept worked better than I expected.
I was relieved to generate a deck of potent cards early in the month. The poorest-performing card in the 50-card deck could still pair with 18 other cards. The game idea was viable. Whew.
Deck generation worked like this:
- Split words into their substrings
- Make a list of popular substrings (used by many words)
- Randomly draft some substrings from that pool, and use them as cards in a deck.
- Score each card based on how many other cards it combined with
- Score the deck based on how many words you can make by combining two cards.
- Mutate the deck into a competing deck: copy the original deck, remove some [random, poor-performing, etc.] cards, and replace them with alternative cards.
- If the new (mutated) deck scores better, it becomes the new "best deck."
- Keep mutating the "best deck" until you can't.
It also helped to have a game engine and simulation before diving deep into the UI. I wrote a command-line app that simulated playing the game. This allowed me to track things like:
- How often is a player forced to discard because they can't make a play? We want to keep this low.
- Do the rules favor the first player or the last player?
- Should players take turns in a circle, or play "as fast as they want", or some other order?
...and then tweak the rules to overcome issues.
The v0.5 prototype still has a bias in favor of the last player, but it could have been so much worse. :) Long-term, we can avoid the bias by playing multiple rounds, and giving each player a round in Nth place.
Originally, I planned to have multiple single-word piles, where you could add [and remove, and re-order] a card to form a new word. This was complicated to explain, and caused more discarding than I was comfortable with. Since I hadn't invested much in the UI or networking yet, I was able to flip to a grid structure without a ton of rework.
Finally, it helped that my family play-tested the game. This encouraged me to continue and made me aware of usability issues.
What's next?
In the short term, I'll be taking a break to avoid burnout.
After that, I might revisit the backlog. At the top of the backlog are topics like:
- Improve graphics
- Add sound effects and music (and volume controls, for folks who don't want this stuff)
- Refine the dictionary (some words and definitions and explicitness aren't quite right)
- Add more fanfare around points and winners at the end of the game
- Improve the online multiplayer experience, such as by letting players vote to skip an away-from-keyboard player's turn.
- Track local achievements, like when a new word is built.
- Allow players to configure the bot's difficulty
- Local multiplayer
If you try the game, please drop a comment! I appreciate your constructive feedback!
Leave a comment
Log in with itch.io to leave a comment.