【100sites #006】Snack

Published on
Currently displaying Chinese version content. This article doesn't have a English version yet. Please stay tuned!

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

Github

Live Demo

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

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

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

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

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

今天的 code:

<!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>
/* @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 } } } }