« Posts tagged coding

How Many Pages of Code in an Indie Game?

FLUTTERFLY is done and launches for iOS this week! And after almost nine months of non stop work, it’s time to reflect. How much work did I actually do? With an app it’s tough to tell sometimes. You bang on your keyboard typing code at the prompt… then you compile… and then an app appears as a single icon on a device you can put in your pocket.

Unlike a novel, you can’t flip through it and feel the pages. Unlike a movie, you can’t scroll through the timeline to get a sense for every frame. It’s a game! It has no real physical mass, as it were.

So I decided to convert EVERY PAGE of code I personally wrote into a pdf, then in photoshop, shrink them down into little jpegs and line them all up. It’s a fascinating way to get a sense of the over all work involved in an indie game.

So: HOW MANY PAGES OF CODE IN AN INDIE GAME?

The answer?

An astonishing 1,266 PAGES OF CODE in Flutterfly that I wrote myself.

(SCROLL DOWN TO SEE ALL THE PAGES!)

A few big caveats:

1. There is obviously WAY more code in the app than just the stuff I wrote myself. There are tons of amazing plugins that I bought and used. And of course the Unity3d game engine which the whole thing is built on probably has enough bits to fill a library. But for this experiment, I think the page count of the stuff I wrote personally is a pretty cool metric.

2. Pages of code are generously spaced. It’s not like this stuff is packed in there. You’ll see from the images below that code, in this case C#, is not very dense when it comes to the amount of space it takes up. So take those 1,266 pages with a grain of wide-margined salt.

3. Code is repetitive. There’s a lot of code that repeats. For instance, almost every class I wrote starts with
using UnityEngine;
using System.Collections;

It’s part of the lexicon. So it’s not like every line of code is completely unique necessarily.

4. A lot of code gets deleted. The game evolved a lot over nine months. So entire sections were ultimately zapped. I’d estimate that at least a good 10-15% of the final product were axed and aren’t pictured below.

5. There is a lot of work that goes into a game that is NOT CODE. There’s animation and sprite sheets and models and sound and camera work. So the code is maybe half of the whole story. At most.

6. A lot of programmers pride themselves on writing super condensed, tight and streamlined code. The less code the better! I’m not one of those guys! I’m more from the literate school: lots of comments and notes to myself. Just fyi.

7. I’m seeing now that a couple blank pages snuck in there. Whoops. Converting over a thousand pages of code into little thumbnails is no easy task unto itself. I highly recommend this little photoshop script if you’re interested in trying the same for your game!

So without further ado, here are the 1,266 pages of code I wrote for FLUTTERFLY over the last nine months. I’ve colored some of it RED and have annotated those sections. Figured it’d be interesting to get a sense of how much of the code did what exactly. Who knew powerups were so complicated?

See you after the long scroll!

code
code
code
code
code
code
code
code
code
code
code
code
code
code
code
code
code
code
code
code
code
code
code
code
code
WindManager

Whew! I feel exhausted just looking at all of that.

And sometimes writing code is like creating music– it just flows out of you. But more often than not it can be a very exacting and painstaking process where you battle through mazes of logic to just write a single line.

Mostly though, I’m excited about starting the next project! Time to open up a new blank page one.

PS: I did a similar experiment with my previous, smaller app SPIDER PRANK. If you’re curious, you can check it out here!

Odds Are

Okay so Flutter 1.0 landed with a dull thud in the app store. But THAT’S OKAY! It has only encouraged me to make it better.

So I’ve spent the last few weeks adding POWERUPS.

Boy are these fun to play with! More on those soon.

powerup

But in the meantime, I was thinking the other day about games and apps and what if you could pull back their skin and see their guts. Like what if you could take a game and strip it of all the design and particle effects and fancy graphics etc. What would be left?

I think it would look like this basically:

odds

These are ODDS. Aren’t they beautiful?

Specifically, these are the odds that specific powerup types will occur depending on our current difficulty level.

You can see from the array setups that there are 8 levels of difficulty (top to bottom), and 11 powerup types (left to right).

At difficulty 0, when a game first starts, the odds are 80% you’ll get a powerup type 0 (the most basic and rudimentary powerup type), and a 20% chance you’ll get a powerup of type 1.

Looking at the numbers feels like peeking into the Matrix to me. I find it fascinating that looking these over you can almost get a kinetic sense of the gameplay. This is the soul of the machine.

As a developer, odds are always top of mind. And of course with something like powerups, this is just the tip of the iceberg. There are other things to consider.

My coding style is pretty verbose. I comment the hell out of stuff to make sure I don’t get confused later. Apparently this is called Literate Programming. Here’s a snippet of comments outlining some of the odds logic at work:

// *** THIS IS WHERE THE MAGIC HAPPENS WHERE WE PICK WHICH POWERUP TO MAKE THIS ***
// need to consider a few things like:
// 1. have we already used this powerup recently? for some powerups that matters a little
// 2. certain powerups should be more prevelant at higher difficulty levels
// 3. of course only pick from powerups that are unlocked/available
// 4. if we *just* unlocked a powerup, we should probably use that one at least once in the following game
// 5. blackopolypse requires a lot of black blocks in the glowboxset
// 6. flutterworks requires a lot of colored blocks in the glowboxset


Isn’t life itself just an infinite array of odds?

Now that’s an odd thought!

Dumb Coding Mistakes #632

Okay here’s a good one that had me stumped all afternoon.

