### ex3-11 ### import tkinter import tkinter.messagebox idx = 0 # 初始化玩家狀態 tmr = 0 # 初始化模擬時間 stage = 1 # 管理關卡數 ix = 0 # 角色的 x 軸起始位置 iy = 0 # 角色的 y 軸起始位置 key = '' # 存放鍵值的變數 def key_down(e): # 定義按下按鍵時的函式 global key # 將 key 宣告為全域變數 key = e.keysym # 將按鍵字串名稱帶入 key def key_up(e): # 定義放開按鍵時的函式 global key # 將 key 宣告為全域變數 key = '' # 將空白字串帶入 key maze = [[], [], [], [], [], [], [], []] # 地圖的二維 list def stage_data(): # 設定 5 關地圖的函式 global ix, iy # 將 ix, iy 設為全域變數 global maze # 將 maze 設為全域變數 if stage == 1: # 設定第 1 關的地圖與角色位置 ix = 1 iy = 1 maze = [ [9, 9, 9, 9, 9, 9, 9, 9, 9, 9], [9, 0, 9, 0, 0, 0, 9, 0, 0, 9], [9, 0, 9, 0, 9, 0, 9, 0, 0, 9], [9, 0, 9, 0, 9, 0, 9, 0, 9, 9], [9, 0, 9, 0, 9, 0, 9, 0, 0, 9], [9, 0, 9, 0, 9, 0, 9, 9, 0, 9], [9, 0, 0, 0, 9, 0, 0, 0, 0, 9], [9, 9, 9, 9, 9, 9, 9, 9, 9, 9] ] if stage == 2: # 設定第 2 關的地圖與角色位置 ix = 8 iy = 6 maze = [ [9, 9, 9, 9, 9, 9, 9, 9, 9, 9], [9, 0, 0, 0, 0, 0, 0, 0, 0, 9], [9, 0, 0, 0, 0, 0, 0, 9, 0, 9], [9, 0, 0, 9, 9, 0, 0, 9, 0, 9], [9, 0, 0, 9, 9, 0, 0, 9, 0, 9], [9, 9, 9, 9, 9, 0, 0, 9, 0, 9], [9, 9, 9, 9, 9, 0, 0, 0, 0, 9], [9, 9, 9, 9, 9, 9, 9, 9, 9, 9] ] if stage == 3: # 設定第 3 關的地圖與角色位置 ix = 3 iy = 3 maze = [ [9, 9, 9, 9, 9, 9, 9, 9, 9, 9], [9, 9, 9, 0, 0, 0, 0, 9, 9, 9], [9, 9, 0, 0, 0, 0, 0, 0, 9, 9], [9, 0, 0, 0, 0, 0, 0, 0, 0, 9], [9, 0, 9, 0, 0, 0, 0, 0, 0, 9], [9, 0, 0, 0, 0, 0, 0, 0, 9, 9], [9, 9, 0, 0, 0, 0, 0, 9, 9, 9], [9, 9, 9, 9, 9, 9, 9, 9, 9, 9] ] if stage == 4: # 設定第 4 關的地圖與角色位置 ix = 4 iy = 3 maze = [ [9, 9, 9, 9, 9, 9, 9, 9, 9, 9], [9, 0, 0, 0, 0, 0, 0, 0, 0, 9], [9, 0, 0, 0, 9, 0, 0, 0, 0, 9], [9, 0, 0, 0, 0, 0, 0, 0, 0, 9], [9, 0, 0, 9, 0, 0, 0, 9, 0, 9], [9, 0, 0, 0, 0, 0, 0, 9, 0, 9], [9, 0, 0, 0, 0, 0, 0, 0, 0, 9], [9, 9, 9, 9, 9, 9, 9, 9, 9, 9] ] if stage == 5: # 設定第 5 關的地圖與角色位置 ix = 1 iy = 6 maze = [ [9, 9, 9, 9, 9, 9, 9, 9, 9, 9], [9, 0, 0, 0, 0, 0, 0, 0, 0, 9], [9, 0, 9, 0, 0, 0, 0, 0, 0, 9], [9, 0, 0, 0, 0, 0, 9, 9, 0, 9], [9, 0, 0, 0, 0, 9, 9, 9, 0, 9], [9, 0, 0, 9, 0, 0, 0, 0, 0, 9], [9, 0, 0, 0, 0, 0, 0, 0, 0, 9], [9, 9, 9, 9, 9, 9, 9, 9, 9, 9] ] maze[iy][ix] = 1 # 將目前關卡的角色 (筆) 起始位置設為 1 (代表有走過) def draw_bg(): # 繪製出關卡的道路 (0) 與牆壁 (9) for y in range(8): # 走訪 y 軸 for x in range(10): # 走訪 x 軸 gx = 80 * x # 設定每一格的 x 位置與大小為 80 像素 gy = 80 * y # 設定每一格的 y 位置與大小為 80 像素 if maze[y][x] == 0: # 畫出道路的格子 cvs.create_rectangle(gx, gy, gx + 80, gy + 80, fill = 'white', width = 0, tag = 'BG') if maze[y][x] == 9: # 置入牆壁圖片的格子 cvs.create_image(gx + 40, gy + 40, image = wall, tag = 'BG') cvs.create_text(120, 40, text = 'STAGE ' + str(stage), fill = 'white', font = ('Times New Roman', 30, 'bold'), tag = 'BG') gx = 80 * ix # 設定目前的筆的 x 軸位置 gy = 80 * iy # 設定目前的筆的 y 軸位置 cvs.create_rectangle(gx, gy, gx + 80, gy + 80, fill = 'pink', width = 0, tag = 'BG') # 將筆的位置塗上粉紅色 cvs.create_image(gx + 60, gy + 20, image = pen, tag = 'PEN') # 置入筆圖片與中心點 def erase_bg(): # 在畫布上移除 'BG' 與 'PEN' cvs.delete('BG') # 移除 'PEN' (筆圖片的 tag) cvs.delete('PEN') # 移除 'BG' (走過粉紅色路的 tag) def move_pen(): # 移動筆的函式 global idx, tmr, ix, iy, key bx = ix by = iy if key == 'Left' and maze[iy][ix - 1] == 0: # 當按下方向鍵 '左邊' 時,而且左邊那格為通道時 ix = ix - 1 # ix 減 1 if key == 'Right' and maze[iy][ix + 1] == 0: ix = ix + 1 if key == 'Up' and maze[iy - 1][ix] == 0: iy = iy - 1 if key == 'Down' and maze[iy + 1][ix] == 0: iy = iy + 1 if ix != bx or iy != by: # 只要有移動過 (任一個,合法的上下左右移動) maze[iy][ix] = 2 # 走過設為 2 gx = 80 * ix # 設定移動後的 x 座標 gy = 80 * iy # 設定移動後的 y 座標 cvs.create_rectangle(gx, gy, gx + 80, gy + 80, fill = 'pink', width = 0, tag = 'BG') cvs.delete('PEN') # 先移除 'PEN' tag cvs.create_image(gx + 60, gy + 20, image = pen, tag = 'PEN') # 置入新的 'PEN' if key == 'g' or key == 'G' or key == 'Shift_L': # 按下左側 Shift 或 G 或 g 鍵重玩 key = '' ret = tkinter.messagebox.askyesno('放棄', '重玩嗎?') # 顯示是/否的資訊視窗 if ret == True: # 若按下是 (要重玩) stage_data() # 初始化目前的關卡 erase_bg() # 將 'PEN' (筆圖片的 tag) 與 'BG' (走過粉紅色路的 tag) draw_bg() # 依目前的關卡,繪製出關卡的道路 (0) 與牆壁 (9) def count_tile(): # 計算道路沒走過的數量 cnt = 0 for y in range(8): for x in range(10): if maze[y][x] == 0: cnt = cnt + 1 return cnt def game_main(): # 主程式 global idx, tmr, stage # 將 idx (玩家目前狀態), tmr (模擬時間), stage (關卡) 宣告為全域變數 if idx == 0: # idx = 0,第一關開始 stage_data() # 載入地圖資料 draw_bg() # 畫出地圖 idx = 1 # idx = 1,代表已經開始玩 if idx == 1: # 若已經開始玩,則執行移動筆與檢查有沒有過關 move_pen() if count_tile() == 0: # 若道路為 0 (代表都被填滿),則顯示 'STAGE CLEAR' txt = 'STAGE CLEAR' if stage == 5: # 若目前的關卡為 5,再顯示 'ALL STAGE CLEAR' txt = 'ALL STAGE CLEAR!' cvs.create_text(400, 320, text = txt, fill = 'white', font = ('Times New Roman', 40, 'bold'), tag = 'BG') # 顯示 'STAGE CLEAR' 的文字 idx = 2 # idx = 2,代表完成目前關卡 tmr = 0 # 完成關卡,初始化 tmr if idx == 2: # 若完成關卡,暫停一下時間後進行下一關的初始化 tmr = tmr + 1 if tmr == 30: if stage < 5: stage = stage + 1 stage_data() # 初始化目前的關卡 erase_bg() # 將 'PEN' (筆圖片的 tag) 與 'BG' (走過粉紅色路的 tag) draw_bg() # 依目前的關卡,繪製出關卡的道路 (0) 與牆壁 (9) idx = 1 # idx = 1,代表已經開始玩 root.after(100, game_main) # 每 0.1 秒即時處理 root = tkinter.Tk() # 宣告一個 TK 視窗物件 root.title('一筆畫成的迷宮遊戲') # 設定視窗的名稱 root.resizable(False, False) # 設定視窗不可變更大小 root.bind('', key_down) # 以 bind() 指定按下按鈕時的執行函式 root.bind('', key_up) # 以 bind() 指定放開按鈕時的執行函式 cvs = tkinter.Canvas(root, width = 800, height = 640) # 建立畫布 cvs.pack() # 配置畫布 pen = tkinter.PhotoImage(file = 'pen.png') # 載入角色圖片 (筆) wall = tkinter.PhotoImage(file = 'wall.png') # 載入牆壁圖片 game_main() # 執行主程式 root.mainloop() # 顯示視窗