Как исправить несовместимую частоту кадров (скорость) в Python Turtle - PullRequest
2 голосов
/ 03 апреля 2019

Я сделал игру понг, следуя этому уроку https://youtu.be/C6jJg9Zan7w Проблема, с которой я столкнулся, заключается в том, что скорость мяча (объекта черепахи) не одинакова на разных компьютерах.Например, на компьютере преподавателя учебного пособия значение ball.dx ball.dy равно 2, а скорость мяча нормальная, но на моем компьютере она действительно высокая, поэтому мне пришлось установить ее на 0,1.Я думаю, что эта проблема заключается в том, что разные компьютеры могут выдавать более или менее кадр.Я знаю, что в популярных игровых движках есть метод (например, unity-unreal), который использует время вместо фреймов, чтобы переменные были глобальными на разных компьютерах.Еще одна проблема, с которой я столкнулся, заключается в том, что при перемещении лопастей скорость мяча слегка меняется.Я не знаю, если это та же проблема, что и выше

import turtle
import winsound

wn = turtle.Screen()
wn.title('Pong')
wn.bgcolor('black')
wn.setup(width=800, height=600)
wn.tracer(0)

# Paddle A
paddle_a = turtle.Turtle()
paddle_a.speed(0)
paddle_a.shape('square')
paddle_a.color('white')
paddle_a.penup()
paddle_a.goto(-350, 0)
paddle_a.shapesize(5, 1)

# Paddle B
paddle_b = turtle.Turtle()
paddle_b.speed(0)
paddle_b.shape('square')
paddle_b.color('white')
paddle_b.penup()
paddle_b.goto(350, 0)
paddle_b.shapesize(5, 1)

# Ball
ball = turtle.Turtle()
ball.speed(0)
ball.shape('square')
ball.color('white')
ball.penup()
ball.dx = 0.15
ball.dy = 0.15

# Pen
pen = turtle.Turtle()
pen.speed(0)
pen.color('white')
pen.penup()
pen.goto(0, 260)
pen.write("Player A: 0  Player B: 0", align='center', font=('Courier', 24, 'bold'))
pen.hideturtle()

# Score
score_a = 0
score_b = 0

def paddle_a_up():
    y = paddle_a.ycor()
    y += 20
    paddle_a.sety(y)

def paddle_b_up():
    y = paddle_b.ycor()
    y += 20
    paddle_b.sety(y)

def paddle_a_down():
    y = paddle_a.ycor()
    y += -20
    paddle_a.sety(y)

def paddle_b_down():
    y = paddle_b.ycor()
    y += -20
    paddle_b.sety(y)

# Keyboard binding
wn.listen()
wn.onkeypress(paddle_a_up, 'w')
wn.onkeypress(paddle_a_down, 's')
wn.onkeypress(paddle_b_up, 'Up')
wn.onkeypress(paddle_b_down, 'Down')


# Main game loop
while True:
    wn.update()

    # Moving Ball
    ball.setx(ball.xcor() + ball.dx)
    ball.sety(ball.ycor() + ball.dy)

    # Border checking
    if ball.ycor() > 290 or ball.ycor() < -290:
        winsound.PlaySound('bounce.wav', winsound.SND_ASYNC)
        ball.dy *= -1

    if ball.xcor() > 390:
        winsound.PlaySound('bounce.wav', winsound.SND_ASYNC)
        ball.goto(0, 0)
        ball.dx *= -1
        score_a += 1
        pen.clear()
        pen.write("Player A: {}  Player B: {}".format(score_a, score_b), align='center', font=('Courier', 24, 'bold'))

    if ball.xcor() < -390:
        winsound.PlaySound('bounce.wav', winsound.SND_ASYNC)
        ball.goto(0, 0)

        ball.dx *= -1
        score_b += 1
        pen.clear()
        pen.write("Player A: {}  Player B: {}".format(score_a, score_b), align='center', font=('Courier', 24, 'bold'))

    # Paddle and ball collisions
    if (ball.xcor() > 340 and ball.xcor() < 350) and (ball.ycor() < paddle_b.ycor() + 60 and ball.ycor() > paddle_b.ycor() -60):
        winsound.PlaySound('bounce.wav', winsound.SND_ASYNC)
        ball.setx(340)
        ball.dx *= -1

    if (ball.xcor() < -340 and ball.xcor() > -350) and (ball.ycor() < paddle_a.ycor() + 60 and ball.ycor() > paddle_a.ycor() -60):
        winsound.PlaySound('bounce.wav', winsound.SND_ASYNC)
        ball.setx(-340)
        ball.dx *= -1

