基于OOP开发Pong游戏:Turtle移动功能报错排查求助
Pong游戏Turtle面向对象开发问题解决
遇到的问题
开发Pong游戏时,使用Turtle库面向对象实现 paddle 移动,先后遇到三个问题:
- 未将Paddles类继承Turtle时,报错:
AttributeError: 'Paddles' object has no attribute 'ycor'
- 将Paddles改为Turtle子类后,报错:
TypeError: unsupported operand type(s) for +=: 'method' and 'int'
- 使用lambda绑定按键事件后,程序运行但按键无响应,同时大量抛出:
File "C:\Users\okpla\AppData\Local\Programs\Python\Python311\Lib\turtle.py", line 1294, in _incrementudc
raise Terminator
用户原始代码:
from turtle import Screen,Turtle wn = Screen() wn.title("Pong by CGGamer") wn.bgcolor("black") wn.setup(width=800, height=600) wn.tracer(0) class Paddles(Turtle): def __init__(self,position,size): super().__init__() self.position = position self.size = size self.speed(0) self.shape("square") self.shape("square") # 重复代码 self.color("white") self.shapesize(size,1) self.penup() self.setposition(position) wn.listen() wn.onkeypress(lambda self:self.sety(self.ycor() + 20),"w") paddle_a = Paddles((-350,0),5) paddle_b = Paddles((350,0),5) ball = Paddles((0,0),1)
错误原因与修复方案
核心问题分析
- 第一个错误:Paddles未继承Turtle类,无法调用Turtle内置的
ycor()、sety()等方法,必须让Paddles成为Turtle的子类。 - 第二个错误:调用
ycor时未加括号(self.ycor是方法对象,不是数值),正确写法是self.ycor()获取当前y坐标值。 - 第三个错误:
- lambda中的
self是多余参数:onkeypress的回调函数不会自动传递实例对象,lambda里的self会被当成未传入的参数,导致调用失败。 wn.listen()被多次调用:每个Paddles实例初始化时都调用一次,只需在屏幕设置完成后调用一次即可。- 缺少主循环与屏幕更新:使用
wn.tracer(0)关闭自动刷新后,必须手动调用wn.update(),且需要主循环维持程序运行。 - 球复用Paddles类不合理:球的行为(移动、碰撞)与paddle不同,应单独实现。
- lambda中的
修复后的完整代码
from turtle import Screen, Turtle # 屏幕初始化 wn = Screen() wn.title("Pong by CGGamer") wn.bgcolor("black") wn.setup(width=800, height=600) wn.tracer(0) wn.listen() # 只调用一次监听 class Paddles(Turtle): def __init__(self, position, size): super().__init__() self.size = size self.speed(0) self.shape("square") self.color("white") self.shapesize(stretch_wid=size, stretch_len=1) self.penup() self.goto(position) # 封装移动方法,更符合面向对象设计 def move_up(self): new_y = self.ycor() + 20 # 限制paddle不超出屏幕上边界 if new_y < 250: self.sety(new_y) def move_down(self): new_y = self.ycor() - 20 # 限制paddle不超出屏幕下边界 if new_y > -250: self.sety(new_y) class Ball(Turtle): def __init__(self): super().__init__() self.speed(0) self.shape("square") self.color("white") self.penup() self.goto(0, 0) # 球的移动速度 self.dx = 2 self.dy = 2 # 创建实例 paddle_a = Paddles((-350, 0), 5) paddle_b = Paddles((350, 0), 5) ball = Ball() # 绑定按键事件,直接调用实例的方法 wn.onkeypress(paddle_a.move_up, "w") wn.onkeypress(paddle_a.move_down, "s") wn.onkeypress(paddle_b.move_up, "Up") wn.onkeypress(paddle_b.move_down, "Down") # 主循环,维持程序运行并更新屏幕 while True: wn.update() # 球的移动逻辑 ball.setx(ball.xcor() + ball.dx) ball.sety(ball.ycor() + ball.dy) # 球的边界碰撞检测(上下) if ball.ycor() > 290 or ball.ycor() < -290: ball.dy *= -1 # 球与paddle碰撞检测(左右) if (ball.xcor() > 340 and ball.xcor() < 350) and (ball.ycor() < paddle_b.ycor() + 50 and ball.ycor() > paddle_b.ycor() -50): ball.dx *= -1 if (ball.xcor() < -340 and ball.xcor() > -350) and (ball.ycor() < paddle_a.ycor() + 50 and ball.ycor() > paddle_a.ycor() -50): ball.dx *= -1
关键修改点
- 将按键事件绑定从类内部移到外部,直接调用实例的
move_up/move_down方法,避免lambda参数错误。 - 封装paddle的移动方法,增加边界限制,防止超出屏幕。
- 单独创建Ball类,实现球的移动与碰撞逻辑。
- 主循环中手动更新屏幕,维持程序运行。
- 只调用一次
wn.listen()。
内容的提问来源于stack exchange,提问作者CGGamer
相关产品推荐
相关产品推荐

