"""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__ = "406273900@qq.com"
__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()