I draw my snake in P5js :)
I wrote a simple snake using p5js library.
Demo
The code
Simply begin with an index.html
<!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>
<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.
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.js
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();
}
}
The main algorithm
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
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);
}
}
done!
Subscribe to my newsletter
Read articles from Legos Light directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by