Jump to content

PixiJs smooth animation


sabot
 Share

Recommended Posts

Hi all,

I want to put together a little snake game to teach myself PixiJs. What I'm trying to achieve is a smooth gliding effect when moving this block around. (press left/right to start moving).

Unfortunately, I simply cannot get rid of the occasional 'stuttering' when rendering - so I'm starting to think I might be going about it the wrong way. Could somebody tell me what I'm doing wrong here?

see: fiddle 

const graphics = new PIXI.Graphics();
const app = new PIXI.Application();
const movementSpeed = 150;
const stage = app.stage;
const blocks = [];

stage.addChild(graphics);

class Block {
    constructor(position=[0, 0], direction=[0, 0]) {
        this.width = 20;
        this.position = position;
        this.prevPosition = position;
        this.direction = direction;
    }

    render (alpha = 0) {

        let offsetX = (this.position[0] * alpha) + (this.prevPosition[0] * (1 - alpha));
        let offsetY = (this.position[1] * alpha) + (this.prevPosition[1] * (1 - alpha));

        graphics.drawRect(offsetX, offsetY, this.width, this.width);
    }

    update (delta = 0) {
        this.prevPosition = this.position;
        this.position = this.calculateNewPosition(delta);
    }

    calculateNewPosition (delta) {
        let movementOffset = movementSpeed * (delta / 1000);

        return [
            this.position[0] + (movementOffset * this.direction[0]),
            this.position[1] + (movementOffset * this.direction[1])
        ];
    }
};

function render (delta) {
    graphics.clear();
    graphics.beginFill(0xFF0000);
    blocks.forEach((block) => block.render(delta));
    graphics.endFill();
};

function update (delta) {
    blocks.forEach((block) => block.update(delta));
};

function changeDirection (direction) {
    blocks.forEach((block) => { block.direction = direction });
}


const fps = 30;                     // Frames per second
const frameDuration = 1000 / fps;   // Maximum time per frame.
const maxFrameDelta = 1000;         // Upper limit on how long the frame takes to render.
let previousTimestamp = 0;          // Last timestamp.
let accumulator = 0;                // Remainder of time that we could not simulate in our fixed-step physics. (used to smooth out temporal aliasing)

function gameLoop(timestamp = 0) {
  requestAnimationFrame(gameLoop);

  //Calculate the time that has elapsed since the last frame
  let frameDelta = timestamp - previousTimestamp;

  //Optionally correct any unexpected huge gaps in the elapsed time
  if (frameDelta > maxFrameDelta) elapsed = maxFrameDelta;

  //Add the elapsed time to the accumulator
  accumulator += frameDelta;

  //Update the physics simulation in discrete chunks, and keep whatever remainder is left in the accumulator.
  while (accumulator >= frameDuration) {

    //Update the physics simulation
    update(frameDuration);

    accumulator -= frameDuration;
  }

  // After performing the physics steps, we might have some remaining time in the accumulator.
  // This causes 'stuttering' (temporal aliasing), so we interpolate where the block should be rendered.
  // That is: we don't draw the block exactly where the physics simulation has placed it, but where it should be to get the smoothest animation.
  // (Thank you Glenn Fiedler!)
  let renderOffset = accumulator / frameDuration;
  render(renderOffset);

  previousTimestamp = timestamp;
}


function initStage() {
    document.body.appendChild(app.view);

    blocks.push(new Block());


    // Keyboard input.
    document.addEventListener('keydown', function(event) {
        if(event.keyCode == 37) {
            changeDirection([-1, 0]);
        }
        else if(event.keyCode == 39) {
            changeDirection([1, 0]);
        }
    });


    gameLoop();
};

document.addEventListener("DOMContentLoaded", initStage);


 

 

Link to comment
Share on other sites

6 hours ago, Fritzy said:

This seems way more complicated than it needs  to be. A simple delta (although max delta is nice) should be enough. I would also recommend just drawing the block once, and then moving the position of the graphics object around.

Thanks for the tip, I did notice that actually drawing to the graphics object seems to be done only during initialization in most examples. I was afraid that this might lead to me making a graphics object for every single segment of the snake, which seems to be something to avoid. Can you tell me if this example stutters on your machine? I'm on a ubuntu laptop, so I was thinking maybe it's related. (it's not too bad actually, but it does irritate me)

As for the complexity of the timestep - I was following along with the famous 'fix your timestep' blogpost by Glenn Fiedler, but it's indeed a little complicated for something this simple

Link to comment
Share on other sites

  • 5 years later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...