1
\$\begingroup\$

I've made a 2D platformer game. The map is an array and I also have a separate array for entities such as spinning coins and moving enemies. This array is declared like this:

animationClock: 50,
animations: [
    {x: 6, y: 23, frame: 0, sprites: [3, 4, 5], type: 'enemy', show: true},
    {x: 8, y: 23, frame: 0, sprites: [4, 5, 6, 7], moves: true, movement: {path: 'horizontal', right: true, x1: 8, x2: 10}, speed: 0.03, type: 'enemy', show: true}
] 

So the animation clock is basically how many frames it takes for the sprite to change.

My game clock runs at 90FPS an will run this every frame:

    currentFrame++;     

    if (currentFrame == currentLevel.animationClock){
        currentFrame = 0;

        //If the clock has hit the end of the cycle, go to the next frame on each sprite
        for (var i = 0; i < currentLevel.animations.length; i++){
            if (currentLevel.animations[i].frame != currentLevel.animations[i].sprites.length - 1){
                currentLevel.animations[i].frame ++
            }else{
                currentLevel.animations[i].frame = 0;
            }
        }
    }

    //For every animation object (in this case 2)
    for (var i = 0; i < currentLevel.animations.length; i++){
        var entity = currentLevel.animations[i];

        //Get the tile that the enemy sits
        var tileX = (entity.x * tileSize) - viewport.x;
        var tileY = (entity.y * tileSize) - viewport.y;

        //If an entity is visible (a coin that hasnt been picked up)
        if (entity.show){
            //And if its in the bounds of the viewport
            if (tileX > -tileSize || tileY < -tileSize || tileX < canvas.width || tileY < ctx.height) { 
                //If the entity moves, move it
                if (entity.moves){
                    if (entity.movement.path == 'horizontal'){
                        if (entity.movement.right){
                            entity.x += entity.speed;
                        } else {
                            entity.x -= entity.speed;
                        }       
                        if (entity.x > entity.movement.x1 + Math.abs(entity.movement.x1 - entity.movement.x2)){
                            entity.movement.right = false;
                        } else if (entity.x < entity.movement.x1){
                            entity.movement.right = true;
                        }
                    } else if (entity.movement.path == 'verticle')
                    {
                        if (entity.movement.down){
                            entity.y += entity.speed;
                        } else {
                            entity.y -= entity.speed;
                        }
                        if (entity.y > entity.movement.y1 + Math.abs(entity.movement.y1 - entity.movement.y2)){
                            entity.movement.down = false;
                        } else if (entity.y < entity.movement.y1){
                            entity.movement.down = true;
                        }
                    }
                }

                //Draw the frame to the canvas
                if (entity.sprites[entity.frame]){      
                    ctx.drawImage(currentLevel.spriteSheet, entity.sprites[currentLevel.animations[i].frame] * tileSize, 0, tileSize, tileSize, tileX, tileY, tileSize, tileSize);
                }

                //Collision detection
                if (playerHitEntity(player.position.x, player.position.y, entity.x * tileSize, entity.y * tileSize)){
                    //Different types of entities
                    if (entity.type == 'enemy'){
                        lives--;
                        player.position.x = currentLevel.spawnPlayer.x * tileSize;
                        player.position.y = currentLevel.spawnPlayer.y * tileSize;
                    } else if (entity.type == 'bonus'){
                        entity.show = false;
                        points++;
                    } else if (entity.type == 'win'){
                        level++;
                        //If theres a next level
                        if (levelList[level])
                        {
                            currentLevel = levelList[level];
                            tileSize = currentLevel.tileSize;
                            player.position.x = currentLevel.spawnPlayer.x * tileSize;
                            player.position.y = currentLevel.spawnPlayer.y * tileSize;
                            viewport.x = player.position.x;
                            viewport.y = player.position.y;
                        } else {
                            gameState = "finished";
                        }
                    }
                }
            }
        }
    }

It doesn't have the worst lag but its definitely a noticeable difference and I don't think I should even notice it especially when I only have a few animations. Am I doing something wrong?

\$\endgroup\$
  • \$\begingroup\$ 90fps is the problem. Browsers present at a rate of 60fps, rendering 90 frames means the 30 of 90 frames will not be seen or 60 of 90 will only partly be seen or somewhere between those. You have not provided the important part. How you do the timing. but i am guessing you are not using requestAnimationFrame which is a must for any type of javascript controlled animation. It ensures that changes made in the callback to the DOM are only moved to the display during the next Vsync. If your code is rendering in under 16.667ms you will get a max rate of 60fps (you can not go higher than that) \$\endgroup\$ – Blindman67 Oct 1 '17 at 0:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.