Nathan Tornquist.com

Burrow

Goals:

While at Purdue I have found it much harder to start and complete projects purely for entertainment, as my weekends are filled with ASIC design and scripting for class. Arcis, a game I released in June 2013, was the product of months of development and I no longer have that sort of luxury. Instead, I have decided to move my focus to smaller projects that are more manageable in a weekend development push.

Burrow is the first of those smaller development pushes. Earlier this semester I had a free weekend, so I spent a Saturday prototyping the game and then the next week and a half or so cleaning up the engine and polishing everything off.

Technical Details:

The hardest thing I’ve had to deal with on Android is the issue of scalability between devices. There are numerous game libraries that easily scale screens, but they tend to be aimed at a development style slightly different than my own, and for personal entertainment I’d rather be able to hold on to my creative paradigm. I actually wrote a complicated library that scaled screen events, calculated letterboxing regions, and really allowed canvas-based games to scale appropriately on multiple screens. It was never really an elegant solution though. The calculations were simple, but not really optimized and I was never really happy with using it as my main method. By a stroke of luck I finally stumbled across a StackOverflow posting that completely met my needs.

The solution provided the following logic:

public void draw(Canvas canvas)
{
    float scaleFactor = Math.min(
      getWidth() / 208.f,
      getHeight() / 160.f
    );
    float finalWidth = 208.f * scaleFactor;
    float finalHeight = 160.f * scaleFactor;
    float leftPadding = (getWidth() - finalWidth) / 2;
    float topPadding =  (getHeight() - finalHeight) / 2;

    int savedState = canvas.save();
    try {
        canvas.clipRect(
          leftPadding,
          topPadding,
          leftPadding + finalWidth,
          topPadding + finalHeight
        );

        canvas.translate(leftPadding, topPadding);
        canvas.scale(scaleFactor, scaleFactor);

        drawBase(canvas);
    } finally {
        canvas.restoreToCount(savedState);
    }
}

While my libraries focused on managing offsets and acting within a single canvas, this solution instead scaled the canvas based on internal methods that I had either overlooked or simply not understood when I started working on a strong general solution. With a new solution in hand, I was able to quickly develop a game that would scale appropriately and appear correctly on any Android phone.

The other key piece to scalability is making sure that the touch events all behave properly. My personal solution to this issue can be seen below:

@Override
public boolean onTouchEvent(MotionEvent event) {
  int action = event.getAction() & MotionEvent.ACTION_MASK;
  int pointerIndex = (
    event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK
  ) >> MotionEvent.ACTION_POINTER_ID_SHIFT;
  int pointerId = event.getPointerId(pointerIndex);

  switch (action) {
    case MotionEvent.ACTION_DOWN:
    case MotionEvent.ACTION_POINTER_DOWN:
      touchDict.put(
        pointerId,
        new Touch(
          event.getX(pointerIndex)/actScreenWidth*CANVAS_WIDTH,
          event.getY(pointerIndex)/actScreenHeight*CANVAS_HEIGHT,
          true,
          true
        )
      );
      break;
    case MotionEvent.ACTION_UP:
    case MotionEvent.ACTION_POINTER_UP:
    case MotionEvent.ACTION_CANCEL:
      touchDict.put(
        pointerId,
        new Touch(
          event.getX(pointerIndex)/actScreenWidth*CANVAS_WIDTH,
          event.getY(pointerIndex)/actScreenHeight*CANVAS_HEIGHT,
          false,
          false
        )
      );
      break;
    case MotionEvent.ACTION_MOVE:
      int pointerCount = event.getPointerCount();
      for(int i = 0; i < pointerCount; ++i)
      {
        pointerIndex = i;
        pointerId = event.getPointerId(pointerIndex);
        boolean state = touchDict.get(pointerId).justHit;
        touchDict.put(
          pointerId,
          new Touch(
            event.getX(pointerIndex)/actScreenWidth*CANVAS_WIDTH,
            event.getY(pointerIndex)/actScreenHeight*CANVAS_HEIGHT,
            true,
            state
          )
        );
      }
      break;
  }
  return true;
}

Concept:

With the technical details out of the way, I was able to put the game together pretty quickly. On this project instead of trying to figure out a complicated game that allowed for advanced strategy, I instead tried to make something as simple as possible. With the recent success of FlappyBird, I actually based my style on some of .GEARS’ other games. The developer is very good at creating interesting but simple games that scale very well.

With the urging to create a bunny based game from my girlfriend, I threw together the concept. You can see some screenshots below:

Arcis has a definite ending point. There is a specific spot in the game that the enemies spawn to fast for any sort of defense to be possible. It isn’t a point many people know how to get to, but that point exists. There are two main reasons most people can’t get that far. The first is that the game logic of Arcis implies that both built towers and the main tower should be used at all times. While playing the game a lot could teach you otherwise, there are intricacies of the game style that are not explained anywhere. My game was too complicated to put in concise instructions and the instructions I did provide did not lead to as much exploration as I could have hoped. This meant that the game ended up keeping users in early levels and due to lack of information severely crippled replay value.

Those two crippling things were the main points I developed away from, and I’ve certainly learned from the mistakes made in that game. While I thought of more advanced features that could be put in Burrow, I tried to follow .GEARS in keeping instructions to a single page of instructions with little to no text. In addition to that, I tried to design the game away from a cutoff point. The birds that you try to avoid in Burrow spawn at the same rate forever. The carrots spawn at a rate that decreases slowly, but should not cause a players loss if they play well. The only thing that changes as the game progresses is the opacity of the clouds. That simply game mechanic makes things slightly more difficult but acts as a feature that can be played around instead of something that you must play against.

Art - Visuals and Audio:

With Burrow I chose the “8-bit” art style simply because it is easy for prototyping. I was working to emulate .GEARS and wanted to make a game that felt simple. Something that required little thought in both development and in use. To continue the feel of the game though, it was necessary to tie in audio as well. Believe it or not, the simple beeping noises made the game significantly more fun.

One of my best friends from high school, Michael Betz, is going to school for composition and has worked with me on some audio projects in the past. He took the sound effect project as a weekend break, as I did with my programming, and added an interesting flair. You can check out some of his more complicated projects here, but everything for Burrow was actually produced on an original Game Boy. Over winter break this year, and sometime before that, he has been modding a Game Boy. This device now has a much cleaner audio output signal, a backlight, and he has a custom cartridge that is used to manually produce output on the device’s sound card. He then uses the device in conjunction with his computer to produce and remaster the sound effects that I’ve used in the game. That’s about as much detail as I can say about the audio, but it is a cool process.

Closing Remarks:

Burrow is a very different turn from the work that I’ve done in the past but it was a really fun project. It was a nice break from classes and it’s always fun to make something and finish it. I had a blast with it, and if you want to check it out, you can read the description on Google Play, or on the Project Page.

Note: Download link only on Google Play

Goodbye for now,

–Nathan