How to make Slither.io with JavaScript: Part 6 - Snake Shadows

8/5/17

This is the sixth part of our tutorial series on creating Slither.io with JavaScript and Phaser! Take a look at Part 1 if you are just starting with this series, or jump back to Part 5 if you need to.

Look at the example and also look at the source code for this part.

Shadow Concepts and Goals

In this part of the series we will be adding gray shadows below snakes. We will want these shadows to be flexible in that they can change colors, so that we can make a shadow light up when the player is pressing the space bar to speed up. Other than that, we simply need the shadow to stay directly below the snake.

Image

Take a look at the asset folder and look at white-shadow.png. Why is it white? Because we can now give its sprite a tint of any color, as we will see shortly. We will add a group of these image sprites below the sections of the snake, then move them with the snake.

Shadow

Now look at shadow.js. First we create the Shadow function:

Shadow = function(game, sections, scale) {
    this.game = game;
    this.sections = sections;
    this.scale = scale;
    this.shadowGroup = this.game.add.group();
    this.shadows = [];
    this.isLightingUp = false;

    this.lightStep = 0;
    this.maxLightStep = 3;

    this.lightUpdateCount = 0;
    this.updateLights = 3;

    //various tints that the shadow could have
    //since the image is white
    this.darkTint = 0xaaaaaa;
    this.lightTintBright = 0xaa3333;
    this.lightTintDim = 0xdd3333;
}

Here we create a shadows Array, initialize some variables to handle color change sequences, and actually set some properties for different shadow tints.

Add Shadows

Next, we need a method to add shadow sprites. Here is the add method in the Shadow prototype:

add: function(x, y) {
    var shadow = this.game.add.sprite(x, y, "shadow");
    shadow.scale.setTo(this.scale);
    shadow.anchor.set(0.5);
    this.shadowGroup.add(shadow);
    this.shadows.push(shadow);
}

We add a shadow sprite at the specified position, then add it to groups. When we look back at the Snake class, you will see that we add one shadow for each snake section.

Update

Now we need an update method, in order to move the shadows below the snake sections:

update: function() {
    var lastPos = null;
    for (var i = 0 ; i < this.sections.length ; i++) {
        var shadow = this.shadows[i];
        var pos = {
            x: this.sections[i].body.x,
            y: this.sections[i].body.y
        };

        //hide the shadow if the previous shadow is in the same position
        if (lastPos && pos.x == lastPos.x && pos.y == lastPos.y) {
            shadow.alpha = 0;
            shadow.naturalAlpha = 0;
        }
        else {
            shadow.alpha = 1;
            shadow.naturalAlpha = 1;
        }
        //place each shadow below a snake section
        shadow.position.x = pos.x;
        shadow.position.y = pos.y;

        lastPos = pos;
    }

    //light up shadow with bright tints
    if (this.isLightingUp) {
        this.lightUpdateCount++;
        if (this.lightUpdateCount >= this.updateLights) {
            this.lightUp();
        }
    }
    //make shadow dark
    else {
        for (var i = 0 ; i < this.shadows.length ; i++) {
            var shadow = this.shadows[i];
            shadow.tint = this.darkTint;
        }
    }
}

The first part of the code moves each shadow to the same position as a snake section. If two shadows are at the same positions, we hide one of them by setting its alpha to zero. This avoids stacking of shadow sprites at the back of the snake as sections are added. Then, we change the shadow sprite tints based on the isLightingUp property.

Lighting Up

Let's see the lightUp method that we call in update:

lightUp: function() {
    this.lightUpdateCount = 0;
    for (var i = 0 ; i < this.shadows.length ; i++) {
        var shadow = this.shadows[i];
        if (shadow.naturalAlpha > 0) {
            //create an alternating effect so shadow is not uniform
            if ((i - this.lightStep) % this.maxLightStep === 0 ) {
                shadow.tint = this.lightTintBright;
            }
            else {
                shadow.tint = this.lightTintDim;
            }
        }
    }
    //use a counter to decide how to alternate shadow tints
    this.lightStep++;
    if (this.lightStep == this.maxLightStep) {
        this.lightStep = 0;
    }
}

This method lights up the shadow with the bright tints. It alternates between dimmer and brighter ones, giving the shadow a flashy look like in the real game.

Scale and Destroy

As with other parts of snakes, shadows need to be scaled and destroyed. Here is our scale method:

setScale: function(scale) {
    this.scale = scale;
    for (var i = 0 ; i < this.shadows.length ; i++) {
        this.shadows[i].scale.setTo(scale);
    }
}

And here is the destroy method:

destroy: function() {
    for (var i = this.shadows.length - 1 ; i >= 0 ; i--) {
        this.shadows[i].destroy();
    }
}

Snake

Let's implement shadows in the Snake class:

this.shadow = new Shadow(this.game, this.sections, this.scale);

In the addSectionAtPosition method of the Snake, we then need to add a shadow sprite, so that there is one for each section:

this.shadow.add(x,y);

Then we need the shadow update method in the Snake update method:

this.shadow.update();

And the shadow destroy method in the Snake destroy method:

this.shadow.destroy();

And with that, shadows have been implemented. In or final part, we will implement food for the snakes and conclude the series.

Until next time,

Loonride

Up Next

Subscribe

  • No spam
  • Update information
  • Free newsletters

Invalid Email

Subscribed

Try Again Later