Ответы [ 2 ]

0 голосов
/ 04 апреля 2019

При анимации, особенно если вы хотите контролировать скорость анимации, вам нужно придерживаться принципа разделения вида модели.Это означает, что у вас есть одна часть программы, которая контролирует состояние объектов (модель), а другая часть, которая рисует объекты (представление).Состояние объектов может включать в себя положение, заголовок, цвет и другие свойства.Эта часть программы, которая изменяет состояния объектов, не связана с рисованием объектов.Рисование выполняется отдельной частью программы, которая проверяет состояния объектов и отображает их на экране.

Ответ cdlane смешанная модель и вид.Внутри одной функции таймера состояния объектов изменяются, а также отображаются (что может занять много времени процессора).Это приведет к задержке следующего вызова этой функции.

Мое решение состоит в том, чтобы функции move_balls() меняли положение только шаров (без их отрисовки), которые будут возвращаться практически без задержки, сохраняя временной интервал постоянным.Затем вы помещаете код для рисования объектов в обработчик таймера с минимально возможной задержкой.Чтобы избежать ненужного рисования, создайте глобальную переменную should_draw, которая немедленно возвращает значение false, не тратя время процессора.После выполнения функции move_balls() объекты будут нуждаться в рисовании.Поэтому убедитесь, что установлено значение should_draw в true, прежде чем вернуться из move_balls().Кроме того, после завершения рисования убедитесь, что should_draw установлено на False.(эта часть отредактирована после обсуждения с cdlane)

Это должно позволить вам рисовать множество объектов с плавной анимацией.Посмотрите это видео на Youtube, на котором сотни светлячков освещают каждую (почти точно) 5-ю секунду.В описании видео также есть учебник.

https://www.youtube.com/watch?v=K9D-wO4w_k0

Вот учебник с полным кодом: https://pythonturtle.academy/tutorial-fireflies/

0 голосов
/ 03 апреля 2019

Твоему while True: нет места в таких событиях, как черепаха. Замена первого уровня будет заключаться в том, чтобы поместить тело вашего цикла в функцию, которую вы вызываете с помощью ontimer(). Это вызовет вашу процедуру через фиксированные интервалы (обязательно включите вызов ontimer() в качестве последнего шага, так как это однократный выстрел). Что-то вроде:

def move_balls():
    global score_a, score_b

    wn.update()

    # Moving Ball
    ball.setx(ball.xcor() + ball.dx)
    ball.sety(ball.ycor() + ball.dy)

    # Border checking
    if ball.ycor() > 290 or ball.ycor() < -290:
        winsound.PlaySound('bounce.wav', winsound.SND_ASYNC)
        ball.dy *= -1

    if ball.xcor() > 390:
        winsound.PlaySound('bounce.wav', winsound.SND_ASYNC)
        ball.goto(0, 0)
        ball.dx *= -1
        score_a += 1
        pen.clear()
        pen.write("Player A: {}  Player B: {}".format(score_a, score_b), align='center', font=('Courier', 24, 'bold'))

    elif ball.xcor() < -390:
        winsound.PlaySound('bounce.wav', winsound.SND_ASYNC)
        ball.goto(0, 0)
        ball.dx *= -1
        score_b += 1
        pen.clear()
        pen.write("Player A: {}  Player B: {}".format(score_a, score_b), align='center', font=('Courier', 24, 'bold'))

    # Paddle and ball collisions
    if (340 < ball.xcor() < 350) and (paddle_b.ycor() - 60 < ball.ycor() < paddle_b.ycor() + 60):
        winsound.PlaySound('bounce.wav', winsound.SND_ASYNC)
        ball.setx(340)
        ball.dx *= -1

    elif (-350 < ball.xcor() < -340) and (paddle_a.ycor() - 60 < ball.ycor() < paddle_a.ycor() + 60):
        winsound.PlaySound('bounce.wav', winsound.SND_ASYNC)
        ball.setx(-340)
        ball.dx *= -1

    wn.ontimer(move_balls, 100)

move_balls()

Однако таймер контролирует только время между окончанием одного вызова move_balls() и началом другого. Он не учитывает время, проведенное в самом методе. Если вам нужно более точное управление, тогда вам может понадобиться что-то вроде высокоточных кадров в секунду .

...