Valentino Ito
Valentino Ito

Reputation: 23

Pyglet 3d control not working as intended

I am currently learning the basics of pyglet and opengl. I have watched a number of tutorials and websites to learn pyglet and opengl, and I thought I would make a simple 3d game. However I am stuck on the camera movement controls and I can't seem to figure out what is wrong with the code. When I press S the camera moves back normally, but then when I press W it doesn't move, and if I press it again it moves but a little faster than before. Why is this happening?

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()

Upvotes: 2

Views: 132

Answers (1)

Julian Kirsch
Julian Kirsch

Reputation: 754

You probably misunderstood the glTranslate function.

glTranslate works in such a way, that it takes the current matrix and multiplies with a translation matrix given by parameters of glTranslate.

So, calling glTranslate(x, y, z) results in the following equation:

new_position = old_position + (x, y, z)

Which makes (x, y, z), if you call glTranslate every frame, the rate of change (i.e. the velocity).

Why does it stop when I press S and the W?

Well, let's take a look at the code and the equation. You use self.position as parameter for glTranslate. So, your self.position is not the position but the current velocity (or rate of change) of the camera.

If you press S once, on_key_press will be called, with following effects:

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

Now press W once, yet again on_key_press will be called again, resulting in this:

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

The implication is that every time you press either control key, you accelerate your camera discretely.

Assuming you pressed S twice after starting the program would cause self.position[2] to equal to 2 * -self.speed.

How to fix this?

Change the following

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

to

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

I really hope I made it clear, why self.position is actually a velocity and not the position. I suggest you rename self.position to self.velocity or something else along the lines.


Alternatively you can rebuild the matrix every time you change your position by doing the following:

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

I cannot help but give a further step. The fact that self.position is just a velocity (in your original version!), becomes apparent if you add a another method:

class Window(...):

    # ...

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

and extend Window.__init__ with the following lines:

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

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

Upvotes: 1

Related Questions