我一直在使用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()函数,它将仅运行一次并停用。
所以我需要帮助的是:
down()
有效时才会发生); Player
函数正常工作而不会使main()
函数混乱; main()
正在运行,则ondrag
类中的main()
方法将无法正常工作。那么你们认为我可以改善这些方面吗?
编辑:以下是完整的追溯
qt()
答案 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}} 方法。
我还有其他优点,但它们与游戏性能无关。