3D-элемент управления Pyglet не работает должным образом

#python-3.x #opengl #3d #pyglet

#python-3.x #opengl #3D #pyglet

Вопрос:

В настоящее время я изучаю основы pyglet и opengl. Я просмотрел несколько руководств и веб-сайтов по изучению pyglet и opengl, и я подумал, что мог бы создать простую 3D-игру. Однако я застрял на элементах управления движением камеры и, похоже, не могу понять, что не так с кодом. Когда я нажимаю, S камера нормально перемещается назад, но затем, когда я нажимаю W , она не перемещается, и если я нажимаю ее снова, она перемещается, но немного быстрее, чем раньше. Почему это происходит?

 from pyglet.gl import *
from pyglet.window import key
import math

class Triangle:
    def __init__(self):
        self.vertices = pyglet.graphics.vertex_list(3, ('v3f', [-0.5, -0.5, 0.0,      0.5, -0.5, 0.0,      0.0, 0.5, 0.0]),
                                                       ('c3B', [100, 200, 220,        200, 110, 100,       100, 250, 100]))

class Quad:
    def __init__(self):
        self.vertices = pyglet.graphics.vertex_list_indexed(4, [0, 1, 2,    2, 3, 0],
                                                            ('v3f', [-0.5, -0.5, 0.0,      0.5, -0.5, 0.0,      0.5, 0.5, 0.0,      -0.5, 0.5, 0.0]),
                                                            ('c3f', [1.0, 0.0, 0.0,    0.0, 1.0, 0.0,   0.0, 0.0, 1.0,    1.0, 1.0, 0.0]))

class Player:
    def __init__(self):
        self.position = [0, 0, 0]
        self.rotation = [0, 0]
        
        self.change_x = 0
        self.change_z = 0
        self.speed = 1
    
    def controls(self, keys):
        self.change_x = 0
        self.change_z = 0
    
        if keys == key.W:
            self.change_z = self.speed
        if keys == key.S:
            self.change_z = -self.speed
        if keys == key.A:
            self.change_x = -self.speed
        if keys == key.D:
            self.change_x = self.speed
        
        self.position[0]  = self.change_x
        self.position[2]  = self.change_z

class Window(pyglet.window.Window):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_minimum_size(300, 300)
        glClearColor(0.2, 0.3, 0.2, 1.0)
        gluPerspective(90, 0.5, 0.01, 1000)
        self.quad = Quad()
        
        self.player = Player()
    
    def on_key_press(self, KEY, MOD):
        self.player.controls(KEY)
    
    def on_draw(self):
        self.clear()
        glTranslatef(*self.player.position)
        self.quad.vertices.draw(GL_TRIANGLES)
    
    def on_resize(self, width, height):
        glViewport(0, 0, width, height)

if __name__ == '__main__':
    window = Window(500, 500, 'My Window', resizable = True)
    pyglet.app.run()
  

Ответ №1:

Вероятно, вы неправильно поняли glTranslate функцию.

glTranslate работает таким образом, что он принимает текущую матрицу и умножает ее на матрицу преобразования, заданную параметрами glTranslate .

Итак, вызов glTranslate(x, y, z) приводит к следующему уравнению:

 new_position = old_position   (x, y, z)
  

Что делает (x, y, z), если вы вызываете glTranslate каждый кадр, скорость изменения (т. Е. скорость).

Почему он останавливается, когда я нажимаю S и W?

Что ж, давайте взглянем на код и уравнение. Вы используете self.position в качестве параметра для glTranslate . Итак, ваше self.position значение — это не положение, а текущая скорость (или скорость изменения) камеры.

Если вы нажмете S один раз, on_key_press будет вызван, со следующими эффектами:

 self.change_z = -self.speed
self.position[2]  = self.change_z # self.position[2] equals now -self.speed
  

Теперь нажмите W один раз, еще раз on_key_press будет вызван снова, в результате чего это:

 self.change_z =  self.speed
self.position[2]  = self.change_z # self.position[2] equals now 0, because self.position[2] was -self.speed; -self.speed   self.speed = 0
  

Подразумевается, что каждый раз, когда вы нажимаете любую клавишу управления, вы дискретно ускоряете свою камеру.

Предполагая, что вы нажали S дважды после запуска программы, это приведет к self.position[2] равному 2 * -self.speed .

Как это исправить?

Измените следующее

 self.position[0]  = self.change_x
self.position[2]  = self.change_z
  

Для

 self.position[0] = self.change_x
self.position[2] = self.change_z
  

Я действительно надеюсь, что прояснил, почему self.position на самом деле это скорость, а не позиция. Я предлагаю вам переименовать self.position в self.velocity или что-то еще в этом роде.


В качестве альтернативы вы можете перестраивать матрицу каждый раз, когда меняете свою позицию, выполнив следующее:

     def on_draw(self):
        self.clear()
        glLoadIdentity()
        gluPerspective(90, 0.5, 0.01, 1000)
        glTranslatef(*self.player.position)
        self.quad.vertices.draw(GL_TRIANGLES)
  

Я не могу не дать следующий шаг. Тот факт, что self.position это просто скорость (в вашей оригинальной версии!), становится очевидным, если вы добавите другой метод:

 class Window(...):

    # ...

    def update(self, dt):
        self.clear()
        glTranslatef(*self.player.position)
        self.quad.vertices.draw(GL_TRIANGLES)
  

и расширьте Window.__init__ следующими строками:

 class Window(...):
    def __init__(...):
        # ...

        import pyglet.clock
        pyglet.clock.schedule_interval(self.update, 1/60)
        # pyglet now calls update(...) 60 times a second
    

  

Комментарии:

1. Спасибо за ответ. Однако теперь, когда я нажимаю клавиши, экран становится фиолетовым или зеленым.