I wrote a simple snake using p5js library.
Simply begin with an index.htmlCopyCopy
snake fish and more
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
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);
}
}