貪吃蛇是非常經典的小遊戲,大家肯定接觸過,它在當年只能打電話發短信的諾基亞手機中是唯一的娛樂項目了。它的操作非常簡單,很多編程語言都可以來實現它。我們已經學習了Processing中基本圖形的繪製以及鼠標鍵盤的識別響應,本篇我們使用Processing來編程實現貪吃蛇小遊戲。
1. 蛇身的繪製
這應該是貪吃蛇遊戲中最主要的部分了,在程序中使用數組來保存組成蛇身的每個方塊的座標值。
// 保存組成蛇身的每個方格位置
int[] x = new int[snake_length_max];
int[] y = new int[snake_length_max];
當蛇頭位置改變以後,我們從尾部開始遍歷數組,將每個方塊座標位置向後移動,然後將新的蛇頭位置賦值到數組第一元素,然後重新繪製所有方塊。
void draw_snake()
{
//從尾部開始更新蛇身方塊座標
for (int i=snake_length-1; i>0; i--) {
x[i] = x[i-1];
y[i] = y[i-1];
}
// 設置蛇頭新的座標
x[0] = snake_head_x;
y[0] = snake_head_y;
// 設置蛇身填充顏色
fill(#3874F6);
// 開始畫蛇
for (int i=0; i<snake_length; i++) {
rect(x[i], y[i], grid, grid);
}
}
2. 食物的產生
當食物被吃掉以後,我們需要在指定長寬的區域內隨機生成食物的座標數據,這裏使用了**random()**函數,用來生成座標值,這個座標值需要是方塊邊長的整數倍。
void draw_food(int max_width, int max_high)
{
//食物填充顏色
fill(#F71E1E);
//如果食物被吃掉,則隨機生成一個
if (food_eaten)
{
food_x = int(random(0, max_width) / grid) * grid;
food_y = int(random(0, max_high) / grid) * grid;
}
rect(food_x, food_y, grid, grid);
food_eaten = false;
}
3. 方向控制
這裏使用前篇介紹的按鍵識別操作。對方向按鍵進行判斷,然後對應改變蛇的運動方向。
if (snake_direction != 'P'&& keyPressed && key == CODED)
{
switch(keyCode) {
case LEFT:
if (snake_direction != 'R') {
snake_direction = 'L';
}
break;
case RIGHT:
if (snake_direction != 'L') {
snake_direction = 'R';
}
break;
case DOWN:
if (snake_direction != 'U') {
snake_direction = 'D';
}
break;
case UP:
if (snake_direction != 'D') {
snake_direction = 'U';
}
break;
}
在刷新顯示的時候,會根據移動方向的不同,對蛇頭的座標進行改變,當重新對蛇身進行繪製的時候,整個蛇就進行了一次移動。
//移動方向選擇
switch(snake_direction) {
case 'L':
snake_head_x -= grid;
break;
case 'R':
snake_head_x += grid;
break;
case 'D':
snake_head_y += grid;
break;
case 'U':
snake_head_y -= grid;
break;
}
此外,增加了暫停鍵“P”或“p”,以及運行鍵“R”或“r”的判斷。
if (key == 'p' || key == 'P')
{
game_pause++;
if (game_pause%2 == 1)
{
snake_direction_temp = snake_direction;
snake_direction = 'P';
} else {
snake_direction = snake_direction_temp;
}
}
....
if (keyPressed && (key == 'r' || key == 'R'))
{
...
}
值得注意的是,這些對於按鍵的監聽,沒有放在draw() 函數中,而是使用了鍵盤事件函數 keyPressed(),每當按下一個鍵,其中的代碼就會運行一次,使用這種方式監聽按鍵更加方便靈活。
4. 判斷遊戲結束
當蛇頭座標超過顯示區域,即蛇撞牆,或蛇頭位置座標與蛇身其他方塊座標相同,即自己吃了自己,都會導致遊戲結束。
boolean check_snake_die()
{
// 撞牆了
if ( snake_head_x < 0 || snake_head_x >= width || snake_head_y < 0 || snake_head_y >= height) {
show_game_over();
return true;
}
// 自己吃自己
if ( snake_length > 2 ) {
for ( int i=1; i<snake_length; i++ ) {
if ( snake_head_x == x[i] && snake_head_y == y[i] ) {
show_game_over();
return true;
}
}
}
return false;
}
5. 移動速度
程序中使用millis() 函數來獲取自程序開始到當前的時間。每當draw() 中代碼運行一次,我們都重新獲取一次當前時間,然後減掉之前的時間來計算出經過的時間,然後與移動間隔時間進行比較,當大於間隔時間時,說明需要刷新移動蛇身一次,然後重新獲取一次時間,爲後續比較做準備。
time_passed = millis() - time_start; //計算出經過的時間
time_interval = 1000 / speed; //計算移動間隔時間
if (time_passed > time_interval && snake_direction != 'P' && game_start)//遊戲刷新條件
{
...
time_start = millis(); //重新獲取時間
}
6. 吃到食物
當蛇頭座標移動到與食物座標相同時,就代表食物被吃到,這時蛇身長度要加1,重新生成食物。程序中每吃掉5個食物,移動速度就會增加1。
//蛇吃到食物
if (snake_head_x == food_x && snake_head_y == food_y)
{
food_eaten = true; //可重新生成食物
snake_length++;
if ( snake_length%5 == 1) {
speed++;
}
speed = min(20, speed);//控制最大速度
}
7. 實現效果
貪吃蛇的實現還是非常簡單的,當然代碼還有很多需要優化的地方,比如說我們在隨機生成食物座標的時候,需要排除蛇身中方塊的座標,即食物不能直接出現在蛇身中,你可以試着來優化下。
我們還可以直接將代碼導出成exe可執行文件,你也來試一試吧。
關注公衆號「TonyCode」,後臺回覆“snake”,獲取貪吃蛇完整程序。
回覆「1024」獲取1000G學習資料。
個人博客