skip to Main Content

when the snake moves, it looks like it ‘jumps’ rather than moving smoothly. How can I fix this?

Below are some functions I’ve implemented for my Phaser snake game: update, move, setBodyPartTexture, and grow

update(time) {
    if (time >= this.moveTime && this.gameStarted) {
        this.keyLock = false;
        if (this.moveEvents.length > 0) {
            this.direction = this.moveEvents.shift();
        }
        this.move();
        this.moveTime = time + this.speed;
        return true;
    }
    return false;
}

move() {
    let oldHeadPosition = { x: this.snakeHead.x, y: this.snakeHead.y };
    this.directions.unshift(this.direction.clone());
    this.snakeHead.x += this.direction.x * this.bodyPartLength;
    this.snakeHead.y += this.direction.y * this.bodyPartLength;

    if (this.snakeHead.x > this.scene.game.config.width || this.snakeHead.x < 0 || this.snakeHead.y > this.scene.game.config.height || this.snakeHead.y < 0) {
        return;
    }

    for (let i = 1; i < this.body.length; i++) {
        let oldBodyPosition = { x: this.body[i].x, y: this.body[i].y };
        let oldBodyDirection = this.directions[i];
        this.body[i].x = oldHeadPosition.x;
        this.body[i].y = oldHeadPosition.y;
        oldHeadPosition = oldBodyPosition;
        this.setBodyPartTexture(i, oldBodyDirection);
    }
    this.setTailTexture();
    if (this.positions.length > this.body.length * this.bodyPartLength) {
        this.positions.pop();
        this.directions.pop();
    }
    this.moveTime = this.scene.time.now + this.speed;
}

setBodyPartTexture(i, oldBodyDirection) {
    if (!oldBodyDirection.equals(this.directions[i - 1])) {
        let prevDirection = `${this.directions[i - 1].x},${this.directions[i - 1].y}`;
        let currDirection = `${oldBodyDirection.x},${oldBodyDirection.y}`;
        let textureMap = {
            "1,0,0,-1": "bodyUpRight",
            "0,1,-1,0": "bodyUpRight",
            "-1,0,0,1": "bodyRightUp",
            "0,-1,1,0": "bodyRightUp",
            "0,1,1,0": "bodyRightDown",
            "-1,0,0,-1": "bodyRightDown",
            "0,-1,-1,0": "bodyDownRight",
            "1,0,0,1": "bodyDownRight",
        };
        let directionKey = `${prevDirection},${currDirection}`;
        this.body[i].setTexture(textureMap[directionKey]);
    } else {
        if (oldBodyDirection.y != 0) {
            this.body[i].setTexture("bodyVertical");
        } else {
            this.body[i].setTexture("bodyHorizontal");
        }
    }
}

grow() {
    let newPart = this.scene.physics.add.sprite(-1 * this.bodyPartLength, -1 * this.bodyPartLength, "tailRight");
    this.scene.physics.add.collider(this.snakeHead, newPart, this.endGame, null, this.scene.snake);

    this.bodyParts.push(newPart);
    this.body.push(newPart);

    this.eat.play();
    score++;
    scoreNumber.innerHTML = score;
    if (score > highScore) {
        highScore = score;
        document.querySelector("#high-score p").innerHTML = highScore;
        document.getElementById("highScoreNumber").innerHTML = highScore;
    }
}

These are the functions I’m using. However, I’m experiencing an issue: when the snake moves, it appears to ‘jump’ instead of moving smoothly. Can anyone help me solve this issue?

As you can see in this video, I’m experiencing that issue.
https://www.awesomescreenshot.com/video/33408786?key=90beb12f2ca0ed729a0133a2b7ad36a6

I’ve tried to make the snake’s movement smoother, so it moves fluidly instead of ‘jumping.

I’ve tried using deltaTime, velocity, and the Tween method, but I haven’t found a solution.

2