I must say that as far as collections go, I’m used to using arrays. Arrays are great cuz they’re typed, they’re fast, and they’re relatively easy to set up and iterate through. The problem is they’re not dynamic. So it’s tough to quickly add and delete stuff from an array on the fly.

Enter the List. Lists are typed like arrays, but they are dynamic– meaning you can just add stuff to your List and the list grows. If you zap something from your list the list shrinks.

This is very useful for saving. In my app, users can add photos to use as backgrounds that they either take themselves or pick from their camera roll. Saving this data is great for a List. I don’t know how many different photos they will add over the course of the app’s life on their device and I don’t care. Save and delete your photos as you wish! They’ll all be stored in a List.

The dumb coding mistake is as follows.

In startup, I need to load these photos. So I iterate through the List. I also check to see if there are problems. If there is missing data or null data I simply throw up my hands and zap the thing from the List. Something went wrong, I’m destroying it. The user can import it again if need be. This isn’t like someone’s bank account.

So it goes something like this:

// ON LOAD

for (int i = 0; i < listOfBackgrounds.Count; i++) { if (listOfBackgrounds[i].IsScrewedUp()) { // list item is messed up or corrupted! // zap it! listOfBackgrounds.RemoveAt(i); } else { // everything is fine! load the background listOfBackgrounds[i].LoadBg(); } } Simple enough, right? We loop through all the items in the list. If we detect an error we zap the offending object and remove it from the list. If it's fine, we load it. ERROR! NULL REFERENCE EXCEPTION! Do you spot the mistake? It had my tearing my hair out for quite a while. Having been used to working with arrays instead of Lists, I overlooked an important quality of Lists. They are dynamic. Most of the time this is great. It allows you to add and drop stuff on the fly. But the problem with that, is the List is always CHANGING! Take the following array: myArray = new string[] {A, B, C, D}; It stores four strings. A is in index 0. B is in index 1. C is in index 2. D is in index 3. (both arrays and lists start counting their indexes at 0 -- itself the source of many dumb coding errors) If we DELETED the B at index 1, what would the value of index 3 be? Index 3 would still be D. Index 1 would have a null since we zapped the B. But I would still know what was in index 3 (and all the other indexes for that matter). A LIST on the other hand, continues to resort itself. So for a similar list with values of A,B,C,D in indexes 0,1,2,3... What would be the value at index 3 if we removed the value at index 1? It would be NULL! If we remove the value at index 1-- ie if we remove the B-- then the ENTIRE LIST changes to become: A,C,D in indexes of 0,1,2 Index 3 does not exist anymore! Everything shifts over. And thus my dumb coding mistake. I was iterating through a LIST while removing things from the list. So the list was CHANGING as I was trying to access it. The solution? Store the indexes I wanted to zap in a SEPARATE LIST which I then looped through AFTER the main for loop. After iterating through the main list and storing the numbers, only then was it safe to zap stuff from the original list and feel confident I knew what I was zapping. Even then, I had to loop through the separate list BACKWARDS. Removing index 5 wouldn't have any effect on indexes 0,1,2,3,4. So as long as I removed indexes from highest index to lowest index, even though the List was dynamically shifting, it wouldn't screw everything up. Another brainteaser for the books. I've learned that the only way through this stuff is banging your head against the keyboard until your stupidity gives up and leaves you alone.

Dumb Coding Mistakes #1

It’s easy to make dumb mistakes when you’re coding. Like here’s one I always make.

Say I have a slider for the interface with a knob in the middle you can slide back and forth. I need to check the position of the knob to see if it has moved left or right.

So I’m always tempted to do:


void CheckSliderPos() {
if (myKnob.transform.localposition.x > 5f) {
// do some stuff cuz the knob x pos is greater than 5 now
} else if (myKnob.transform.localposition.x < 3f) { // do some stuff cuz the knob x pos is less than 3 now } }

So what's wrong with this? Usually, NOTHING! And that's the problem. Usually this sort of check is just fine. But what if there are more things to check? What if there is a whole list of if thens. And then what if the user slides the knob REALLY FAST?

Well, in this case there is the rare possibility that the user SLIDES THE SLIDER faster than the machine can compute the code. And this leads to ugly bugs.

So imagine this:


void CheckSliderPos() {
// THE FIRST CHECK OF THE SLIDER
if (myKnob.transform.localposition.x > 5f) {
// do some stuff cuz the knob x pos is greater than 5 now

// AND NOW PRETEND THERE A BUNCH OF OTHER LOGIC CHECKS...
// } else if { (etc)
// } else if { (etc)
// } else if { (etc)

// AND PRETEND THE USER SLID THE KNOB REALLY FAST...
// Do you see the problem?

// PROBLEM!!!!
// BY THIS POINT, the knob position may very likely have changed significantly from
// where it was when we checked at the top.
} else if (myKnob.transform.localposition.x < 3f) { // do some stuff cuz the knob x pos is less than 3 now } }

This can quickly lead to whacky results.

The proper way to do it, of course, is to STORE the knob position immediately as a constant... and use the same constant to check against all the logic.

Like so:


void CheckSliderPos() {

// STORE THE POSITION so we can compare against a CONSTANT position
float knobXPos = myKnob.transform.localposition.x;

// THEN check your logic and you'll be fine.
if (knobXPos > 5f) {
// etc
} else if (knobXPos < 3f) { // etc } }

This is such an easy and basic mistake, but I find myself making it all the time. A great example, I think, of how there are little tricky pitfalls in coding that can surprise you if you're not carefully thinking everything through.