Monday, March 30, 2015

How Do You Like Them Apples?

Throwing Apples!
More progress on my game entry for the GitHub Game Off 2015! I've been working down the list I made for myself back when I started, and this weekend I was able to add the projectiles that come in from the sides and try to knock you off the platforms as you jump.

What image did I select for these projectiles?

Apples, of course.

Why apples? Well first of all, I had an apple image already. I made it for the February 18th comic. Secondly, both me and my daughter Alex are allergic to apples. They are actually bad for us. So it seemed like a good thing to throw at frogs. Does it have some kind of sinister, hidden meaning which implies that iPhones are dangerous as well? No. Not really. My intention was to have something to represent new requirements that are thrown at you during the development cycle...you know, stuff that knocks you off your schedule. The game is jumping through a Gantt chart. Apples make sense for that, right? No? Maybe I need a better metaphor.

I'm sure that you are wondering how easy it is to add game elements using the Crafty game framework. Good, because I'm going to tell you. It was not very difficult. As I talked about last week, everything you see on the screen in a Crafty game (and some things that you don't see too!) is an Entity, and Entities are created with the properties of one or more Components. I defined my own "Apple" component and then simply added a method to create Apple entities randomly.

This is the Component creation code.

Crafty.c("Apple", {
    init: function () {
        this._dir = (Math.random() < 0.5 ? -1 : 1);
        this.x = ( this._dir > 0 ? -(this._w) : (Crafty.viewport.width + (this._w)));
        this.y = (Crafty.viewport.height / 4) - (Crafty.viewport.y);
        this.z = 9999;
        this.bind("EnterFrame", this._enterframe);
    },
    _enterframe: function () {
        this.x = this._x + (this._dir * 4.5);
        if (this._x < -(this._w*2) || this._x > Crafty.viewport.width + (this._w*2)) {
            this.destroy();
        }
    }
});

And this is the Entity creation code.

Crafty.e("2D, DOM, Image, Tween, Apple")
    .image("assets/images/apple.png");

Here's how it works... Calling Crafty.c(String, Object) creates a new Component with the name given by the first parameter and the properties given in the second parameter. In my code, I made an Apple component that had two properties: a function called init and a function called _enterframe. You can define whatever you want in this object and everything will get copied into any Entity you create using this component, with two exceptions. Crafty has two special-purpose functions that can be included in the object: init and remove. I don't use remove, but I do have init. The special behavior is that the init function will be called whenever this component is added to an Entity (often at creation time, but it can be at any point). My init function sets up some interval variables such as the direction of the apple (from the left or from the right) and its position on the screen. The last thing it does is bind the "EnterFrame" event to the other function, _enterframe. The "EnterFrame" event is fired by Crafty for every frame of the game, meaning pretty much constantly. That's why I use a function bound to that event to update the position of the Apple. They only move horizontally, so the function just updates their x-value until they move off the opposite side of the screen - at which point they are destroyed.

One thing that troubles me a little with this setup is that I know that I will be changing the x-value of each apple by 4.5 pixels each frame, but I don't actually know what that really means in terms of speed. I know from my past work with JavaScript game engines that while 60 frames per second is the goal of functions like window.requestAnimationFrame(callback), stuff happens. By specifying movement per frame instead of movement per second, the game will slow down instead of getting "choppy" if the browser can't keep up for whatever reason. I might change this behavior, but it seems to be a common approach in Crafty. Perhaps it's accounted for in some other way that I haven't realized yet.

By the way, I don't typically call my "private" methods names that start with an underscore, but I am doing it in this game simply because the original author did it. I tend to continue things like naming and code style when I work with other people's code...even though in this case I'm not planning on submitting a pull request back to Octocat Jump.

That's all I'm doing for today. I still want to make more changes, but I have to do things in small steps. I still have comics to write, you know. And remember, you can play the latest version of the game at http://caseyleonard.com/ggo15.

Amphibian.com comic for 30 March 2015