I draw my snake in P5js :)

I wrote a simple snake using p5js library.

Simply begin with an index.htmlCopyCopy

				
					<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>snake fish and more</title>
</head>
<body data-rsssl=1>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>
    <script src="segment.js"></script>   
    <script src="snake.js"></script>
    <script src="sketch.js"></script>
</body>
</html>
				
			

Then, create a sketch.js and create a snake object.CopyCopy

				
					let snake;
function setup(){
    // create a canvas of your favorite size width and height
    createCanvas(600, 400);
    // start point where the snake be
    let point = new p5.Vector(300, 200);
    // create a snake
    snake = new Snake(point.x, point.y, 15, 20);    
}

function draw(){
    background(51);
    // make the snake follow the mouse cursor
    snake.follow(mouseX, mouseY);
   // draw it
    snake.draw();    
}
				
			

Now, define the snake.jsCopyCopy

				
					class Snake {
    constructor(x, y, nSegments, segmentLength) {
        this.x = x;
        this.y = y;
        this.head = new Segment(createVector(x, y), segmentLength, 0);
        this.segmentLength = segmentLength;

        let current = this.head;
        for (let i = 0; i < nSegments; i++) {
            let next = new Segment(current.p2, segmentLength, 0);
            current.child = next;
            current = next;
        }
    }

    follow(tx, ty) {
        this.head.follow(tx, ty);
    }

    drawHead() {
        strokeWeight(1);
        stroke("#000");

        push();
        fill("#fff");
        translate(this.head.p1.x, this.head.p1.y);
        rotate(this.head.angle);
        ellipse(0, 0, this.segmentLength*1.5, this.segmentLength);

        // eyes                
        fill("#fff");
        ellipse(this.segmentLength / 4, -this.segmentLength / 4, 5, 5);
        ellipse(this.segmentLength / 4, this.segmentLength / 4, 5, 5);
        // pupils
        fill("#000");
        ellipse(this.segmentLength / 4, -this.segmentLength / 4, 3, 3);
        ellipse(this.segmentLength / 4, this.segmentLength / 4, 3, 3);        

        pop();
    }

    drawSegment(segment, color) {
        strokeWeight(10);
        stroke(color);
        line(segment.p1.x, segment.p1.y, segment.p2.x, segment.p2.y);
    }

    draw() {
        let next = this.head;
        let i = 0;        
        while (next) {
            let color = i % 2 === 0 ? "#fff" : "#000";
            this.drawSegment(next, color);
            next = next.child;
            i++;
        }

        this.drawHead();
    }
}
				
			

Now, define the snake.jsCopyCopy

Lastly, define a “piece” of snake. Basically, a snake is a chain of segments. Each segment has start point P1 and end point P2. All segments are chained together which start point of this will be the end point of the segment right before it.

The main idea of this is the application of kinematic which implemented in the follow function describe below:

      • For each of the segment, execute the follow(target) function

      • Calculate the new segment angle between the target and P2

      • Set P1 at the target position

      • Update P2‘s position with given P1‘s position and the angle

    Illustration of kinematic position update

    CopyCopy

    				
    					class Segment {
        constructor(startPoint, len, angle = 0) {
            this.p1 = startPoint;
            this.p2 = createVector(0, 0);
            this.len = len;
            this.angle = angle;
            this.update();
        }
    
        // kinematic update
        follow(tx, ty) {
            // calculate the new angle
            this.angle = Math.atan2(ty - this.p2.y, tx - this.p2.x);
            // set p1 at the target's position
            this.p1.x = tx;
            this.p1.y = ty;
            // recalculate the p2's position 
            this.update();
            // apply to the child
            if (this.child) {
                this.child.p1 = this.p2;
                this.child.follow(this.p2.x, this.p2.y);
            }
        }
    
        update() {
            let dx = this.len * cos(this.angle);
            let dy = this.len * sin(this.angle);
            this.p2.x = this.p1.x - dx;
            this.p2.y = this.p1.y - dy;
        }
    
        draw() {
            stroke(255);
            line(this.p1.x, this.p1.y, this.p2.x, this.p2.y);
            fill("#f00")
            ellipse(this.p1.x, this.p1.y, 3, 3);
            fill("#0f0")
            ellipse(this.p2.x, this.p2.y, 3, 3);
        }
    }
    				
    			

    Subscribe to SkyGLab

    Scroll to Top