"""bcwawa.py
《編程娃娃Python版》是繼《編程娃娃》的姊妹篇。它不需要拖曳編程命令,採用的是直接編排圖形化指令的模式,指揮一個娃娃回家。
此版本更重要的是能做爲一個模塊導入到Python原生環境,從而實現用真正的Python命令去指揮娃娃移動。
本版本新增命令如下:
forward:前進50像素,縮寫爲:fd,使用方法: fd()
backward:倒退50,縮寫爲:bk,back,使用方法:bk()
right:右轉90,縮寫:rt, 使用方法:rt()
left:左轉90,縮寫:lt,使用方法:lt()
jump:前進100,主要用來跳過炸彈。使用方法:jump()
bomb_ahead:檢測娃娃前面是否有炸彈,返回布爾值。使用方法: bomb_ahead()
playmusic:播放音樂,不輸入參數會播放小星星曲子。使用方法: playmusic()
pausemusic:暫停播放音樂。使用方法: playmusic()
stopmusic:停止播放音樂。使用方法: stopmusic()
unpausemusic:繼續播放音樂。使用方法: unpausemusic()
set_level:設置當前關卡號。使用方法: set_level(3)
娃娃根據指令前進的主要原理如下:
在作品中設計了三排按鈕,娃娃能訪問這三排按鈕。
第一排是子指令表1(相當於子程序1),以橙星開始,後面跟着9個指令按鈕。
第二排也是子指令表2(相當於子程序2),以綠星開始,後面跟着9個指令按鈕。
第三排代表主指令表(相當於主程序),以信封開始,後面跟着9個指令按鈕.
程序啓動後,娃娃對象生成,它會註冊回車鍵綁定自己的execute方法。
當按回車鍵或單擊信封按鈕時,娃娃就像是收到了信的內容,會根據信的內容執行它的execute方法。
這個方法執行時會遍歷主指令列表,從而根據主指令表中的命令讓娃娃前進,轉向或執行子指令表。
子程序也能調用子程序,但不能調用主程序。當子程序遞歸調用過多時,娃娃會自動停止移動。
讓娃娃移動的三種方法:
1、按鍵:直接按上、下、左、右,空格鍵,手動操作娃娃。適合於體驗按鍵。
2、編排圖形化指令:單擊信封及五角星後面的按鈕,給娃娃編排指令。信封后面的9個按鈕爲主指令表,橙星爲調用子程序1指令,綠星爲調用子程序2指令。
向上箭頭爲前進指令(並不僅僅是向上移動,而是朝它當前的方向移動)。向右轉箭頭爲右轉指令,向左轉箭頭爲左轉指令,彎箭頭爲跳躍指令。
3、Python編程模式:把本程序做爲模塊導入,然後通過編寫Python代碼指揮娃娃前進。典型代碼舉例:
from bcwawa import *
set_level(9) # 設置當前關卡爲第9關。
sleep(3)
for i in range(4): # 這個for循環的意思是重複4次“前進前進右轉”
fd();fd()
rt()
配置文件名:cfg.txt
本程序的房子和炸彈這兩種道具擺放方式分爲過關模式與隨機模式。過關模式是按內置的排列順序每關擺放在固定的位置。
隨機模式每個關卡都是隨機的。
如果配置文件不存在,則默認爲過關模式。
如果配置文件存在,裏面的字符串爲1,則也是關卡模式,其它字符則爲隨機擺放模式。
主要原理:在生成娃娃對象時,傳遞__game_mode__變量的值,讓娃娃知道模式,當它碰到房子就能決定採用哪種方式重新擺放道具。
"""
__author__ = "李興球"
__date__ = "2018/10"
__email__ = "[email protected]"
__version__ = "1.0"
import sys,os,webbrowser
from turtle import *
from codewawa.gridturtle import *
from codewawa.bomb import *
from codewawa.house import *
from codewawa.wawa import *
from codewawa.command import *
from random import randint
from time import sleep
"全局變量加雙下劃線,不污染全局名稱空間"
__gamename__ = "編程娃娃Python版"
__images_folder__ = os.getcwd() + os.sep + "images" + os.sep
__audios_folder__ = os.getcwd() + os.sep + "audios" + os.sep
__explosion_sound__=__crystalsound__=__stepsound__=__rotatesound__=__jumpsound__ = None
__sounds__ = [ ] # 娃娃的三個音效表
__pygame_normal__ = True # 描述pygame是否正常或能否初始化混音器
__wawagif__ = [] # 娃娃的4個方向gif圖片表
__commandpic__= [] # 命令按鈕gif圖片表
__screen_width__,__screen_height__ = 660,660 # 設定屏幕寬度和高度
__explode_image_list__ = [] # 爆炸效果造型表
__house_list__ = [] # 多個圖片只是爲了房子有動態效果
__mute_list__ = []
__all_images__ = ['invalid.gif', 'roundman_down.gif', 'roundman_left.gif', 'roundman_right.gif', 'roundman_up.gif', 'volumeOff.gif', 'volumeOn.gif', '信封.gif', '前進.gif', '右轉.gif', '左轉.gif', '房子0.gif', '房子1.gif', '橙星.gif', '橙色星.gif', '炸彈.gif', '爆炸效果-0.gif', '爆炸效果-1.gif', '爆炸效果-10.gif', '爆炸效果-11.gif', '爆炸效果-2.gif', '爆炸效果-3.gif', '爆炸效果-4.gif', '爆炸效果-5.gif', '爆炸效果-6.gif', '爆炸效果-7.gif', '爆炸效果-8.gif', '爆炸效果-9.gif', '綠星.gif', '綠色星.gif', '跳躍.gif']
__all_images__ = [ __images_folder__ + image for image in __all_images__]
__download_url__ = "http://www.scratch8.net/downloads/bcwawa_python.rar"
def check_images():
"""圖像文件丟失檢測,在主程序所在文件夾下面要有名爲images目錄名的文件夾,存儲圖像資源。"""
for image in __all_images__:
if not os.path.exists(image):
info = image + "文件丟失。"
info = info + "請從以下網址重新下載本軟件:" + "\n"
info = info + __download_url__
showerror(__gamename__,info)
return False
return True
def __load_cfg__(filename):
"""加載配置文件"""
if os.path.exists(filename): # 配置文件存在則讀取字符串
f = open(filename)
__game_mode__ = f.read()
f.close()
if __game_mode__.strip() == "1":# 字符串爲1,關卡模式爲過關模式
__game_mode__ = 1
else:
__game_mode__ = 0 # 爲其它字符串爲隨機擺放模式
else:
__game_mode__ = 1 # 文件不存在,默認爲關卡模式
return __game_mode__
def __init_screen__(width,height,bgcolor,title):
"""初始化屏幕,註冊gif圖到形狀列表"""
__wawagif__.append(__images_folder__ + "roundman_right.gif") # 娃娃朝向右的圖形
__wawagif__.append(__images_folder__ + "roundman_up.gif") # 娃娃朝向上的圖形
__wawagif__.append(__images_folder__ + "roundman_left.gif") # 娃娃朝向左的圖形
__wawagif__.append(__images_folder__ + "roundman_down.gif") # 娃娃朝向下的圖形
__commandpic__.append(__images_folder__ + "前進.gif") # “前進”指令的圖形
__commandpic__.append(__images_folder__ + "左轉.gif") # “左轉” 指令的圖形
__commandpic__.append(__images_folder__ + "右轉.gif") # “右轉”指令的圖形
__commandpic__.append(__images_folder__ + "橙星.gif") # “子程序1”指令的圖形
__commandpic__.append(__images_folder__ + "綠星.gif") # “子程序2”指令的圖形
__commandpic__.append(__images_folder__ + "跳躍.gif") # “跳躍”指令的圖形
__commandpic__.append(__images_folder__ + "invalid.gif") # “無”指令的圖形
__explode_image_list__ .append( __images_folder__ + "炸彈.gif") # 定義爆炸造型表
__explode_image_list__.extend([ __images_folder__ + "爆炸效果-" + str(i) + ".gif" for i in range(12)])
__house_list__.append(__images_folder__ + "房子0.gif")
__house_list__.append(__images_folder__ + "房子1.gif")
__mute_list__.append(__images_folder__ + "volumeOn.gif")
__mute_list__.append(__images_folder__ + "volumeOff.gif")
screen = Screen()
screen.title(title)
screen.bgcolor(bgcolor)
screen.setup(width,height)
screen.delay(0)
if not check_images():screen.bye();return
[screen.addshape(image) for image in __commandpic__] # 註冊所有命令按鈕的造型到屏幕
[screen.addshape(image) for image in __wawagif__] # 註冊所有娃娃的造型到屏幕的形狀列表
[screen.addshape(image) for image in __explode_image_list__] # 註冊炸彈的所有造型到形狀列表
[screen.addshape(image) for image in __house_list__] # 註冊房子的造型到形狀列表
[screen.addshape(image) for image in __mute_list__] # 註冊靜音按鈕的造型到形狀列表
return screen
def __draw_grid__(start,end,rowscols):
"""畫格子的函數,參數說明:
start:起始座標元組,左上角
end:結束座標元組, 右下角
rowscols:行和列數
"""
g = Gridturtle() # 畫格子的海龜對象
g.drawgrid(start,end,rowscols,"gray",4) # 格子海龜專業畫格子
def __make_button__():
"""生成命令按鈕,每個按鈕有7種指令,對應7張gif圖,它們都在列表中。
單擊按鈕會調用相應的方法切換指令,同時切換到相應的圖形。
"""
suborange = [] # 子程序1指令序列
for x in range(-200,201,50):
suborange.append(Command(__commandpic__,__commandlist__,x,-100))
subgreen = [] # 子程序2指令序列
for x in range(-200,201,50):
subgreen.append(Command(__commandpic__,__commandlist__,x,-150))
main = [] # 主程序指令序列
for x in range(-200,201,50):
main.append(Command(__commandpic__,__commandlist__,x,-200))
return suborange,subgreen,main
def forward():
"""讓娃娃前進50個像素"""
wawa.fd50()
def right():
"""讓娃娃右轉90度"""
wawa.rt90()
def left():
"""讓娃娃左轉90度"""
wawa.lt90()
def jump():
"""讓娃娃跳過一格,實際上是前進100個像素"""
wawa.jump()
def backward():
"""讓娃娃倒退50個像素"""
wawa.fd_50()
def bomb_ahead():
"""檢測在娃娃的前進方向上是否有一個炸彈"""
return wawa.front_have_bomb_check()
def playmusic(filename = __audios_folder__ + "小星星.wav",times = -1):
"""播放音樂,times:播放次數,-1表示無限,0表示1次,1表示播放1次,2表示播放3次,詳情看pygame說明書"""
try:
pygame.mixer.music.load( filename)
pygame.mixer.music.play(times,0)
except:
print("出錯了,音樂文件不存在或無法播放。")
def stopmusic():
"""停止播放音樂"""
try:
pygame.mixer.music.stop()
except:
pass
def pausemusic():
"""暫停播放音樂"""
try:
pygame.mixer.music.pause()
except:
pass
def unpausemusic():
"""繼續播放音樂"""
try:
pygame.mixer.music.unpause()
except:
pass
def __init_audio__():
"""本函數試圖導入pygame模塊,如果導入不成功或者混音初
始化不成功,相關變量的值都爲None,程序仍舊能運行,只是不發聲。
"""
global __pygame_normal__,__explosion_sound__,__crystalsound__,__stepsound__,__rotatesound__,__jumpsound__
try:
import pygame
pygame.mixer.init()
except:
pygame = None
__pygame_normal__ = False
print("pygame模塊沒有安裝或混音器初始化不成功。請重新安裝pygame模塊。\n安裝方法,請在命令提示符下輸入:pip install pygame --user。")
if __pygame_normal__: # 如果pygame正常
try:
__explosion_sound__ = pygame.mixer.Sound( __audios_folder__ + "BOMB2.wav")
__crystalsound__ = pygame.mixer.Sound( __audios_folder__ + "水晶.wav")
__stepsound__ = pygame.mixer.Sound( __audios_folder__ + "步進聲.wav")
__rotatesound__ = pygame.mixer.Sound( __audios_folder__ + "轉彎聲.wav")
__jumpsound__ = pygame.mixer.Sound( __audios_folder__ + "跳躍聲.wav")
except:
pass
__sounds__.append(__stepsound__)
__sounds__.append(__rotatesound__)
__sounds__.append(__jumpsound__)
return pygame
def set_level(number = 0):
"""設置起始關卡"""
if type(number) != type(3) : number = 1
if number <= 0 :number = 1
wawa.level_number = number - 1
wawa.resetbombs()
wawa.display_info() #顯示信息
"""定義別名"""
前進 = 走你 = fd = qianjin = qj = go = forward
後退 = 倒退 = bk = daotui = dt = back = backward
右轉 = 向右 = rt = right
左轉 = 向左 = lt = left
關卡 = 設關卡 = 設定關卡 = 設置關卡 = 設置關卡號 = setlevel = set_level
等待 = 延時 = sleep
跳 = 跳躍 = jump
pygame = __init_audio__()
screen = __init_screen__(__screen_width__,__screen_height__,"white",__gamename__) # 新建屏幕對象
if screen==None:sys.exit(0)
__game_mode__ = __load_cfg__("cfg.txt")
#-------------------------畫格子 ----------------------------------
"""畫一個5x9的格子,每個格子的像素爲50x50。
"""
__draw_grid__((-225,250),(225,0),(5,9)) # 畫格子,起始座標,結束座標,行列數
#------------------------生成命令按鈕------------------------------
"""這段程序生成三行命令按鈕,每行有9個按鈕,從上到下。
第一行的按鈕是代碼娃娃的子程序1,它用橙色星星代表。用suborange列表保存。
第二行的按鈕是代碼娃娃的子程序2,它用綠色星星代表。用subgreen列表保存。
第三行的按鈕是代碼娃娃的主程序, 它用一封信代表。用main列表保存。
"""
__commandlist__ = ["前進","左轉","右轉","suborange","subgreen","跳躍","pass"]
suborange,subgreen,main = __make_button__() # 生成橙,綠,主程序指列按鈕
#----------------------炸彈 ----------------------------------
"""本段程序新建12個炸彈
"""
def __manual_bomb__():
"""按z鍵人工生成一個炸彈,在(0,-25)坐置"""
zd = Bomb(__explode_image_list__,__explosion_sound__)
zd.goto(0,-25)
zd.showturtle()
bombs.append(zd)
if __game_mode__ == 1:
bombs = [Bomb(__explode_image_list__,__explosion_sound__) for i in range(36)] # 新建36個炸彈
else:
bombs = [Bomb(__explode_image_list__,__explosion_sound__) for i in range(12)] # 新建12個炸彈
screen.onkeypress(__manual_bomb__,"z") #按z鍵可人工生成一顆炸彈
#----------------------6、房子 ----------------------------------
"""本段程序生成房子對象,參數爲房子造型列表,水晶聲,炸彈列表
"""
house = House(__house_list__,__crystalsound__,bombs) # 新建房子對象
#----------------------娃娃 ----------------------------------
"""本段程序生成娃娃對象,它的參數如下:
__wawagif__:它是已經註冊到形狀列表的娃娃的4個造型。
main:娃娃的要執行的主程序指令表。
suborange:娃娃的主程序將調用的子程序1。
subgreen:娃娃的程序將調用的子程序2。
house:娃娃可訪問房子。
__allcoordinates__:所有格子中央座標點。洗牌後就是隨機的,用於隨機放置炸彈。
bombs:娃娃可訪問所有炸彈。
__sounds__:娃娃的音效列表。
"""
__allcoordinates__=[] # 所有格子的座標中心點
__gridx__,__gridy__ = -200,25 # 左下角格子中心點座標
for __x__ in range(__gridx__,201,50):
for __y__ in range(__gridy__,226,50):
#print("(",__x__,",",__y__,")",end = ",")
__allcoordinates__.append((__x__,__y__))
#print()
__ps__ = __wawagif__,main,suborange,subgreen,house,__allcoordinates__,bombs,__sounds__,__game_mode__,Command
wawa =Wawa(*__ps__) # 新建娃對象,它的默認的座標爲(-200,25)
if __game_mode__ != 1 : # 隨機模式
wawa.resetbombs() # 重置炸彈的位置
else:
set_level()
#----------------------放幾個標誌----------------------------------
__flag_images__ = [ __images_folder__ + "橙色星.gif", __images_folder__ + "綠色星.gif", __images_folder__ + "信封.gif"]
[screen.addshape(image) for image in __flag_images__]
__flag__ = Turtle(visible = False) # 初始狀態是不可見的
__flag__.pencolor("orange")
__flag__.penup()
__x__,__y__ = 0,270
__flag__.goto(__x__,__y__)
__flag__.write(__gamename__,align='center',font=("黑體",16,"normal"))
__x__,__y__ = -260,-100
for __i__ in range(3):
__flag__.shape(__flag_images__[__i__])
__flag__.goto(__x__,__y__)
if __i__ < 2 : __flag__.stamp() # 最後一個不蓋章,要不然無法單擊信封實現運行指令
__y__ = __y__ - 50
__envelopex__ = __flag__.xcor()
__envelopey__ = __flag__.ycor()
__x__,__y__ = -230,-110
__flag__.pencolor("gray")
for __i__ in range(3):
__flag__.goto(__x__,__y__)
__flag__.write("=",align='center',font=("Arial",14,"normal"))
__y__ = __y__ - 50
__flag__.goto(__envelopex__,__envelopey__)
__flag__.showturtle()
__flag__.onclick(lambda __x__,__y__:wawa.execute()) # 單擊左鍵執行main指令集
__flag__.onclick(lambda __x__,__y__:wawa.gohome(),3) # 單擊右鍵回到左下角格子
#----------------------播放與靜音按鈕 ----------------------------------
"靜音按鈕,起始狀態是on,單擊後放音樂。造型列表爲:__mutebutton__"
__mute_index__ = 0
def __alt_music_status__():
"""播放音樂或停止播放"""
global __mute_index__
__mutebutton__.shape(__mute_list__[1 -__mute_index__])
if __mute_index__ == 1:
playmusic()
else:
stopmusic()
__mute_index__ = 1 - __mute_index__
__x__,__y__ = -260,-250
__mutebutton__ = Turtle(shape = __mute_list__[0],visible = False) # 初始狀態是不可見的
__mutebutton__.penup()
__mutebutton__.goto(__x__,__y__)
__mutebutton__.showturtle()
__mutebutton__.onclick(lambda x,y:__alt_music_status__())
playmusic()
screen.listen()
if __name__ == "__main__":
def open_url(x,y):
#print(x,",",y)
if x>=-328 and x<=323 and y>=-321 and y<=-270:
webbrowser.open("http://www.scratch8.net/wawa_python.php")
copyrighter = Turtle(visible=False)
copyrighter.penup()
copyrighter.color("gray")
copyrighter.sety(-300)
copyrighter.write("風火輪少兒編程出品,操作說明:http://www.scratch8.net/wawa_python.php",align='center',move=False,font=("楷體",12,"normal"))
screen.onclick(open_url)
screen.mainloop()