Animating sprites using TypeScript

I've been tackling the development of a small Mario game lately.

However, I'm facing some difficulty when it comes to animating sprites. For instance, I have a mario.gif file featuring running Mario (although he's not actually running in the gif).

Check out the Mario image here.

The image size is 60 x 20 pixels. Here's my current code snippet:

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;
      }
}

Then follows this part:

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

The picture width is 60 pixels with 4 running images of Mario. The height remains 20 pixels.
Hence, 60/4 = 15.

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

This makes me believe that I could advance from 15 to 30 to select the next stage of Mario running. However, instead of that, it displays 2 running Marios from the same image.
How does this functionality work? How can every running phase of Mario be selected?


Once that's resolved, should the sprite be animated using a for loop and timer? It doesn't seem like the most optimal approach to me, especially considering there are more sprites than just Mario's running animation.

Answer №1

The definition of drawImage function is as follows:

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)

In the above context, (sx, sy) represent the starting position of the slice within the source image to begin copying from. The dimensions of this slice are given by (sWidth x sHeight).

Given that the sprite frames are arranged horizontally, the value of sx needs to be incremented in order to draw subsequent 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,   // Beginning of slice
            this.frameWidth, this.frameHeight, // Dimensions of slice
            this.x, this.y);                   // Destination coordinates
        ctx.restore();
    }
}

Answer №2

    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);
    }

}

Utilizing the sprite's this.sprite.height and this.sprite.width allows for dynamic sizing. This flexibility enables the loading of various sprites.

Example with Mario standing:

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

In this instance, numberOfFrames is set to 1 since the standing Mario gif consists of only one image.

However, when Mario is in motion:

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;      
}

}

For running Mario, a different number of frames are utilized to accommodate the four images within the running Mario gif.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Manipulate Attributes in Javascript

Having an issue here - I'm trying to use JavaScript to set some attributes. for(i=0;i<div_navi.childNodes.length;i++){ if(div_navi.childNodes[i].nodeName =="SPAN"){ div_navi.childNodes[i].setAttribute("onclick","g ...

js issue with passing form data to use with PHP mail function

As a novice, I am diving into the world of HTML webpage creation. Utilizing a free online template, my current project involves developing a Contact Page that triggers a php script to send an email with the captured fields. While I've successfully man ...

Choose a specific element using jQuery based on a class that is shared by several elements

I am looking to target an element with the class 'submenu-expand' and apply an additional class to it. $('.menu-item').on('click',function(){ $('.submenu-expand').toggleClass('expanded') }) While this cod ...

Is Ajax still a popular choice for developing web applications?

Currently, I am developing a comprehensive ajax-based web application and have been investigating various frameworks for SEO optimization and history tracking. During my research, I came across an interesting framework at Although it seems promising, I no ...

What is the best way to start data in an Angular service?

I'm currently navigating my way through building my first Angular application. One of the services I am using needs to be initialized with a schema defined in its constant block, but the schema/configuration is not yet finalized. Therefore, I am perfo ...

Looking for a fully customizable event and booking calendar?

Currently, I am searching for a unique event and booking calendar that provides the ability to customize each cell or day with our desired content. While most calendars only allow for inputting events as text, we require a solution that enables us to add ...

Using an external script to modify or call a Vue.js method

My Vue app is constructed using Webpack and includes a few basic computed properties, such as calculating the sum amount from input values. However, I now require the capability to replace the summation function with one stored in a separate file that is n ...

What steps can I take to resolve the CSP errors I am experiencing?

I am currently working with NextJs@12 and I am attempting to set up CSP for my application. Unfortunately, I keep encountering errors in my console and I cannot figure out where I am going wrong. Below is the current policy that I have in my next.config fi ...

When the page loads, a JavaScript function is triggered

My switchDiv function in Javascript is being unexpectedly called when the page loads. It goes through each case in the switch statement, except for the default case. Does anyone know how to solve this issue? $(document).ready(function() { $("#be-button" ...

I'm perplexed as to why my array remains empty despite assigning a value to it in my controller. (Just to clarify, I am working with AngularJS, not Angular)

I spent a whole day debugging this issue without any luck. Issue: this.gridOptions.data = this.allTemplatesFromClassificationRepo ; **this.allTemplatesFromClassificationRepo ** remains an empty array. I have already called the activate() function to assig ...

Ajax handling all tasks except for adding HTML elements

Having an issue with my basic "Load More on Scroll" AJAX function. The console is showing that the HTML is being sent back from the request, but for some reason, nothing is being rendered on the page. I must be missing something really simple here. $(wi ...

The Ajax request is not being triggered when using a different form submit method

Being a tech enthusiast, I have developed a handy function: function blur_slide_visit_count(){ $.ajax({ type: 'POST', url: 'add_save_slide_visitor_count.php', async: false, data: { fillvalue: fieldAr ...

Inversify's Http Context consistently remains void of any content

Hello there, I'm in need of assistance with building an API using inversify and inversify-express-utils. Everything seems to be working fine with my controllers and the API so far, except for one issue. When trying to access the httpContext property i ...

Upon completion of a promise in an express middleware and breaking out of a loop, a 404 error is returned

In my efforts to retrieve an array of object (car) from express using database functions in conjunction with the stolenCarDb object, everything seems to be working fine. However, when attempting the following code snippet, it results in a 404 error w ...

Having trouble getting your AngularJS code to work?

Recently, I decided to experiment with AngularJS and started working on a new project. Below is the HTML code I wrote: <div ng-app ng-controller="nameController"> <input type="text" value="Jack" ng-model="fname" /> <input type="tex ...

Efficient configuration for pnpm monorepo with TypeScript, vite, and rollup

Struggling to set up a monorepo using pnpm workspaces with typescript, vite for frontends, and rollup for backend microservices. Here's the current project structure: package.json <== all dependencies reside here tsconfig ...

Is there a way to remove the initial number entered on a calculator's display in order to prevent the second number from being added onto the first one?

I am currently in the process of developing a calculator using HTML, CSS, and JavaScript. However, I have encountered an issue with my code. After a user inputs a number and then clicks on an operator, the operator remains highlighted until the user inputs ...

Error message: Next.js - Unable to access properties of an undefined object (user)

I am currently utilizing Next.js and Next-auth in my current project. Within this project, I am working on creating a Sidebar component that will display a list of items specific to each user. To achieve this, I am using the useSession hook to retrieve t ...

jQuery function used to create an HTML menu

My goal is to dynamically update the HTML file generated by a PHP script when I modify an item in a drop-down menu using jQuery. Here is the code: JQUERY <script type="text/javascript"> $(document).ready(function() { $('#regioni&ap ...

Include a character in a tube using Angular

Hey everyone, I have a pipe that currently returns each word with the first letter uppercase and the rest lowercase. It also removes any non-English characters from the value. I'm trying to figure out how to add the ':' character so it will ...