如何在python turtle中管理事件处理程序递归?

时间:2019-01-20 20:32:25

标签: python object recursion event-handling turtle-graphics

我一直在使用Python中的turtle模块开发Pong游戏。下面是我的代码:

Dir

这是可行的,但是如果您使用键进行播放,最终将返回错误:

  

RecursionError:调用a时超出了最大递归深度   Python对象

首先看来问题出在foldl函数上,但实际的问题出在按键的事件处理程序上。如果我仅使用鼠标进行游戏,则游戏不会出​​错,只会让人感觉生涩。

到目前为止,我已经阅读了以下主题:

maximum recursion depth exceeded

Avoid RecursionError in turtle paint code

Turtle.onkeypress not working (Python)

并尝试实施在此找到的解决方案。对我而言唯一有效的方法是为from turtle import Turtle, _Screen, TurtleScreen from random import choice, randrange, randint from tkinter import * from tkinter import messagebox class Field(_Screen): def __init__(self, width = 1024, height = 600): # Get __init__ from _Screen super().__init__() # Get __init__ from TurtleScreen (parent class of _Screen) TurtleScreen.__init__(self, self._canvas) if Turtle._screen is None: Turtle._screen = self self.width = width self.height = height self.setup(self.width+100,self.height+50) self.screensize(self.width,self.height) self.title("Pong") self.bgcolor("black") # Define size of score bar, above the play field self.score_height = self.height/10 # Offset 0 axis line, due to score bar self.yzero = -(self.height/2 - (self.height-self.score_height)/2) class Ball(Turtle): def __init__(self, velocity = 5, size = 1, color = "white"): super().__init__(shape="circle",visible=False) self.color(color) self.speed(0) self.penup() self.shapesize(size,size) self.setposition(0,field.yzero) self.st() self.velocity = velocity self.dirrection = 0 def player_collision(self,player): bx,by = self.position() px,py = player.position() x_off = ball.shapesize()[1]*10 + player.shapesize()[0]*10 y_off = ball.shapesize()[0]*10 + player.shapesize()[1]*10 if px > 0: if (bx > px-x_off and by <= py+y_off and by >= py-y_off): return True elif px < 0: if (bx < px+x_off and by <= py+y_off and by >= py-y_off): return True return False def court_collision(self,court): if (ball.ycor() >= ((field.height/2)- court.score_height-self.shapesize()[0]*10) or ball.ycor() <= -court.height/2+10): return True return False def out_left(self,court): if self.xcor() <= -court.width/2: return True return False def out_right(self,court): if self.xcor() >= court.width/2: return True return False class Player(Turtle): def __init__(self, x=0, y=0, color="white", up=None, down=None): super().__init__(shape="square",visible=False) self.color(color) self.speed(0) self.penup() # setup player paddle self.shapesize(1,10) # Rotate turtle, to allow the use of forward method self.setheading(90) self.setposition(x,y) self.st() self.score = 0 self.height = self.shapesize()[1]*10 self.velocity = 50 self.ondrag(self.drag) self.upkey = up self.downkey = down def drag(self,x,y): self.ondrag(None) # Disable event handler to avoid recursion if y >= (field.height/2-field.score_height) - self.height: y = (field.height/2-field.score_height) - self.height if y <= -field.height/2+self.height: y = -field.height/2+self.height self.goto(self.xcor(),y) self.ondrag(self.drag) # Reactivate event handler def up(self): #field.onkeypress(None, self.upkey) if (self.ycor()+self.height <= (field.height-field.score_height)/2+field.yzero): self.forward(self.velocity) #field.onkeypress(self.up, self.upkey) def down(self): #field.onkeypress(None, self.downkey) if self.ycor()-self.height >= -field.height/2: self.forward(-self.velocity) #field.onkeypress(self.down, self.downkey) class Score(Turtle): def __init__(self): super().__init__(visible=False) self.speed(0) self.color("white") self.pensize(3) # Draw lower border self.penup() self.goto(-field.width,-field.height/2) self.pendown() self.goto(field.width,-field.height/2) # Draw upper border self.penup() self.goto(-field.width,field.height/2-field.score_height) self.pendown() self.goto(field.width,field.height/2-field.score_height) self.penup() # Draw score self.goto(-100,field.height/2-field.score_height) self.write(player2.score,font=("Monospace",50,"bold")) self.goto(100,field.height/2-field.score_height) self.write(player1.score,font=("Monospace",50,"bold")) def update(self): # Clear the previous score for i in range(3): self.undo() # And write the new one self.write(player2.score,font=("Monospace",50,"bold")) self.goto(100,field.height/2-field.score_height) self.write(player1.score,font=("Monospace",50,"bold")) class Game: def __init__(self,court,difficulty=0): # Difficulty = increase in ball speed self.difficulty = difficulty # Setup event handlers court.onkeypress(self.qt, "Escape") court.onkeypress(player1.up, player1.upkey) court.onkeypress(player1.down, player1.downkey) court.onkeypress(player2.up, player2.upkey) court.onkeypress(player2.down, player2.downkey) court.onkey(self.pause, "p") court.listen() # Try to implement game pause. Not working, for the moment #self.pause = False #self.pause_count = 0 def reset(self): ball.setposition(0,field.yzero) player1.setposition(player1.xcor(),field.yzero) player2.setposition(player2.xcor(),field.yzero) ball.dirrection = choice([0,180]) # Left or right ball.setheading(ball.dirrection+randrange(-80,80)) def restart(self): self.reset() self.player1_score = 0 self.player2_score = 0 self.difficulty = 0 def qt(self): prompt = Tk() prompt.eval('tk::PlaceWindow %s center' % prompt.winfo_toplevel()) prompt.withdraw() answer = messagebox.askyesno("Quit", "Are you sure you want to quit?") if answer == True: field.bye() return # Not currently working def pause(self): if self.pause_count % 2 == 0: self.pause == True else: self.pause = False class Play(Turtle): def __init__(self): super().__init__(visible=False) self.shape("square") self.color("white") self.speed(0) self.penup() self.shapesize(2,4) self.goto(-field.width/2,field.height/2-field.score_height/2) self.write("Play",font=("Monospace",20,"bold")) field.onscreenclick(self.click) def click(self,x,y): print(x,y) if (x <= -field.width/2+field.width/2/10 and x >= -field.width/2 and y >= field.height/2-field.score_height/2 and y <= field.height/2): self.color("green") self.clear() self.write("Play",font=("Monospace",20,"bold")) self.color("white") self.clear() self.write("Play",font=("Monospace",20,"bold")) game.reset() main() def main(): ball.forward(ball.velocity+game.difficulty) # Check for paddle collision if ball.player_collision(player1) or ball.player_collision(player2): ball.setheading(180 - ball.heading()) # Bounce from upper or lower border if ball.court_collision(field): ball.setheading(-ball.heading()) # Check for ball out of field and update player score elif ball.out_right(field): game.reset() player2.score += 1 score.update() game.difficulty += 0.5 elif ball.out_left(field): game.reset() player1.score += 1 score.update() game.difficulty += 0.5 field.ontimer(main) if __name__ == "__main__": field = Field(1280,720) ball = Ball() player1 = Player(field.width/2,field.yzero,up = "Up", down = "Down") player2 = Player(-field.width/2,field.yzero, up = "w", down = "s") game = Game(field) score = Score() play_button = Play() #field.mainloop() 函数禁用事件处理程序。如果我尝试在播放器上使用相同的解决方案(取消注释main()的{​​{1}}和ondrag()方法中的行),则仅在up()未运行时才起作用。如果我启动main()函数,它将仅运行一次并停用。

所以我需要帮助的是:

  1. 避免最大递归错误。 (仅当down()有效时才会发生);
  2. 使Player函数正常工作而不会使main()函数混乱;
  3. 如果main()正在运行,则ondrag类中的main()方法将无法正常工作。

那么你们认为我可以改善这些方面吗?

编辑:以下是完整的追溯

qt()

1 个答案:

答案 0 :(得分:0)

我看到的主要问题是您在玩游戏时进行了不必要的计算。例如,考虑一下court_collision()方法,该方法在每次球运动时都会被调用:

def court_collision(self,court):
        if (ball.ycor() >= ((field.height/2)-
                            court.score_height-self.shapesize()[0]*10)
            or ball.ycor() <= -court.height/2+10): return True
        return False

在所有这些值中,只有ball.ycor()处于更改状态,其余的应该已经在游戏开始和隐藏之前进行了计算,因此该方法看起来更像:

def court_collision(self):
        return not self.wall_top_offset > ball.ycor() > self.wall_bottom_offset

player_collision()drag()等的同上。

main() 功能应该实际上是move()的{​​{1}} 方法

我还有其他优点,但它们与游戏性能无关。