russellsayshi
russellsayshi

Reputation: 2569

How can I create a running animation on an HTML5 canvas?

I am building a simple 2D game as an attempt to learn canvas. The character can run around a virtual environment, and a variable called yOffset controls his offset from the top of the screen. I also have a global variable called running which sets itself to true or false based on whether or not the character is running (not shown here). My goal is to make the character bob up and down whilst he is running, and all the below code does is spawn lots of setInterval()s. Is this the right way to make my character run, or should I do it another way? If so, how?

$(document).keydown(function(e) {
        if(e.which == 97) {
            running = true;
            run();
        } else if(e.which == 100) {
            running = true;
            run();
        } else if(e.which == 119) {
            running = true;
            run();
        } else if(e.which == 115) {
            running = true;
            run();
        }
      });

(yes, if the character stops running, the running variable does go to false [not shown here] - I've already made sure the running variable works well)

runTimer = 0;
function run() {
    if(runTimer == 0 && running) {
        runTimer = 1;
        yOffset = 80;
        setTimeout(function() {
            yOffset = 120;
        }, 150);
        setTimeout(function() { if (running) { runTimer = 0;run(); } }, 300);
    }
}

If you need more information, the version that I am currently working on is available here.

Upvotes: 0

Views: 786

Answers (1)

GameAlchemist
GameAlchemist

Reputation: 19294

I think you can simplify your code, and in fact you must in the quite probable case where you'd like to add some other characters.

To allow re-use of the animation, it's better to separate what is an animation (== the different steps that your character will go through), and an animation state (== in which step your character is now).

I wrote here some elements of an animation system.
So i define what is an animation step, a whole Animation (which is so far only an array of animation step), and an Animator (which holds the state, one might see it as a 'reader' of an animation).

Once you defined the animation and animators, and started the animators, you just have to call tick(time) to have the animation move on, and offset() to read the offset, which is way simpler than fighting with a bunch of setIntervals.

http://jsfiddle.net/xWwFf/

// --------------------
function AnimationStep(duration, offset) {
    this.duration = duration;
    this.offset = offset;
    // you might add : image index, rotation, ....
}

// --------------------
function Animation(animationSteps) {
    this.steps = animationSteps;  // Array of AnimationStep
}

// define a read-only length property
Object.defineProperty(Animation.prototype, 'length', {
    get: function () {
        return this.steps.length
    }
});

// --------------------
function Animator() {
    this.currentAnimation = null;
    this.step = -1;
    this.running = false;
    this.remainingTime = 0; // remaining time in current step;
}

Animator.prototype.startAnim = function (newAnim, firstStep) {
    this.currentAnimation = newAnim;
    this.step = firstStep || 0;
    this.remainingTime = newAnim.steps[this.step].duration;
    this.running = true;
}

Animator.prototype.tick = function (dt) {
    // do nothing if no animation ongoing.
    if (!this.running) return;
    this.remainingTime -= dt;
    // 'eat' as many frames as required to have a >0 remaining time
    while (this.remainingTime <= 0) {
        this.step++;
        if (this.step == this.currentAnimation.length) this.step = 0;
        this.remainingTime += this.currentAnimation.steps[this.step].duration;
    }
};

Animator.prototype.offset = function () {
    return this.currentAnimation.steps[this.step].offset;
}

// ______________________________
// example

var bounceAnim = [];
bounceAnim.push(new AnimationStep(200, 10));
bounceAnim.push(new AnimationStep(180, 20));
bounceAnim.push(new AnimationStep(150, 30));
bounceAnim.push(new AnimationStep(300, 40));
bounceAnim.push(new AnimationStep(320, 45));
bounceAnim.push(new AnimationStep(200, 40));
bounceAnim.push(new AnimationStep(120, 30));
bounceAnim.push(new AnimationStep(100, 20));

var anim1 = new Animation(bounceAnim);

var animator1 = new Animator();
var animator2 = new Animator();

animator1.startAnim(anim1);
animator2.startAnim(anim1, 3);

// in action :
var ctx = document.getElementById('cv').getContext('2d');

function drawScene() {
    ctx.fillStyle = 'hsl(200,60%, 65%)';
    ctx.fillRect(0, 0, 600, 200);
    ctx.fillStyle = 'hsl(90,60%,75%)';
    ctx.fillRect(0, 200, 600, 200);
    ctx.fillStyle = 'hsl(10,60%,75%)';
    ctx.fillRect(200, 200 + animator1.offset(), 22, 22);
    ctx.fillStyle = 'hsl(40,60%,75%)';
    ctx.fillRect(400, 200 + animator2.offset(), 22, 22);
    animator1.tick(20);
    animator2.tick(20);
}

setInterval(drawScene, 20);

Upvotes: 1

Related Questions