Answers


  1. I believe this is the problem:

    this.snakeHead.x += this.direction.x * this.bodyPartLength;
    this.snakeHead.y += this.direction.y * this.bodyPartLength;
    

    This means the the least amount the snake can move any given game tick is this.bodyPartLength. This means that the movement is jerky and easy to see as the user. It should be possible to render the snake body parts anywhere on the screen, and therefore, you don’t need to move a whole body part.

    To get rid of the jerkiness, the idea is to increase the game tick speed and decrease the amount moved per tick.

    Login or Signup to reply.
  2. Well the issue is that you are moving by setting the next position in bigger increments, I see two ways to solve this issue:

    • speed up the snake, In you game it would have to be this this.speed variable.
    • make the movement increments smaller

    Choppy movement can of course be caused by to demanding source code or a slow/old Device. For the later there is no real solution, but this game should be fine on most hardware.

    Short Demo:
    (ShowCasing some options)

    // equivalent to your "this.speed"
    const speedTime0 = 100;
    const speedTime1 = 50;
    
    class DemoScene extends Phaser.Scene {
        create () {
            this.createDemoTextObject(10, 10, 'Changing time: ');
            
            this.snakeZero = this.add.rectangle( 10, 50, 10, 10, 0xffffff);
            this.snakeOne = this.add.rectangle( 10, 60, 10, 10, 0xff0000);
            
            
            this.createDemoTextObject(10, 75, 'Changing increments: ');
            
            this.snakeTwo = this.add.rectangle( 10, 100, 10, 10, 0xffff00);
            this.snakeThree = this.add.rectangle( 10, 110, 10, 10, 0xff0077);
            
            
            this.createDemoTextObject(10, 130, 'Simply use physics');
            
            this.snakeFour = this.add.rectangle( 10, 170, 10, 10, 0x00ff00);
            this.snakeFour = this.physics.add.existing(this.snakeFour);
            
            this.snakeFour.body.setVelocity(100, 0);
            
            this.moveTime0 = 0;
            this.moveTime1 = 0;
        }
        
        update(time){
            if (time >= this.moveTime0 ) {
                this.snakeZero.x += this.snakeZero.width;
                
                this.snakeTwo.x += this.snakeTwo.width / 5 ;
                this.snakeThree.x += this.snakeTwo.width / 8;
                this.moveTime0 = time + speedTime0;
            } 
            
            if (time >= this.moveTime1 ) {
                this.snakeOne.x += this.snakeOne.width;
                this.moveTime1 = time + speedTime1;
            }
            
            if(this.snakeOne.x > config.width){
              this.snakeOne.x = 0;
            }
            
            if(this.snakeTwo.x > config.width){
              this.snakeTwo.x = 0;
            }
            
             if(this.snakeThree.x > config.width){
              this.snakeThree.x = 0;
            }
            
            if(this.snakeFour.x > config.width){
              this.snakeFour.x = 0;
            }
            
        }
    
        createDemoTextObject(x, y, text){
            this.add.text(x, y, text)
                .setScale(1)
                .setOrigin(0)
                .setStyle({fontStyle: 'bold', fontFamily: 'Arial'});
        }
    }
    
    var config = {
        width: 540,
        height: 180,
        physics: {
            default: 'arcade',
            arcade: { debug: true }
        },
        scene: DemoScene,
    }; 
    
    new Phaser.Game(config);
    
    console.clear();
    document.body.style = 'margin:0;';
    <script src="//cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js"></script>

    But the easy solution is to simply use the physics velocity (this should take care of all the needed smoothness).

    Mini Demo:

    class DemoScene extends Phaser.Scene {
      create() {
        this.createDemoTextObject(10, 10, 'using physics (use arrow keys left/right): ');
        this.snakeFour = this.add.rectangle(10, 170, 10, 10, 0x00ff00);
        this.snakeFour = this.physics.add.existing(this.snakeFour);
        this.snakeFour.body.setVelocity(100, 0);
    
        this.cursors = this.input.keyboard.createCursorKeys();
      }
    
      update(time) {
        if (Phaser.Input.Keyboard.JustDown(this.cursors.left)) {
          // Just used some math to turn snake
          this.snakeFour.body.velocity.rotate(-Math.PI / 2);
        } else if (Phaser.Input.Keyboard.JustDown(this.cursors.right)) {
        // Just used some math to turn snake
          this.snakeFour.body.velocity.rotate(Math.PI / 2);
        }
        
        // Wraps snake so that it doesn't leave the screen
        this.physics.world.wrap(this.snakeFour, 10);
      }
    
      createDemoTextObject(x, y, text) {
        this.add.text(x, y, text).setScale(1).setOrigin(0).setStyle({ fontStyle: 'bold', fontFamily: 'Arial' });
      }
    }
    
    var config = {
      width: 540,
      height: 180,
      physics: {
        default: 'arcade',
        arcade: { debug: true },
      },
      scene: DemoScene,
    };
    
    new Phaser.Game(config);
    
    console.clear();
    document.body.style = 'margin:0;';
    <script src="//cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js"></script>

    btw: it always good to add a MRE to you question. This helps you pinpoint the issue.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search