Role: Engineer (solo)
Studio: Sunbreak Games
Technologies Used: Unity (C#), Spine, Visual Studio, Git
Exp Gained: Asset Bundles, Spine API
Care for our World was my first contract position as a game engineer and the first title I've
shipped. I was the only engineer on the project. It is a digital interactive version of a
popular children's book, and includes coloring and sticker book activities.
★ Featured in the AppStore.
Care For Our World was a great development experience. It was the first official contract I got as a freelance game developer, and my first released title (if you don’t count Vector Cthulhu, which you probably shouldn’t). It’s been a few month since we’ve released, and I’d like to spend some time going through the various aspects of the game, showing off what we did and talking through what I learned.
Care For Our World is a interactive adaptation of a popular children’s book of the same
The app takes the wholesome and beautiful story presented by the book and brings it to life. Each page is read to the player by a narrator while characters in the illustrations gently animate, reacting to touch.
Not only are the words in the book highlighted as narration read over them, but when animals and environments are touched the word for them pops up in addition to animations and sound effects. Both of these features are aimed at helping children learn to read. Also included with the app are a few mini-games: a coloring book, a sticker book, and an encyclopedia.
The art, animation, and story are so delightfully adorable. The days when our animators submitted new content to be implemented were days full of joy and giggles for me. Overall the project was really enjoyable, and I’m very proud to have it as my first released game.
Care for Our World was released for iOS in March of 2017. It has been featured multiple times in the app store, and has received very kind reviews from customers. If you’re interested in purchasing the app, it can be found here.
There were a lot of things that I learned during the development of CFOW. I’m not really going to cover everything, but there were some particularly memorable moments that I’d like to go over and burn into my dev-memory forever. I really wish I’d taken more screenshots/gifs in early development, I’m really regretting that now.
So I think by far the biggest lesson I learned on the project had to do with optimization. We had almost 20 pages of high resolution art and we wanted to oer HD and SD resources depending on the device. I had the plan in place to use asset bundles pretty early on, but I didn’t swap the code over to using asset bundles until pretty late into development, mostly because I didn’t want to explain to the designers and animators how to keep their bundles up to date, and the assets were changing enough that I think it would have been more annoying that anything.
When all of our assets were in and the game was starting to stabilize, our builds were averaging at about 1GB. As you can imagine, with a lot of similar games installing at about 200MB, our lead concerned about the build size. Then, on one magical night, I went through and put all the background (sprite) assets into Atlases, built all the asset bundles, and flipped the switch on making the code pull from asset bundles. Our build size dropped to somewhere around 220MB, and I felt like a magician.
A lot of this came from the Asset Bundling, but I also saved a lot with Sprite Atlasing. I hadn’t much messed with compression at this point, and wasn’t entirely sure the importance of POT (power of two) and square sprites. I did start noticing, however, that some of my sprites were like the one pictured on the left below, without any compression information (even though it was set to be compressed), and some looked like the screenshot below right. I also noticed a warning in the inspector, noting that only POT textures could be compressed.
I was a little crestfallen here – thinking that we’d need to resize all of our assets to be POT, probably putting a lot of them into sprite sheets. Luckily, after some googling, I learned about sprite packing in Unity. As soon as I organized all of my sprites into atlases (using the “packing tag” in the sprite import settings), the size of my asset bundles were reduced by about 35%. I did make the mistake early on of throwing all of the page backgrounds under one packing tag, which resulted in some asset bundle issues (since my asset bundles are by page) and a lot of superfluous things being loaded. So, after I grouped each page into their own packing tag, everything started going smoothly.
So. Asset Bundling. This was a big learning experience for me. In the beginning I went down a really long and very unnecessary path. Some background on how I set up this game to work: the scene that held the book was a single scene without any of the page contents in it. The pages themselves are prefabs that get loaded in one at a time. I toyed around for a while with loading them all in and staying inactive, but the lower end devices had a hard time handling it. So I went with loading the pages in on demand. I was left with some visual hitching between page turns, but it was better than the low end devices crashing out all the time.
Now, I don’t know why from the beginning I didn’t just assign each of those prefabs to their own asset bundle. Maybe I tried and for some reason it failed, or maybe I just really thought I had to assign things to asset bundle on a much more granular level. It might be that I initially started with loading the pages in as scenes instead of prefabs, and I didn’t think you could assign a scene to an asset bundle (I’ve never tried, now that I think about it I don’t know why you couldn’t). I don’t really remember, and I probably never will. But for whatever reason, I stared with going into each page and assigning all the contents to either a character asset bundle or an environment asset bundle. I then ran into some issues with assigning Spine characters to an asset bundle – something about scriptable objects was making the bundle process break. So, I spent a week or so trying to sort that out, eventually getting to the point where I was re-creating my spine characters from scratch at runtime (so I could create the scriptable objects instead of loading them from a bundle). As you can imagine, this was a little bit of a nightmare.
Then one day either it dawned on me or someone said something, but I realized I could just be assigning the entire page’s prefab to its own asset bundle and leave it at that. No assigning any of the pieces inside, just letting the whole prefab go in by itself. Turns out that worked, and I felt rather silly but also rather relieved. It made loading page contents pretty easy, and swapping assets via hd/sd rather simple as well.
I followed a similar pattern with the minigames. Hopefully next time I’ll be better prepared to work with Asset Bundles =]. Though now that I think back on it, having more experience encountering development difficulties, this could have gone much worse.
The Coloring Book
Our team lead teased for a while that I needed to remember that the coloring book was not the focus of the game, and that I should talk about the rest of the game with as much fervor as I talk about the coloring book. The development of it was a little bit of an adventure, and for a while I was really excited to talk about anyone who would listen.
The coloring book was the first things I worked on in the game, and it was one of my first lessons in ‘whatever works best’. Since this was pretty early on in my Unity career, I figured that since I was having people pay me to be an engineer now, I had to approach things like an engineer. So I assumed that this painting program was going to be full of algorithms and fancy blend coding and a whole mess of complicated thing.
The “Painting” Tool
My first attempt at the painting tool, if I remember correctly, was using Set Pixels to drop
raw color right onto my “canvas”. Initially this was promising. I was able to paint, but the
edges of my paint circle were rather ragged, and every time I tried to properly blend the colors and alpha at the edge, it never quite came out looking like I wanted. Not to mention
that it was actually processing a bit slower than I expected it to. At the time, there was also
the possibility of shaped brushes, and that added another layer of complexity to my already
rapidly-growing class. I then decided maybe it would be better to have textures for my
brushes, and use Get/Set Pixels to splat that texture onto my canvas. This was again working
alright, but it wasn’t as fast as I wanted to be, and I’m pretty sure I was encountering other
issues as well. (I really wish I’d documented more of this at the time).
Finally, after a lot of fussing, I wondered what would happen if I just instantiated sprites
when the mouse was held down, and then attened them using a render texture when the
user released. Surely, that couldn’t be better, could it? It didn’t have any crazy math or
algorithms, no way could it be better.
Well, it was better, apparently. Things started running a lot smoother, all of my blending was looking a lot nicer, and I was all-together pleased. I’m sure there is a way to programatically do that same thing and be even more efficient, but this method got me the performance I needed and was very simple to implement, which over time I grew to realize was more important than stressing over the “best solution” To soothe my “under engineered” feeling, I did end up implementing some object pooling for efficiency, and some timing/distance code to help the sprites drop more smoothly. But the point was, sometimes its easy to make things too hard.
The “Fill” Tool
For my first fill tool, I used Flood Fill algorithm and, again, painted directly to the canvas. I
tried a lot of different fill methods, but they all had similar issues: They tool a long time to
execute, they sometimes missed areas, and you had to have absolutely clean cut lines in
order for the algorithm to get close enough to the lines for it to look good. I tried to combat
the last issue by having two linearts – one with really clean tight lines for the fill algorithm,
and then thicker lineart layered over the top of it to hide the paint not getting close enough
to the lines; and it kind of worked, but the other issues still weren’t solved.
I once again went backwards in fancy-pants coding for a simpler solution. I created a white sprite for each of the fill regions, made a collider in the scene for that region, and just splotted that sprite with whatever color you selected onto the canvas, and then collapsed it with the render texture, same as I did with the painting tool. This required a bit more setup in the editor and photoshop, but it ended up feeling really good and looking really nice. It also fixed an issue we were running into with a lot of the book’s art having a lot of tiny little pieces to fill, and the fill tool being frustrating to use in those situations. We were instead able to group a lot of small regions together (like the zebra’s stripes) so you would fill them all at once.
So, basically, for everything Coloring Book, I spent a bunch of unnecessary time trying to implement a complex solution instead of going for the simplest, and worrying about it if it didn’t work out.
I won’t dive too far into this – I just want to briey cover the use of Spine with our project. I’d never used Spine before, and though I struggled a little from time to time, I was able to do everything I wanted to do with relative ease. Any time I encountered troubles (like when I was struggling with Asset Bundles), their team was quick to respond on their forums when I had a question.
I think my biggest “facepalm” moment, was after spending a few hours digging around trying like hell to find a list of animations so that I could present it in a dropdown on the custom editor I made for our characters, I eventally found it in SkeletonAnimation.Skeleton.Data.Animations. So in order to generate a list that I could create a dropdown from, I had this code:
Not the most graceful thing, but it worked. And since it took me longer than I’d like to admit to find that Animation array, I was fed up with it and perfectly happy to call it good enough. However, a few weeks later, I was watching a tutorial and saw this little beauty in someone’s code:
And wouldn’t you know it – that attribute makes the spineAnimation variable present itself as a dropdown in the editor; with all of the animations of the attached Skeleton Animation. I was both extremely happy and very disappointed that I hadn’t found it earlier. I know it doesn’t seem like a lot, but it was just one of those problems that was so frustrating.
Okay, I really think I’ve been talking long enough. That wraps up my Post Mortem/Development Rant/Whatever this was. I really had a lot of fun on this team, and I’m very grateful for a good first experience in game development.