isvisible() 缺少 1 个必需的位置参数:'self' Python Turtle Graphics Error

isvisible() missing 1 required positional argument: 'self' Python Turtle Graphics Error

几天来,我一直在 python turtle 中开发这款简单的射击游戏,但 运行 遇到了一个我找不到答案的错误。 我想要发生的是玩家每杀死 5 人,就会出现一个额外的敌人。但是当我击杀 5 人时,无限的错误消息开始涌入。 错误信息是:isvisible() missing 1 required positional argument: 'self' 它指向我的 enemy_attack 函数,我在其中使用 isvisible() 命令来测试敌人是否被击中,以便从另一个随机位置重生它。感谢您提前提供帮助。 对于缺少评论,我深表歉意。

import turtle
import time
import random
from random import randint
game_over = False
kill_counter = 0
enemy_spawn_number = 5

def close():
    turtle.bye()

speed = 10
enemy_speed = 0.5

lives = 3
wn = turtle.Screen()
wn.title("Defend.")
wn.bgcolor("black")
wn.setup(width=600, height=600)
wn.tracer(0)

player = turtle.Turtle()
player.speed(0)
player.shape("triangle")
player.color("white")
player.penup()
player.goto(0,0)

life1 = turtle.Turtle()
life1.speed(0)
life1.shape("triangle")
life1.color("yellow")
life1.penup()
life1.goto(265,-260)
life1.setheading(-90)

life2 = life1.clone()
life2.goto(240,-260)

life3 = life2.clone()
life3.goto(215,-260)

over_message = turtle.Turtle()
over_message.speed(0)
over_message.shape("square")
over_message.color("white")
over_message.penup()
over_message.goto(0,0)
over_message.ht()

killcount = over_message.clone()
killcount.goto(250,250)
killcount.ht()
killcount.write("Kill Count: 0", align="right", font=("Courier", 15, "normal"))


def go_up():
    player.setheading(90)
    player.forward(speed)

def go_right():
    player.setheading(0)
    player.forward(speed)


def go_left():
    player.setheading(180)
    player.forward(speed)

def go_down():
    player.setheading(-90)
    player.forward(speed)

wn.listen()
wn.onkeypress(go_up, "w")
wn.onkeypress(go_right, "d")
wn.onkeypress(go_left, "a")
wn.onkeypress(go_down, "s")
wn.onkeypress(close, "Escape")


collectible = turtle.Turtle()
collectible.speed(0)
collectible.shape("square")
collectible.color("blue")
collectible.penup()
collectible.goto(0,100)

bullet = turtle.Turtle()
bullet.shape("square")
bullet.color("grey")
bullet.shapesize(0.5,1)
bullet.hideturtle()
bullet.penup()
bullet.goto(500,500)

shooting = False
def shoot():
    global shooting
    if bullet.isvisible() == False:
        player_x = player.xcor()
        player_y = player.ycor()
        player_facing = player.heading()
        bullet.goto(player_x,player_y)
        bullet.setheading(player_facing)
        shooting = True
        bullet.showturtle()

enemies = []
enemies.append(turtle.Turtle())
for enemy in enemies:
    enemy.speed(0)
    enemy.shape("circle")
    enemy.color("red")
    enemy.penup()
    enemy.goto(0,350)

def enemy_attack():
    global game_over
    for enemy in enemies:
        if enemy.isvisible() == True:
            enemy.setheading(enemy.towards(player))
            enemy.forward(enemy_speed)
            wn.ontimer(enemy_attack, 10)
        else:
            if game_over == False:
                rand_direction = randint(0,3)
                if rand_direction == 0:
                    x = randint(-240, 200)
                    enemy.goto(x,450)
                    enemy.st()
                    enemy_attack()
                elif rand_direction == 1:
                    x = randint(-240, 200)
                    enemy.goto(x,-450)
                    enemy.st()
                    enemy_attack()
                elif rand_direction == 2:
                    y = randint(-210, 210)
                    enemy.goto(-450,y)
                    enemy.st()
                    enemy_attack()
                elif rand_direction == 3:
                    y = randint(-210, 210)
                    enemy.goto(450,y)
                    enemy.st()
                    enemy_attack()

