0

I am working on a small mario game.

But I can't figure out how to anime sprites. As per example, I have a running mario.gif file (mario is not runnning in the gif)

Click here for the mario picture.

The picture is 60 x 20 pixels. At the moment, this is my code.

class Character {

    public y_: number;
    public x_: number;
    public nFrames: number = 30;

    constructor(public _x: number, public _y: number) {
        this._x = _x;
        this._y = _y;
    };

 sprite: HTMLImageElement;

    setSpriteUrl(input: string) : void {
        this.sprite = new Image();
        this.sprite.src = input;
    }

drawSprite(): void {
        ctx.save();
        ctx.beginPath();
        ctx.drawImage(this.sprite, 0, 0, 15, 20, this._x, this._y, 20, 20);
            ctx.restore;
      }
}

and after that

var mario = new Character(40, 50);
mario.setSpriteUrl("graphics/mario/small/Running-mario.gif");

the width of the picture is 60 pixels and there are 4 running images of mario. The height of the picture is also 20 pixels.
60/4 = 15.

ctx.drawImage(this.sprite, 0, 0, 15, 20, this._x, this._y, 20, 20);

This would make me think that it I can go from 15 to 30 and it would select the next mario image. Instead it gives me 2 running mario's from the picture.
How does it work? How can select every running stage of mario?


If that is done, should the sprite be animated with a for loop and a timer to do that? To me it doesn't seem like the best practice.. Since I have more sprites to animate then only mario and running.

Romano
  • 117
  • 1
  • 2
  • 12
  • Considering you are trying to draw a gif on canvas I think your question might be a duplicate of this one http://stackoverflow.com/questions/3062229/animated-gif-in-html5-canvas – toskv May 15 '16 at 22:41
  • 1
    In that question the person is asking about his animated gif. My gif file is not animated but just a plain picture (see link). Also the person gets as answer a library. I'm not looking for a library, I want to understand (educational purposes) – Romano May 16 '16 at 00:05
  • thanks for the clarification, I will retract my close vote. :) – toskv May 16 '16 at 13:37

2 Answers2

1

The signature of drawImage is

ctx.drawImage(image, dx, dy)
ctx.drawImage(image, dx, dy, dWidth, dHeight)
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy)
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

(sx, sy) is the start position of the slice in the source image from where to start copying. (sWidth × sHeight) is the size of that slice.

Since your sprite frames are oriented horizontally, you need to increase the value of sx to draw the next frames.

class Character {

    frameWidth: number = 15;
    frameHeight: number = 20;

    constructor(
        public x: number,
        public y: number) { }

    sprite: HTMLImageElement;

    setSpriteUrl(input: string) : void {
        this.sprite = new Image();
        this.sprite.src = input;
    }

    drawSprite(frameIndex: number): void {
        ctx.save();
        ctx.beginPath();
        ctx.drawImage(this.sprite,
            frameIndex * this.frameWidth, 0,   // Start of slice
            this.frameWidth, this.frameHeight, // Size of slice
            this.x, this.y);                   // Destination position
        ctx.restore();
    }
}
Markus Jarderot
  • 86,735
  • 21
  • 136
  • 138
  • The `ctx.save`, `beginPath` and `restore` functions in the function `drawSprite` are not doing anything and are redundent in terms of the function's task. These functions can add considerable overhead, which in certain conditions would be much longer than the call to drawImage. – Blindman67 May 16 '16 at 18:20
  • Thanks ! This helped a lot. I got the animation working. I will post it as final answer. Thanks for the help – Romano Jun 04 '16 at 20:45
0
    class Character {

    frameWidth: number;
    frameHeight: number; 
    tickCount: number;
    ticksPerFrame: number = 1;
    frameIndex: number;
    jump: boolean;

    constructor(public position: Vector, public numberOfFrames : number) {}

    sprite: HTMLImageElement;


    setSpriteUrl(input: string) : void {
        this.sprite = new Image();
        this.sprite.src = input;
    }

    addGravity(): void {

        this.position.y += downForce;
        if (this.position.y >= 415)
            this.position.y = 415;
    }

    drawSprite(): void {

        this.tickCount = this.ticksPerFrame;

        if (this.tickCount >= this.ticksPerFrame) {
            this.tickCount = 0;
            if (this.frameIndex < this.numberOfFrames - 1) {
                this.frameIndex += 1;
            } else {
                this.frameIndex = 0;
            }
        }

        this.frameHeight = this.sprite.height;
        this.frameWidth = this.sprite.width / this.numberOfFrames;

        this.position.setWidth(this.frameWidth);
        this.position.getHeight(this.frameHeight);
        ctx.drawImage(this.sprite,
            this.frameIndex * this.frameWidth, 0,   // Start of slice
            this.frameWidth, this.frameHeight, // Size of slice
            this.position.x, this.position.y, 15, 20);
    }

}

By using sprite this.sprite.height and this.sprite.width I can have the size dynamic. This way I can load any sprite.

And Setting Mario

var mario = new Character(new Vector(40,50), 4);
mario.setSpriteUrl("graphics/mario/small/Standing-mario.gif");
mario.numberOfFrames = 1;

numberOfFrames is in this case only 1. Since the standing mario gif has only 1 picture in it.

But if Mario is running.

    function keyboardInput(event: KeyboardEvent) {


    switch (event.keyCode) {
        case 65: case 37: //a
            mario.setSpriteUrl("graphics/mario/small/Running-mario-left.gif");
            mario.numberOfFrames = 4;
            mario.position.x -= 10;
            break;

        case 38: case 87: //w
            mario.numberOfFrames = 1;
            mario.setSpriteUrl("graphics/mario/small/Jumping-mario.gif");
            if(mario.position.y < 415) {
                return false;
            }
            mario.position.y -= 30;
            break;
        case 39: case 68: //d
            mario.setSpriteUrl("graphics/mario/small/Running-mario.gif");
            mario.numberOfFrames = 4;
            mario.position.x += 10;
            break;
        case 40: case 83: //s
            mario.position.y += 20;
            break;
        case 32: //space
            break;
        default:
            mario.setSpriteUrl("graphics/mario/small/Standing-mario.gif");
            mario.numberOfFrames = 1;
            break;      
    }

}

If Mario is running, another number of frames is used. Running mario has 4 images inside the gif. Making the numberOfFrames 4.

Romano
  • 117
  • 1
  • 2
  • 12