【play.py程式碼】
# 初始位置.
CONST_STARTING_SNAKE_POS_X = 32
CONST_STARTING_SNAKE_POS_Y = 24
16 ~ 18:設定遊戲區二維陣列大小
# 遊戲區大小.
game_area_width = 64
game_area_height = 48
25 ~ 26:設定遊戲區二維陣列
# 遊戲區陣列.
gameAreaArray =[[0]*game_area_height for i in range(game_area_width)]
# 前進方向.
# 0:上 1:下 2:左 3:右.
direction = 1
# 要吃的節點資料.
# 0:x 1:y 2:產生蛇身數量.
eat_data = [32,24,5]
#-----------------------------------------------------------------
# 函數:亂數產生要吃的數字節點.
#-----------------------------------------------------------------
def randomEatData():
r = True
while r:
# 亂數陣列位置.
eat_data[0] = random.randint(2, 61)
eat_data[1] = random.randint(7, 45)
# 亂數產生增加蛇身數量.
eat_data[2] = random.randint(3, 9)
# 陣列位置為空.
if(gameAreaArray[eat_data[0]][eat_data[1]] == 0):
# 設定產生蛇身數量.
gameAreaArray[eat_data[0]][eat_data[1]] = eat_data[2]
# 離開迴圈.
r = False
此函數會在遊戲區域內亂數產生一個3~9的數字,並會判斷產生的位置不能是在蛇身位置上。
76 ~ 85:此函數處理在畫面上秀字
#-----------------------------------------------------------------
# 函數:秀字.
#-----------------------------------------------------------------
def showFont( text, x, y, color, size):
global canvas
if(size==24):
text = font_24.render(text, True, color)
else:
text = font_40.render(text, True, color)
canvas.blit( text, (x,y))
傳入參數說明:
text:要秀的字串
x, y:字串顯示在畫面上的位置
color:字串顯示的顏色
size:字體大小,傳入24為顯示24x24字體大小,傳入非24顯示為40x40字體大小
87 ~ 107:呼叫此函數後會立即重新開始遊戲
#-----------------------------------------------------------------
# 函數:重新開始遊戲.
#-----------------------------------------------------------------
def resetGame():
global game_mode, score,snake_x, snake_y
# 分數.
score = 0
# 開始位置.
snake_x = CONST_STARTING_SNAKE_POS_X
snake_y = CONST_STARTING_SNAKE_POS_Y
# 蛇身位置串列.
for i in range(snake_body_linkedList.size()-3):
snake_body_linkedList.remove_first()
# 清除畫面陣列.
for y in range(game_area_height):
for x in range(game_area_width):
gameAreaArray[x][y] = 0
# 亂數產生要吃的數字節點.
randomEatData()
# 開始遊戲.
game_mode = 10
程式碼主要是在初始分數、初始貪吃蛇位置、將畫面陣列內容清除為0並亂數產生要吃的數字,最後將狀態設定為開始遊戲(game_mode = 10)。
113 ~ 118:初始pygame、設定視窗Title設定視窗大小
# 初始.
pygame.init()
# 顯示Title.
pygame.display.set_caption(u"貪吃蛇")
# 建立畫佈大小.
canvas = pygame.display.set_mode((canvas_width, canvas_height))
120 ~ 121:建物時脈物件
# 時脈.
clock = pygame.time.Clock()
123 ~ 125:建立兩組字體物件以供遊戲使用
# 設定字型.
font_24 = pygame.font.Font("Fonts/Cascadia.ttf", 24)
font_40 = pygame.font.Font("Fonts/Cascadia.ttf", 40)
這邊分別建立了兩組字體大小的物件供程式使用,字體大小分別為24x24跟40x40。
24x24字體使用在顯示蛇身、蛇要吃的數字與除錯訊息
40x40字體使用畫面左上角顯示的分數與”GAME OVER”訊息
127 ~ 128:在畫面上產生要給貪吃蛇吃的數字
# 亂數產生要吃的數字節點.
randomEatData()
130 ~ 309:遊戲主迴圈
#----------------------------------------------------------------
# 主迴圈.
#----------------------------------------------------------------
running = True
while running:
。
。
。
主迴圈內處理貪吃蛇遊戲的所有輸入、邏輯與繪圖程式運算
135 ~ 136:設定主回圈更新率
# 每秒執行fps次
clock.tick(fps)
這邊設定主迴圈每秒更新8次(fps = 8)
138 ~ 176:處理遊戲中所有輸入處理
#-----------------------------------------------------------------
# 判斷輸入.
#-----------------------------------------------------------------
for event in pygame.event.get():
# 離開遊戲.
if event.type == pygame.QUIT:
running = False
# 判斷按下按鈕
if event.type == pygame.KEYDOWN:
# 判斷按下ESC按鈕
if event.key == pygame.K_ESCAPE:
running = False
# 除錯訊息開關.
elif event.key == pygame.K_d:
debug_message = not debug_message
# 10:遊戲開始.
if(game_mode == 10):
#-----------------------------------------------------
# 上.
if event.key == pygame.K_UP:
direction = 0
#-----------------------------------------------------
# 下.
elif event.key == pygame.K_DOWN:
direction = 1
#-----------------------------------------------------
# 左.
elif event.key == pygame.K_LEFT:
direction = 2
#-----------------------------------------------------
# 右.
elif event.key == pygame.K_RIGHT:
direction = 3
# 11:Game Over.
elif (game_mode == 11):
if event.key == pygame.K_RETURN:
resetGame()
- 按下Esc按鈕後會將running設定為 False,這將會讓程式離開主迴圈並關閉遊戲視窗
- 按下D按鈕後會反轉debug_message變數以啟動將除錯訊息顯示在畫面上的功能
- 在遊戲模式下(game_mode= 10)判斷玩家按下上下左右按鈕來控制貪吃蛇行進方向
- 在GAME OVER模式下(game_mode= 11)判斷玩家按下Enter按鈕以重新開始遊戲
178 ~ 230: 這區段在處理遊戲邏輯
#-----------------------------------------------------------------
# 邏輯運算.
#-----------------------------------------------------------------
# 10:遊戲開始.
if(game_mode == 10):
。
。
。
183 ~ 192:處理貪吃蛇吃到數字後的邏輯處理
# 判斷吃到數字節點.
if(gameAreaArray[snake_x][snake_y] >= 1 and gameAreaArray[snake_x]
[snake_y] <= 9):
# 加分數.
score += gameAreaArray[snake_x][snake_y]
# 設定要產生的蛇身體數量.
generate_node = gameAreaArray[snake_x][snake_y]
# 清除產生蛇身體數量節點.
gameAreaArray[snake_x][snake_y] = 0
# 亂數產生要吃的數字節點.
randomEatData()
吃到數字後首先會增加分數,然後增加蛇身並清除畫面上的數字,最後在呼叫randomEatData()函數產生新數字在畫面上。
194 ~ 224: 處理貪吃蛇前進相關邏輯處理
# 蛇前進.
if(gameAreaArray[snake_x][snake_y] == 0):
# 設定蛇身體陣列編號.
gameAreaArray[snake_x][snake_y] = 10
# 將節點加入串列鏈節.
snake_body_linkedList.insert_front([snake_x,snake_y])
# 增加節點.
if(generate_node > 0):
generate_node-=1
else:
# 取得尾節點.
p = snake_body_linkedList.fetch(snake_body_linkedList.size()-1)
# 清除尾節點陣列編號.
gameAreaArray[p[0]][p[1]] = 0
# 刪除尾節點.
snake_body_linkedList.remove_last()
# 控制蛇前進方向.
# 0:上.
if (direction == 0):
snake_y -= 1
# 1:下.
elif (direction == 1):
snake_y += 1
# 2:左.
elif (direction == 2):
snake_x -= 1
# 3:右.
elif (direction == 3):
snake_x += 1
一開始先判斷前進的位置是空(陣列編號為0),確定後將前進的位置設定為蛇身(陣列編號為10)
# 設定蛇身體陣列編號.
gameAreaArray[snake_x][snake_y] = 10
並將這個節點的座標加入蛇身串列鏈節頭的位置
# 將節點加入串列鏈節.
snake_body_linkedList.insert_front([snake_x,snake_y])
接下來從蛇身串列鏈節的尾端取出一個座標節點,並清除這個座標節點的蛇身,然後從蛇身串列鏈節內刪除這個座標節點
# 取得尾節點.
p = snake_body_linkedList.fetch(snake_body_linkedList.size()-1)
# 清除尾節點陣列編號.
gameAreaArray[p[0]][p[1]] = 0
# 刪除尾節點.
snake_body_linkedList.remove_last()
以上動作執行完畢後就可以看到畫面上的貪吃蛇前進了一格,接下來在判斷玩家輸入的方向狀態(direction)繼續往前行進,不斷的執行這段程式碼,就會看到貪吃蛇不停在畫面上行走的動畫效果。
225 ~ 230:判斷到貪吃蛇前進的位置不為0
# 失敗.
else:
# 清除產生節點數.
generate_node = 0
# 11:GameOver.
game_mode = 11
貪吃蛇前進的位置如果不為0表示撞到牆壁或自己了,這時要將遊戲狀態設定為GameOver(game_mode = 11)。
232 ~ 309:繪製外框
#-----------------------------------------------------------------
# 繪製畫面.
#-----------------------------------------------------------------
# 清除畫面.
canvas.fill(darkBlock)
# 外框.
for x in range(game_area_width):
if(gameAreaArray[x][3]==0):
gameAreaArray[x][3] = 10
if(gameAreaArray[x][5]==0):
gameAreaArray[x][5] = 10
if(gameAreaArray[x][game_area_height-1]==0):
gameAreaArray[x][game_area_height-1] = 10
for y in range(5,game_area_height):
if(gameAreaArray[0][y]==0):
gameAreaArray[0][y] = 10
if(gameAreaArray[game_area_width-1][y]==0):
gameAreaArray[game_area_width-1][y] = 10
。
。
。
執行以上程式碼後會在畫面上畫出以下結果
252 ~ 253:在畫面左上角顯示得分
# 顯示分數.
showFont( str(score), 14, 0, darkGreen, 40)
255 ~ 302:繪製遊戲區
# 繪製遊戲區.
ix = 15
iy = 2
for y in range(game_area_height):
for x in range(game_area_width):
# 顯示數字-1.
if(gameAreaArray[x][y]==1):
showFont( u"1", ix, iy, darkGreen, 24)
# 顯示數字-2.
elif(gameAreaArray[x][y]==2):
showFont( u"2", ix, iy, darkGreen, 24)
# 顯示數字-3.
elif(gameAreaArray[x][y]==3):
showFont( u"3", ix, iy, darkGreen, 24)
# 顯示數字-4.
elif(gameAreaArray[x][y]==4):
showFont( u"4", ix, iy, darkGreen, 24)
# 顯示數字-5.
elif(gameAreaArray[x][y]==5):
showFont( u"5", ix, iy, darkGreen, 24)
# 顯示數字-6.
elif(gameAreaArray[x][y]==6):
showFont( u"6", ix, iy, darkGreen, 24)
# 顯示數字-7.
elif(gameAreaArray[x][y]==7):
showFont( u"7", ix, iy, darkGreen, 24)
# 顯示數字-8.
elif(gameAreaArray[x][y]==8):
showFont( u"8", ix, iy, darkGreen, 24)
# 顯示數字-9.
elif(gameAreaArray[x][y]==9):
showFont( u"9", ix, iy, darkGreen, 24)
# 方塊.
elif(gameAreaArray[x][y]==10):
showFont( u"⬛", ix, iy, darkGreen, 24)
# 空.
elif(gameAreaArray[x][y]==11):
showFont( u"⠀", ix, iy, darkGreen, 24)
# 除錯.
if(debug_message):
if(gameAreaArray[x][y]!=0):
# 顯示除錯訊息.
showFont( str(gameAreaArray[x][y]), ix, iy, (255, 0, 0), 24)
# 顯示FPS.
showFont( u"FPS:" + str(int(clock.get_fps())), 8, 2, (255, 0, 0), 24)
ix+=12
ix = 15
iy+=12
使用兩個for迴圈尋歷所有遊戲區陣列(gameAreaArray)的內容,並依照陣列位置將數字或方塊繪製到畫面上。
同時也會判斷如果除錯變數(debug_message)開啟,就在畫面上顯示除錯訊息。
304 ~ 306:顯示GameOver
# 顯示 GameOver.
if(game_mode == 11):
showFont( u"GAME OVER", 300, 280, darkGreen, 40)
在GameOver模式下(game_mode = 11)在畫面中間顯示GAME OVER字串
308 ~ 309:這邊會依照我們設定的FPS(clock.tick(fps))更新畫面
# 更新畫面.
pygame.display.update()
311 ~ 313:結束程式
# 離開遊戲.
pygame.quit()
quit()
在按下Esc按鈕後離開遊戲主迴圈就會進入這邊執行結束程式的動作
關於串列鏈節(doublyLinkedList.py)
本次使用到網路大神寫的串列鏈節模組,使用方法建議大家可以直接連到大神的教學頁面,裡面有這個模組的詳細解說。。
執行遊戲 - 請在命令列下輸入python play.py 以執行遊戲
GitHub下載原始碼
後記
呼~~終於又完成了一篇,感覺整個腦袋都快被榨乾了,寫遊戲一直是筆者的休閒興趣之一,所以一有空就會動動手寫一些小遊戲來玩,順道練練程式功力,接下來也會繼續保持這個習慣,下一款小遊戲教學也正在構思中,希望不會讓大家等太久。