enemy_attack()      
wn.onkey(shoot, "space")
while True:
    wn.update()

    if player.distance(collectible) < 20:
        x = randint(-290, 290)
        y = randint(-290, 290)
        collectible.goto(x,y)

    if bullet.xcor()>310 or bullet.xcor()<-310 or bullet.ycor()>310 or bullet.ycor()<-300:
        shooting = False
        bullet.hideturtle()

    if shooting == True:
        bullet.forward(0.2)

    if player.xcor()>285:
        playerX = player.xcor()
        playerX = playerX-5
        playerY = player.ycor()
        player.goto(playerX,playerY)
    elif player.xcor()<-285:
        playerX = player.xcor()
        playerX = playerX+5
        playerY = player.ycor()
        player.goto(playerX,playerY)
    elif player.ycor()>285:
        playerY = player.ycor()
        playerY = playerY-5
        playerX = player.xcor()
        player.goto(playerX,playerY)
    elif player.ycor()<-285:
        playerY = player.ycor()
        playerY = playerY+5
        playerX = player.xcor()
        player.goto(playerX,playerY)

    for enemy in enemies:
        if bullet.distance(enemy) < 20:
            bullet.hideturtle()
            enemy.hideturtle()
            enemy.goto(0,350)
            kill_counter = kill_counter+1
            killcount.clear()
            killcount.write("Kill Count: {}".format(kill_counter), align="right", font=("Courier", 15, "normal"))
            if kill_counter == enemy_spawn_number:
                enemies.append(turtle.Turtle)
                for enemy in enemies:
                        enemy.speed(0)
                        enemy.shape("circle")
                        enemy.color("red")
                        enemy.penup()
                        enemy.goto(0,350)
                enemy_spawn_number += 5

        if enemy.distance(player) < 20:
            enemy.ht()
            player.goto(0,0)
            lives = lives-1
            if lives == 2:
                life3.ht()
            elif lives == 1:
                life2.ht()
            elif lives == 0:
                life1.ht()
            time.sleep(0.1)

    if lives == 0:
        game_over = True
        collectible.ht()
        player.ht()
        for enemy in enemies:
            enemy.ht()
        over_message.write("Game Over! Press Esc to exit.", align="center", font=("Courier", 24, "normal"))```

我 运行 代码,大部分时间都没有问题。

有时会出错

Traceback (most recent call last):
  File "/home/furas/main.py", line 198, in <module>
    enemy.speed(0)
  File "/usr/lib/python3.7/turtle.py", line 2167, in speed
    return self._speed
AttributeError: 'int' object has no attribute '_speed'

我开始思考为什么 enemy(即 turtle)没有 _speed?为什么将其视为整数?所有 enemy 都在列表 enemies 上,所以我开始检查您添加到此列表的内容,我发现您在 Turtle()

之一中忘记了 ()
enemies.append(turtle.Turtle)

所以它添加了 class Turtle 而不是 Turle 的实例。

添加 () 后我没有这个错误,但似乎还有其他问题,因为在杀死 5 个敌人后它会冻结。它需要一些调试(或使用 print())来找出问题所在。


编辑: 如果我在开始时添加两个敌人,我会遇到同样的问题。问题可能是它可能 运行 两次 wn.ontimer 可能 运行 另外两次 wn.ontimer 所以最后它可能没有时间执行它们。


编辑: 如果我在函数末尾只使用 wn.ontimer(enemy_attack, 10) 并删除其他 enemy_attack(),代码会更好。但这次敌人大多同时出现,当我杀死其中一个时,它会移除所有敌人。它可能需要更多的改变。

def enemy_attack():
    global game_over
    for enemy in enemies:
        if enemy.isvisible() == True:
            enemy.setheading(enemy.towards(player))
            enemy.forward(enemy_speed)
        else:
            if game_over == False:
                rand_direction = randint(0,3)
                if rand_direction == 0:
                    x = randint(-240, 200)
                    enemy.goto(x,450)
                    enemy.st()
                    #enemy_attack()
                elif rand_direction == 1:
                    x = randint(-240, 200)
                    enemy.goto(x,-450)
                    enemy.st()
                    #enemy_attack()
                elif rand_direction == 2:
                    y = randint(-210, 210)
                    enemy.goto(-450,y)
                    enemy.st()
                    #enemy_attack()
                elif rand_direction == 3:
                    y = randint(-210, 210)
                    enemy.goto(450,y)
                    enemy.st()
                    #enemy_attack()

    wn.ontimer(enemy_attack, 10)

我看到的主要问题是@furas 提到的那些问题:enemy_attack() 递归地和通过计时器调用自身; turtle.Turtle 调用时不带括号。我认为以任何一种方式调用 enemy_attack() 都不是一个好主意,而应该在主程序循环中调用它。

使用 ontimer() 的一个问题是您需要确保您使用的超时时间大于执行函数所需的时间。除非它被设计为同时 运行 自身的多个实例。鉴于它写了一个全局,那么它可能一次只能写一个。

在像 turtle 这样的基于事件的环境中,你不应该 while True:(也不应该 time.sleep())。 ontimer() 方法应该同时处理两者。

Python-明智的,你不应该这样做:

if bullet.isvisible() == False:
    if enemy.isvisible() == True:
        if game_over == False:
if shooting == True:

改为:

if not bullet.isvisible():
    if enemy.isvisible():
        if not game_over:
if shooting:

下面,我已经拆开您的代码并按照我希望设计基于海龟的游戏的方式重新组装它。可能还有bug,不过现在好像可以玩了:

from turtle import Screen, Turtle
from random import randint

ENEMY_SPEED = 1
PLAYER_SPEED = 4
BULLET_SPEED = 6

SMALL_FONT = ('Courier', 15, 'normal')
LARGE_FONT = ('Courier', 24, 'normal')

def go_up():
    player.setheading(90)
    player.forward(PLAYER_SPEED)

def go_right():
    player.setheading(0)
    player.forward(PLAYER_SPEED)

def go_left():
    player.setheading(180)
    player.forward(PLAYER_SPEED)

def go_down():
    player.setheading(-90)
    player.forward(PLAYER_SPEED)

def enemy_attack():

    for enemy in enemies:

        if enemy.isvisible():
            enemy.setheading(enemy.towards(player))
            enemy.forward(ENEMY_SPEED)
        else:
            rand_direction = randint(0, 3)

            if rand_direction == 0:
                x = randint(-240, 200)
                enemy.goto(x, 325)
            elif rand_direction == 1:
                x = randint(-240, 200)
                enemy.goto(x, -325)
            elif rand_direction == 2:
                y = randint(-210, 210)
                enemy.goto(-325, y)
            elif rand_direction == 3:
                y = randint(-210, 210)
                enemy.goto(325, y)

            enemy.showturtle()

def shoot():
    if not bullet.isvisible():
        bullet.goto(player.position())
        bullet.setheading(player.heading())
        bullet.showturtle()

kill_counter = 0
enemy_spawn_number = 5
lives = 3

def move():
    global kill_counter, enemy_spawn_number, lives

    enemy_attack()

    if player.distance(collectible) < 20:
        collectible.goto(randint(-260, 260), randint(-260, 260))

    if bullet.isvisible():
        if not (-310 < bullet.xcor() < 310 and -310 < bullet.ycor() < 310):
            bullet.hideturtle()
        else:
            bullet.forward(BULLET_SPEED)

    playerX, playerY = player.position()

    if playerX > 285:
        player.setx(playerX - 5)
    elif playerX < -285:
        player.setx(playerX + 5)
    elif playerY > 285:
        player.sety(playerY - 5)
    elif playerY < -285:
        player.sety(playerY + 5)

    for enemy in enemies:
        if bullet.isvisible() and bullet.distance(enemy) < 15:
            bullet.hideturtle()
            enemy.hideturtle()
            enemy.sety(325)

            kill_counter += 1
            killcount.clear()
            killcount.write("Kill Count: {}".format(kill_counter), align='right', font=SMALL_FONT)

            if kill_counter == enemy_spawn_number:
                enemy = enemy_prototype.clone()
                enemy.showturtle()

                enemies.append(enemy)

                for enemy in enemies:
                    enemy.sety(325)

                enemy_spawn_number += 5

        if enemy.distance(player) < 20:
            enemy.hideturtle()
            player.goto(0, 0)

            lives -= 1

            if lives == 2:
                life3.hideturtle()
            elif lives == 1:
                life2.hideturtle()
            elif lives == 0:
                life1.hideturtle()

    if lives == 0:
        collectible.hideturtle()
        player.hideturtle()

        for enemy in enemies:
            enemy.hideturtle()

        over_message.write("Game Over! Press Esc to exit.", align='center', font=LARGE_FONT)
    else:
        screen.ontimer(move, 70)

    screen.update()

screen = Screen()
screen.setup(width=600, height=600)
screen.title("Defend.")
screen.bgcolor('black')
screen.tracer(0)

player = Turtle()
player.shape('triangle')
player.color('white')
player.penup()

life1 = Turtle()
life1.shape('triangle')
life1.color('yellow')
life1.penup()
life1.goto(265, -260)
life1.setheading(-90)

life2 = life1.clone()
life2.goto(240, -260)

life3 = life2.clone()
life3.goto(215, -260)

over_message = Turtle()
over_message.hideturtle()
over_message.color('white')
over_message.penup()

killcount = over_message.clone()
killcount.goto(270, 270)
killcount.write("Kill Count: 0", align='right', font=SMALL_FONT)

collectible = Turtle()
collectible.shape('square')
collectible.color('blue')
collectible.penup()
collectible.goto(randint(-260, 260), randint(-260, 260))

bullet = Turtle()
bullet.hideturtle()
bullet.shape('square')
bullet.shapesize(0.5, 1)
bullet.color('grey')
bullet.penup()
bullet.goto(500, 500)

enemy_prototype = Turtle()
enemy_prototype.hideturtle()
enemy_prototype.shape('circle')
enemy_prototype.color('red')
enemy_prototype.penup()
enemy_prototype.sety(325)

enemy = enemy_prototype.clone()
enemy.showturtle()

enemies = [enemy]

screen.onkeypress(go_up, 'w')
screen.onkeypress(go_right, 'd')
screen.onkeypress(go_left, 'a')
screen.onkeypress(go_down, 's')
screen.onkeypress(screen.bye, 'Escape')
screen.onkeypress(shoot, 'space')
screen.listen()

move()

screen.mainloop()

我认为您应该解决的下一个问题是代码中阻止您调整屏幕大小的所有数字常量:

life1.goto(265,-260)
life2.goto(240,-260)
life3.goto(215,-260)
killcount.goto(250,250)
x = randint(-240, 200)
x = randint(-240, 200)
y = randint(-210, 210)
y = randint(-210, 210)
x = randint(-290, 290)
y = randint(-290, 290)
if bullet.xcor()>310 or bullet.xcor()<-310 or bullet.ycor()>310 or bullet.ycor()<-300:
if player.xcor()>285:
elif player.xcor()<-285:
elif player.ycor()>285:
elif player.ycor()<-285:

这些都应该相对于屏幕尺寸来定义:

screen.setup(width=600, height=600)

以及他们操纵的对象。 (即代码中没有大数字!)