Python初学——实例中的编程问题汇总01“弹球游戏”

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/The_Dried_camellia/article/details/81150926

(一)关于“弹球游戏”python编程遇到的问题及解决方案(含有超级详细注释版代码)

1)错误一:

def __init__(self,canvas,color):  ##注意初始化时是双下划线__,容易写成单下划线_!!

在python编程中,注意下划线长度的问题!

2)错误二:

while 1:
    tk.update_idletasks()
    tk.update()
    time.sleep(0.01)
    time.sleep(0.01)

这段代码运行后点击退出出现了以下问题:

Traceback (most recent call last):
  File "F:/Work/python/01Game.py", line 42, in <module>
    tk.update_idletasks()
  File "D:\Python27\ArcGIS10.2\lib\lib-tk\Tkinter.py", line 916, in update_idletasks
    self.tk.call('update', 'idletasks')
TclError: can't invoke "update" command:  application has been destroyed

在查阅解决方案后,据说当退出应用程序时,下次调用update_idletasks窗口对象会被销毁,而代码却仍在基于已经不存在了的窗口调用update;同时time.sleep()这种写法很单一且复杂了。因此最好直接使用tk.mainloop()来进行屏幕更新。代码可做如下修改:

##while 1:
##    tk.update_idletasks()
##    tk.update()
##    time.sleep(0.01)
##    time.sleep(0.01)
tk.mainloop()

但是在写到令小球运动以及后面的代码的时候,我们可以发现其实while 1:...这几行代码的作用就显示出来了,此前关闭窗口报错是正常的,因为我们并没有让球不断地绘制。

3)错误三:

while 1:
    ball.draw()  ##不断重新绘制球
    tk.update_idletasks()
    tk.update()
    time.sleep(0.01)

在运行此行代码时,报了以下错误:

Traceback (most recent call last):
  File "F:\Work\python\01Game.py", line 64, in <module>
    ball.draw()  ##不断重新绘制球
AttributeError: Ball instance has no attribute 'draw'

像 “AttributeError: XXX  instance has no attribute 'XXX'”这种错误是很常见的,也很容易对人们造成误导,但是其出错原因通常是因为代码的缩进等问题。例如,我之所以出现此问题,在于我在Ball类中创建draw函数时,缩进到了__init__函数之下,而draw函数时独立于初始化函数的Ball类下的另一个函数。

解决办法:请仔细检查代码是否有误,注意检查缩进!

简单版弹球游戏完整代码如下(注释非常清晰了,应该不会遇到什么问题?):


import Tkinter #引入GUI工具包
import random #引入随机函数
import time #引入定时器

#创建游戏主界面和配置界面的一些基本属性   (用内置模块Tkinter,它是Python的标准GUI工具包,可以非常方便的在win和linux下运行,我们用Tk里的canvas绘图功能来制作一个小游戏,先把主界面画出来。)

#创建一个tk的实例
tk=Tkinter.Tk() 
tk.title("BallGame") #给这个窗口取一个名字叫BallGame
tk.resizable(0,0) #通知窗口管理器调整布局大小,0,0表示不能被拉伸
tk.wm_attributes("-topmost",1) 
canvas=Tkinter.Canvas(tk,width=500,height=400,bd=0,highlightthickness=0) #创建一个长度为400*500的界面,背景色为默认,边框厚度为0
canvas.pack() #通知窗口管理器注册组件
tk.update() #刷新一下界面


#创建一个Ball球的类

