-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsnake.js
164 lines (141 loc) · 5.94 KB
/
snake.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
class Snake {
constructor(xOffset, yOffset, size) {
this.xOffset = xOffset;
this.yOffset = yOffset;
this.size = size;
this.setInitialState();
}
setInitialState() {
this.total = 1; // 1 counting the head
this.tail = [];
this.head = { x: this.xOffset, y: this.yOffset };
this.xSpeed = this.size;
this.ySpeed = 0;
this.directionsToChange = [];
this.tailsToAdd = 0;
}
eat(food) {
let distanceToFood = p5i.dist(this.head.x, this.head.y, food.x, food.y);
if (distanceToFood < 1) {
this.increaseTailSize();
return true;
} else {
return false;
}
}
collides(x, y, ignoreHead = false) {
let collides = false;
// Check head collision
if (!ignoreHead) {
const distanceToDeath = p5i.dist(x, y, this.head.x, this.head.y);
if (distanceToDeath < 1) {
collides = true;
}
}
if (!collides) {
// Check tail collision
for (let i = 0; i < this.tail.length - 1; i++) {
let tail = this.tail[i];
const distanceToDeath = p5i.dist(x, y, tail.x, tail.y);
if (distanceToDeath < 1) {
collides = true;
break;
}
}
}
return collides;
}
dieOnCollision() {
let collides = false;
// Check game boundaries collision
if ((this.head.x < this.xOffset || this.head.x >= p5i.width + this.xOffset) ||
(this.head.y < this.yOffset || this.head.y >= p5i.height + this.yOffset)) {
collides = true;
}
// If the snake head collides with his body (ignoring head) or the game boundaries, reset snake position, size, and direction
if (!collides) {
collides = this.collides(this.head.x, this.head.y, true);
}
if (collides) this.setInitialState();
return collides;
}
addTail() {
// Adds tail increase to buffer
this.tailsToAdd++;
}
increaseTailSize() {
let newTail = { x: this.head.x, y: this.head.y };
this.total++;
this.tail.unshift(newTail);
this.tailsToAdd--;
}
addDirectionChange(x, y) {
// Adds direction change to buffer
var predicate = (this.directionsToChange.length > 0 ? this.directionsToChange[this.directionsToChange.length - 1] : { x: this.xSpeed, y: this.ySpeed });
if (x != predicate.x * -1 || y != predicate.y * -1) {
this.directionsToChange.unshift({ x, y });
}
}
changeDirection() {
let direction = this.directionsToChange.pop();
this.xSpeed = direction.x;
this.ySpeed = direction.y;
}
update() {
// Run buffered actions
if (this.tailsToAdd > 0) this.increaseTailSize();
if (this.directionsToChange.length > 0) this.changeDirection();
this.move();
}
move() {
let headBefore = { x: this.head.x, y: this.head.y };
// Move snake head by the current direction
this.head.x = this.head.x + this.xSpeed;
this.head.y = this.head.y + this.ySpeed;
// Move every tail piece to the next tail position and set the last to the previous head position
if (this.tail.length > 0) {
for (let i = 0; i < this.tail.length - 1; i++) {
this.tail[i] = this.tail[i + 1];
}
this.tail[this.tail.length - 1] = headBefore;
}
}
render() {
const from = p5i.color(p5i.colorFromSelector('.color-snake-head'));
const to = p5i.color(p5i.colorFromSelector('.color-snake-tail'));
// Draw head
p5i.noStroke();
p5i.fill(from);
if (this.xSpeed > 0) {
p5i.arc(this.head.x, this.head.y + (this.size / 2), this.size, this.size, p5i.PI + p5i.HALF_PI, p5i.HALF_PI);
} else if (this.xSpeed < 0) {
p5i.arc(this.head.x + this.size, this.head.y + (this.size / 2), this.size, this.size, p5i.HALF_PI, p5i.PI + p5i.HALF_PI);
} else if (this.ySpeed > 0) {
p5i.arc(this.head.x + (this.size / 2), this.head.y, this.size, this.size, p5i.TWO_PI, p5i.PI);
} else if (this.ySpeed < 0) {
p5i.arc(this.head.x + (this.size / 2), this.head.y + this.size, this.size, this.size, p5i.PI, p5i.TWO_PI);
}
// Draw tail
for (let i = 0; i < this.tail.length; i++) {
p5i.fill(p5i.lerpColor(to, from, (1 * i) / this.tail.length));
if (i == 0) {
let tailXSpeed = (this.tail.length >= 2 ? (this.tail[1].x - this.tail[i].x) : this.xSpeed);
let tailYSpeed = (this.tail.length >= 2 ? (this.tail[1].y - this.tail[i].y) : this.ySpeed);
if (tailXSpeed > 0) {
p5i.triangle(this.tail[i].x + this.size, this.tail[i].y, this.tail[i].x + this.size, this.tail[i].y + this.size, this.tail[i].x, this.tail[i].y + (this.size / 2));
} else if (tailXSpeed < 0) {
p5i.triangle(this.tail[i].x, this.tail[i].y, this.tail[i].x, this.tail[i].y + this.size, this.tail[i].x + this.size, this.tail[i].y + (this.size / 2));
} else if (tailYSpeed > 0) {
p5i.triangle(this.tail[i].x, this.tail[i].y + this.size, this.tail[i].x + this.size, this.tail[i].y + this.size, this.tail[i].x + (this.size / 2), this.tail[i].y);
} else if (tailYSpeed < 0) {
p5i.triangle(this.tail[i].x, this.tail[i].y, this.tail[i].x + this.size, this.tail[i].y, this.tail[i].x + (this.size / 2), this.tail[i].y + this.size);
} else {
p5i.rect(this.tail[i].x, this.tail[i].y, this.size, this.size);
}
} else {
p5i.rect(this.tail[i].x, this.tail[i].y, this.size, this.size);
}
}
}
}
module.exports = Snake;