【100sites #006】Snack

Posted by Eason Chang on 2016-02-16

Snack - 貪食蛇,吃掉那些點點吧!

Github

Live Demo

螢幕快照 2016-02-16 下午8.39.27.png

今天比較有閒有空,把之前一直想做的貪食蛇遊戲做出來了,使用Processing.js。

規則如同經典的貪食蛇,目標是不斷的吃點點,讓自己越長越長,但如果撞到自己就輸了。

使用方向鍵來移動,Enter鍵來開始/暫停。

我正在努力學習如何組織自己的程式碼和適當地下註解,非常歡迎指教。

今天的code:

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
<title>Snack</title>
<script src="http://cdn.bootcss.com/processing.js/1.4.16/processing.min.js"></script>
</head>

<body>
<h1>Be a Snack!</h1>
<p>Move - Arrow key.</p>
<p>Start/Pause - Enter(Return)</p>
<canvas id="game" data-processing-sources="snack.pde"></canvas>
</body>
</html>
snack.pde
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/* @pjs globalKeyEvents="true"; */

int NUM_OF_PILES = 26; // how many piles per dimension
int PILE_SIZE = 18; // unit: px
int SPEED = 12; // frame rate
int DEFAULT_SNACK_LENGTH = 4;
int DIRECTION_UP = 1; // direction of the snack (1:up 2:down 3:left 4:right)
int DIRECTION_DOWN = 2;
int DIRECTION_LEFT = 3;
int DIRECTION_RIGHT = 4;
int DEFAULT_SNACK_DIRECTION = DIRECTION_UP;

int direction = DEFAULT_SNACK_DIRECTION; // direction of snack
ArrayList snack = new ArrayList();
Food food = new Food(0, 0); // the only food on the board
boolean isPausing = true; // whether the game is pausing


void setup() {
size(NUM_OF_PILES * PILE_SIZE, NUM_OF_PILES * PILE_SIZE);
ellipseMode(CORNER);
frameRate(SPEED);

resetSnack();
food.resetPosition();

noLoop(); // the default state of the game is pausing
isPausing = true;
}


void resetSnack() {
// clear snack arrarList
for(int i = 0; i < snack.size(); ++i) {
snack.remove(0);
}
direction = DEFAULT_SNACK_DIRECTION; // set direction to default (up)

// set the snack at the center of the board with default snack length
// +i is to set the tail of snack to the south(+Y)
for(int i = 0; i < DEFAULT_SNACK_LENGTH; ++i) {
snack.add(new SnackUnit(NUM_OF_PILES/2, NUM_OF_PILES/2+i));
}
}



void draw() {
background(50);
for(int i = 0; i < snack.size(); ++i) {
snack.get(i).display();
}
food.display();

snackMove();
checkFood();
checkLose();
}


void snackMove() {
int vx = 0; // velocity of the snack at x axis (-1, 0, 1)
int vy = 0; // velocity of the snack at x axis (-1, 0, 1)
// set velocities according to the direction
switch(direction) {
case DIRECTION_UP:
vy = -1;
break;
case DIRECTION_DOWN:
vy = 1;
break;
case DIRECTION_LEFT:
vx = -1;
break;
case DIRECTION_RIGHT:
vx = 1;
break;
default:
break;
}
// move snack body forward
for(int i = snack.size()-1; i > 0; --i) {
snack.get(i).xpos = snack.get(i-1).xpos;
snack.get(i).ypos = snack.get(i-1).ypos;
}
// move snack head forward according to the velocities
snack.get(0).xpos += vx;
snack.get(0).ypos += vy;

// if the snack is in the wall, make it appear on the other side
if(snack.get(0).xpos < 0)
snack.get(0).xpos = NUM_OF_PILES-1;
else if(snack.get(0).xpos > NUM_OF_PILES-1)
snack.get(0).xpos = 0;
else if(snack.get(0).ypos < 0)
snack.get(0).ypos = NUM_OF_PILES-1;
else if(snack.get(0).ypos > NUM_OF_PILES-1)
snack.get(0).ypos = 0;
}


void checkFood() {
if(snack.get(0).isOn(food.xpos, food.ypos)) {
// append a new snack body at the tail
snack.add(new SnackUnit(snack.get(snack.size()-1).xpos, snack.get(snack.size()-1).ypos));
food.resetPosition();
}
}


void checkLose() {
// if the snack hit itself
for(int i = 1; i < snack.size(); ++i) {
if(snack.get(0).isOn(snack.get(i).xpos, snack.get(i).ypos)) {
// the player lose, pause the game
isPausing = true;
noLoop();
}
}
}


void keyPressed() {
switch(keyCode) {
// pressing arrow key to change the direction
case UP:
direction = DIRECTION_UP;
break;
case DOWN:
direction = DIRECTION_DOWN;
break;
case LEFT:
direction = DIRECTION_LEFT;
break;
case RIGHT:
direction = DIRECTION_RIGHT;
break;
// pressing ENTER and RETURN to toggle Pausing state
case ENTER:
case RETURN:
if(isPausing) {
isPausing = false;
loop();
} else {
isPausing = true;
noLoop();
}
break;
default:
break;
}
}


class SnackUnit {
int xpos, ypos;

SnackUnit(int x, int y) {
xpos = x;
ypos = y;
}
void display() {
fill(255);
rect(xpos*PILE_SIZE, ypos*PILE_SIZE, PILE_SIZE, PILE_SIZE);
}
boolean isOn(int x, int y) {
return xpos == x && ypos == y;
}
}


class Food {
int xpos, ypos;

Food(int x, int y) {
xpos = x;
ypos = y;
}
void display() {
fill(255);
// the size of the food is 4px smaller than a pile
ellipse(xpos*PILE_SIZE+2, ypos*PILE_SIZE+2, PILE_SIZE-4, PILE_SIZE-4);
}
void resetPosition() {
xpos = int(random(0, NUM_OF_PILES));
ypos = int(random(0, NUM_OF_PILES));
for(int i = 0; i < snack.size(); ++i) {
if(snack.get(i).isOn(xpos, ypos)) { // if the snack is already on the position of the new food
resetPosition(); // reset food position to another place
}
}
}
}