#首先创建一个Ball的类,这个类的初始化参数有三个:一个canvas也就是画图用来画一个球,一个是挡板,一个是color,表示球的颜色
class Ball():  
    def __init__(self,canvas,paddle,color):  ##注意初始化时是双下划线__,容易写成单下划线_!!        
        #在类的初始化的函数里面初始化canvas、paddle
        self.canvas=canvas
        self.paddle=paddle
        self.id=canvas.create_oval(10,10,25,25,fill=color) #画一个实心的球并记录下它的id
        self.canvas.move(self.id,245,100) #创建球的默认在主界面上的位置,把它放在屏幕中间
        
        #让球动起来
        ##将0,-1两个值设成两个变量self.x,self.y,当球运动到上边界的时候,就把self.y加1,
        ##也就是向下运动,而当运动到下边界的时候,就把self.y减1,表示向上运动
        starts=[-3,-2,-1,1,1,2,3]
        random.shuffle(starts)
        self.x=starts[0] ##从list中随机取一个
        self.y=-3 ##-3表示y轴运动的速度
        self.canvas_height=self.canvas.winfo_height() ##取画布当前高度
        self.canvas_width=self.canvas.winfo_width() ##取画布当前宽度
        self.hit_bottom=False ##初始化球撞到画布底部为False
               
    def hit_paddle(self,pos): ##判断球是否撞到挡板
        paddle_pos=self.canvas.coords(self.paddle.id) ##画一个球拍并记录下它的id
        if pos[2]>=paddle_pos[0] and pos[0]<=paddle_pos[2]: ##当前运动中,如果球左右活动范围在挡板长度范围之间
            if pos[3]>=paddle_pos[1] and pos[3]<=paddle_pos[3]: ##当前运动中,如果球所在上下活动范围的点正好与挡板上下活动范围的所在相等
                return True ##满足以上条件,球撞到挡板,返回True
            return False 

            
        
    def draw(self):
        ##表示向上运行
        ##其中,0:x坐标不动;-1:y坐标不断-1向下运动,球则相对于坐标不断向上运动
        self.canvas.move(self.id,self.x,self.y)
        
        ##判断球是否碰壁:令球的坐标为[x1,y1,x2,y2],其中x1,y1表示top-left左上角的坐标
        ##x2,y2表示bottom-right右下角的坐标。在取得球的坐标之后(它是一个list),判断一
        ##post[1]和post[3]就可以了。
        
        pos=self.canvas.coords(self.id) ##coords返回画布上画好的x和y坐标
        ##判断小球是否撞到画布顶部或底部,保证小球反弹回去,不消失
        if pos[1]<=0: ##球碰到画布顶部,y1=0,y轴以3的速度向上运动,球反之向下运动
            self.y=3 
        if pos[0]<=0: ##球碰到画布左部,x1=0,x轴以3的速度向左运动,球反之向右运动
            self.x=3 
        if pos[2]>=self.canvas_width: ##球碰到画布右部,x2=画布宽度,x轴以3的速度向右运动,球反之向左运动
            self.x=-3
        if self.hit_paddle(pos)==True:#如果球撞到挡板,此时返回Ture,满足条件,球反弹
            self.y=-3
        if pos[3]>=self.canvas_height: ##球碰到画布底部,y2=画布高度,游戏结束
            self.hit_bottom=True 
        

##增加一个挡板,代码同Ball类
class Paddle:
    def __init__(self,canvas,color):
        self.canvas=canvas
        self.id=canvas.create_rectangle(0,0,150,10,fill=color)
        self.canvas.move(self.id,200,300)
        self.x=0
        self.canvas_width=self.canvas.winfo_width()
        ##响应键盘按键
        self.canvas.bind_all('<KeyPress-Left>',self.turn_left) 
        self.canvas.bind_all('<KeyPress-Right>',self.turn_right)

    def turn_left(self,evt): #每次按键挡板移动的速度
        self.x=-3
    def turn_right(self,evt):
        self.x=3

    def draw(self):
        self.canvas.move(self.id,self.x,0)
        pos=self.canvas.coords(self.id)
        ##碰到边界则会自动反弹,原理同球类一样
        if pos[0]<=0:
            self.x=0
        if pos[2]>=self.canvas_width:
            self.x=0
    
paddle=Paddle(canvas,"green")

ball=Ball(canvas,paddle,"red")

#不断地重新绘制球和挡板,并刷新界面
while 1:
    if ball.hit_bottom==False: ##如果球一直没有撞到画布底部,就不断绘制球和挡板
        ball.draw()  
        paddle.draw()
    tk.update_idletasks()
    tk.update()
    time.sleep(0.01)
    pass
tk.mainloop()









持续更新中,后面接着学写加强版。。。

展开阅读全文

没有更多推荐了,返回首页