Pyglet on_key_press and on_mouse_motion are being very inconsistent - python

Background
I've been toying around with pyglet when I stumbled across this guy's Minecraft clone. Github is here: https://github.com/fogleman/Minecraft
I've made some modifications (for Python 3 and some of my own preferences), and the complete code is here: modified minecraft.
The Problem
Whenver I run the code, it may sometimes not register any mouse movements or key presses. It is rare, but it can happen occasionally. I would say that out of 10 times, it'll break once.
Details
I don't even know what the culprit is, but I'll provide some snippets code.
It's unpredictable, but there are some ways to fix it. The only sure-fire way currently is to FORCE QUIT (not just quit) the application and then restart it.
I'm not sure why, and I've tried all sorts of things to try and fix it.
If it matters, I'm using macOS Mojave, Python 3.8.2, and Pyglet 1.5.14
Here's the __init__ function for the window:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Whether or not the window exclusively captures the mouse.
self.exclusive = False
# When flying gravity has no effect and speed is increased.
self.flying = False
# Strafing is moving lateral to the direction you are facing,
# e.g. moving to the left or right while continuing to face forward.
#
# First element is -1 when moving forward, 1 when moving back, and 0
# otherwise. The second element is -1 when moving left, 1 when moving
# right, and 0 otherwise.
self.strafe = [0, 0]
# Current (x, y, z) position in the world, specified with floats. Note
# that, perhaps unlike in math class, the y-axis is the vertical axis.
self.position = (0, 0, 0)
# First element is rotation of the player in the x-z plane (ground
# plane) measured from the z-axis down. The second is the rotation
# angle from the ground plane up. Rotation is in degrees.
#
# The vertical plane rotation ranges from -90 (looking straight down) to
# 90 (looking straight up). The horizontal rotation range is unbounded.
self.rotation = (0, 0)
# Which sector the player is currently in.
self.sector = None
# The crosshairs at the center of the screen.
self.reticle = None
# Velocity in the y (upward) direction.
self.dy = 0
# A list of blocks the player can place. Hit num keys to cycle.
self.inventory = [BRICK, GRASS, SAND]
# The current block the user can place. Hit num keys to cycle.
self.block = self.inventory[0]
# Convenience list of num keys.
self.num_keys = [
key._1, key._2, key._3, key._4, key._5,
key._6, key._7, key._8, key._9, key._0]
# Instance of the model that handles the world.
self.model = Model()
# The label that is displayed in the top left of the canvas.
self.label = pyglet.text.Label('', font_name='Arial', font_size=18,
x=10, y=self.height - 10, anchor_x='left', anchor_y='top',
color=(0, 0, 0, 255))
# This call schedules the `update()` method to be called
# TICKS_PER_SEC. This is the main game event loop.
pyglet.clock.schedule_interval(self.update, 1.0 / TICKS_PER_SEC)
Here's the input handlers:
def on_mouse_press(self, x, y, button, modifiers):
""" Called when a mouse button is pressed. See pyglet docs for button
amd modifier mappings.
Parameters
----------
x, y : int
The coordinates of the mouse click. Always center of the screen if
the mouse is captured.
button : int
Number representing mouse button that was clicked. 1 = left button,
4 = right button.
modifiers : int
Number representing any modifying keys that were pressed when the
mouse button was clicked.
"""
if self.exclusive:
vector = self.get_sight_vector()
block, previous = self.model.hit_test(self.position, vector)
if (button == mouse.RIGHT) or \
((button == mouse.LEFT) and (modifiers & key.MOD_CTRL)):
# ON OSX, control + left click = right click.
if previous:
self.model.add_block(previous, self.block)
if button == pyglet.window.mouse.LEFT and block:
texture = self.model.world[block]
self.model.remove_block(block)
else:
self.set_exclusive_mouse(True)
def on_mouse_motion(self, x, y, dx, dy):
""" Called when the player moves the mouse.
Parameters
----------
x, y : int
The coordinates of the mouse click. Always center of the screen if
the mouse is captured.
dx, dy : float
The movement of the mouse.
"""
if self.exclusive:
m = 0.15
x, y = self.rotation
x, y = x + dx * m, y + dy * m
y = max(-90, min(90, y))
self.rotation = (x, y)
def on_key_press(self, symbol, modifiers):
if symbol == key.W:
self.strafe[0] -= 1
if symbol == key.S:
self.strafe[0] += 1
if symbol == key.A:
self.strafe[1] -= 1
if symbol == key.D:
self.strafe[1] += 1
if symbol == key.SPACE:
if self.dy == 0:
self.dy = JUMP_SPEED
if symbol == key.ESCAPE:
self.set_exclusive_mouse(False)
if symbol == key.TAB:
self.flying = not self.flying
if symbol in self.num_keys:
index = (symbol - self.num_keys[0]) % len(self.inventory)
self.block = self.inventory[index]
And finally, here's the setup:
""" Configure the OpenGL fog properties.
"""
# Enable fog. Fog "blends a fog color with each rasterized pixel fragment's
# post-texturing color."
glEnable(GL_FOG)
# Set the fog color.
glFogfv(GL_FOG_COLOR, (GLfloat * 4)(0.5, 0.69, 1.0, 1))
# Say we have no preference between rendering speed and quality.
glHint(GL_FOG_HINT, GL_DONT_CARE)
# Specify the equation used to compute the blending factor.
glFogi(GL_FOG_MODE, GL_LINEAR)
# How close and far away fog starts and ends. The closer the start and end,
# the denser the fog in the fog range.
glFogf(GL_FOG_START, 50.0)
glFogf(GL_FOG_END, 100.0)
def setup():
""" Basic OpenGL configuration.
"""
# Set the color of "clear", i.e. the sky, in rgba.
glClearColor(0.5, 0.69, 1.0, 1)
# Enable culling (not rendering) of back-facing facets -- facets that aren't
# visible to you.
glEnable(GL_CULL_FACE)
# Set the texture minification/magnification function to GL_NEAREST (nearest
# in Manhattan distance) to the specified texture coordinates. GL_NEAREST
# "is generally faster than GL_LINEAR, but it can produce textured images
# with sharper edges because the transition between texture elements is not
# as smooth."
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
setup_fog()
def main():
window = Window(width=800, height=600, caption='Minecraft', resizable=True)
# Hide the mouse cursor and prevent the mouse from leaving the window.
setup()
if __name__ == '__main__':
main()
pyglet.app.run()
Here's texture.png:
Here's an example of what's happening (the black circles means I've clicked the mouse, and at the end, I was rapidly pressing W):
gif
What I've Done
Here is what I've done so far:
Looked at the Pyglet Docs
Researched on google with various phrasing and keywords

It sounds like it could be a Mac related issue. Here is a bug report on something similar happening with Mac: https://github.com/pyglet/pyglet/issues/225
One thing I would try is just try a barebones setup and see if the problems persists with the minimal code. If this still occurs, there is most likely a bug in the Mac/Pyglet interaction. If the basic sample works, there might be a bug in the Minecraft example.
import pyglet
window = pyglet.window.Window()
#window.event
def on_draw():
print('on_draw')
window.clear()
#window.event
def on_mouse_motion(x, y, dx, dy):
print('on_mouse_motion', x, y, dx, dy)
#window.event
def on_key_press(symbol, modifiers):
print('on_key_press', symbol, modifiers)
pyglet.app.run()

Related

Pyglet on_draw wont draw updated function

The crtaj() function (the main drawing function) takes two global matrices, "T" and "P", applies transformations on the global vertices "vrhovi" using the two matrices and stores the transformed vertices into "novivrhovi", and then draws the polygon using the transformed vertices "novivrhovi" and global "poligoni" (describes the connections of vertices). When the key "up" is pressed, the matrices "T" and "P" are updated. What i want, is to draw on every one of these updates, but after the initial draw the screen goes blank after first "up" is pressed. I am 100% certain my transformations are okay, because pressing the key up once gives matrices for which i have tried to set to be initial , and it draws it correctly so no reason not to draw correctly when using default initial and pressing up, because the result is the same and the function even prints correct vertices every time I press "up" but just doesn't show anything.
#window.event
def on_draw():
glScalef(150,150,150)
glTranslatef(sum(xkord)/2,sum(ykord)/2,0)
window.clear()
crtaj()
#window.event
def on_key_press(key, modifiers):
global vrhovi
if (key == pyglet.window.key.UP):
ociste[0][0]=ociste[0][0]+1
transform()
on_draw()
#main draw
def crtaj():
glBegin(GL_LINE_LOOP)
global T
global P
global vrhovi
global poligoni
#calculate new vertices of polygon-->novivrhovi
novivrhovi = []
for vrh in vrhovi:
novivrh = vrh.copy()
novivrh.append(1)
novivrh = np.matrix(novivrh)
novivrh = novivrh.dot(T)
novivrh = novivrh.dot(P)
novivrh = novivrh.tolist()
novivrhovi.append(novivrh[0])
print("N O V I V R H O V I")
print(novivrhovi)
#draw the poligon
for poligon in poligoni:
index0 = poligon[0]-1
index1 = poligon[1]-1
index2 = poligon[2]-1
glVertex4f(novivrhovi[index0][0],novivrhovi[index0][1],novivrhovi[index0][2],novivrhovi[index0][3])
glVertex4f(novivrhovi[index1][0],novivrhovi[index1][1],novivrhovi[index1][2],novivrhovi[index1][3])
glVertex4f(novivrhovi[index2][0],novivrhovi[index2][1],novivrhovi[index2][2],novivrhovi[index2][3])
glEnd()
pyglet.app.run()
but after the initial draw the screen goes blank after first "up" is pressed.
The Legacy OpenGL matrix operations like glScalef and glTranslatef do not just set a matrix, they define a new matrix and multiply the current matrix by the new matrix.
OpenGL is a state engine, states are kept until they are changed again, even beyond frames. Hence in your application the current matrix is progressively scaled and translated.
Load the Identity matrix at the begin of on_draw:
#window.event
def on_draw():
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glScalef(150,150,150)
glTranslatef(sum(xkord)/2,sum(ykord)/2,0)
window.clear()
crtaj()
PyGlet sets by default an Orthographic projection projection matrix to window space. See pyglet.window. If you want a different projection, it can be set by glOrtho:
#window.event
def on_draw():
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0, window.width, window.height, 0, -1, 1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glScalef(150,150,150)
glTranslatef(sum(xkord)/2,sum(ykord)/2,0)
window.clear()
crtaj()

Get coordinates of every point in mouse movements

I need all mouse movement coordinates: as the mouse moves, I need a continuous sequence of (x,y) values. My current code leaves gaps:
def paint(self, event):
self.line_width = self.choose_size_button.get()
paint_color = 'white' if self.eraser_on else self.color
coord = (event.x, event.y)
if self.old_x == None or self.old_y == None:
else:
print(coord)
paint_color = self.color
self.c.create_line(self.old_x, self.old_y, event.x, event.y,
width=self.line_width, fill=paint_color,
capstyle=ROUND, smooth=TRUE, splinesteps=36)
coord is incomplete: it "jumps" some points, maybe because the mouse moves too fast and so it lost some points. How can I record all of the points in transit?
You can't record input with any finer grain than the sampling rate of the device. If you're already set up for fine-grained events on mouse movements, that's the best you can do in that respect.
However, if you need contiguous points for some reason, you can interpolate the missing points to make a connected path. The simple way is to connect the dots with Bresenham's line algorithm. I recommend that you use something to smooth the curve; there are several packages that will fit a nice curve to a sequence of points; look for the term "spline" as well.

Drawing convexity polygon in Pyglet

Is it possible to draw convexity polygon with Pyglet?
If yes, how can I draw it? I only know n vertex and their 2D coordinates.
You could use the primitive GL_POLYGON (or even try GL_LINE_LOOP).
Check documentation...
https://pyglet.readthedocs.io/en/pyglet-1.2-maintenance/api/pyglet/pyglet.graphics.html
http://pyglet.readthedocs.io/en/pyglet-1.3-maintenance/programming_guide/graphics.html
... and an example
Pyglet GL_QUADS and GL_POLYGON not working properly
Yes, cou can do it.
Just take the following code snippets to get a feeling for the functionality.
Play a little bit with the modes:
GL_POLYGON,
GL_TRIANGLES,
GL_TRIANGLE_FAN and
GL_TRIANGLE_STRIP.
Left mouse click button increases points in window. First drawing is shown after clicking three times. Del-Button clears the window.
GL_POLYGON and GL_TRIANGLE_FAN behave in the same way, the first point is the anchor point of the convex polygon and is used for every drawn triangle from the points list.
GL_TRIANGLE takes 3 points for drawing a triangle, then the next 3 points and so on.
With GL_TRIANGLE_STRIP you can draw every complex structure. But there you have be careful for the given points. Sometimes, you have to visit a point more than one time.
The first triangle is drawn by points 1-3.
The second triangle is drawn by points 2-4, then 3-5, 4-6 and so on.
import pyglet
from pyglet.window import key
global n
global vertices
global colors
window = pyglet.window.Window()
n = 0
vertices = []
colors = []
polygon = None
main_Batch = pyglet.graphics.Batch()
#window.event
def on_draw():
window.clear()
main_Batch = pyglet.graphics.Batch()
if n > 2:
polygon = main_Batch.add(n, pyglet.gl.GL_POLYGON, None,
('v2i', vertices),
('c3B', colors))
main_Batch.draw()
#window.event
def on_key_press(symbol, modifiers):
if symbol == key.DELETE:
global n;
global vertices
global colors
vertices = []
colors = []
n = 0
#window.event
def on_mouse_press(x, y, button, modifiers):
if button == pyglet.window.mouse.LEFT:
global n
vertices.append(x)
vertices.append(y)
n = n + 1
colors.append(255)
colors.append(255)
colors.append(255)
pyglet.app.run()

pygame - Snap Mouse to Grid

I'm making a little platformer game using pygame, and decided that making a level editor for each level would be easier than typing each blocks' coordinate and size.
I'm using a set of lines, horizontally and vertically to make a grid to make plotting points easier.
Here's the code for my grid:
def makeGrid(surface, width, height, spacing):
for x in range(0, width, spacing):
pygame.draw.line(surface, BLACK, (x,0), (x, height))
for y in range(0, height, spacing):
pygame.draw.line(surface, BLACK, (0,y), (width, y))
I want the user's mouse to move at 10px intervals, to move to only the points of intersection. Here's what I tried to force the mouse to snap to the grid.
def snapToGrid(mousePos):
if 0 < mousePos[0] < DISPLAYWIDTH and 0 < mousePos[1] < 700:
pygame.mouse.set_pos(roundCoords(mousePos[0],mousePos[1]))
(BTW, roundCoords() returns the coordinates rounded to the nearest ten unit.)
(Also BTW, snapToGrid() is called inside the main game loop (while not done))
...but this happens, the mouse doesn't want to move anywhere else.
Any suggestions on how to fix this? If I need to, I can change the grid code too.
Thanks a bunch.
P.S. This is using the latest version of PyGame on 64 bit Python 2.7
First of all I think you're not far off.
I think the problem is that the code runs quite fast through each game loop, so your mouse doesn't have time to move far before being set to the position return by your function.
What I would have a look into is rather than to pygame.mouse.set_pos() just return the snapped coordinates to a variable and use this to blit a marker to the screen highlighting the intersection of interest (here I use a circle, but you could just blit the image of a mouse ;) ). And hide your actual mouse using pygame.mouse.set_visible(False):
def snapToGrid(mousePos):
if 0 < mousePos[0] < DISPLAYWIDTH and 0 < mousePos[1] < 700:
return roundCoords(mousePos[0],mousePos[1])
snap_coord = snapToGrid(mousePos)# save snapped coordinates to variable
pygame.draw.circle(Surface, color, snap_coord, radius, 0)# define the remaining arguments, Surface, color, radius as you need
pygame.mouse.set_visible(False)# hide the actual mouse pointer
I hope that works for you !

coordinates changed when migrating from pygame+rabbyt to pyglet+rabbyt

I'm working on a 2D game and decided to switch from SDL to OpenGL. I took rabbyt as an opengl wrapper for rendering my sprites and using pymunk (chipmunk) for my physics. I used pygame for creating the window and rabbyt for drawing the sprites on the screen.
I discovered that with pygame+rabbyt the (0,0) coordinate is in the middle of the screen. I liked that fact, because the coordinate representation in the physics engine were the same as in my graphics engine (I don't have to recalculate the coordinates when rendering the sprites).
Then I switched to pyglet because I wanted to draw lines with OpenGL - and discovered that suddenly the (0,0) coordinate was at the bottom left of the screen.
I suspected that that has something to do with the glViewport function, but only rabbyt executes that function, pyglet touches it only when the window is resized.
How can I set the (0,0) coordinate at the middle of the Screen?
I'm not very familiar with OpenGL and couldn't find anything after several hours googling and trial&error... I hope someone can help me :)
Edit: Some additional information :)
This is my pyglet screen initialization code:
self.window = Window(width=800, height=600)
rabbyt.set_viewport((800,600))
rabbyt.set_default_attribs()
This is my pygame screen initialization code:
display = pygame.display.set_mode((800,600), \
pygame.OPENGL | pygame.DOUBLEBUF)
rabbyt.set_viewport((800, 600))
rabbyt.set_default_attribs()
Edit 2: I looked at the sources of pyglet and pygame and didn't discover anything in the screen initialization code that has something to do with the OpenGL viewport... Here is the source of the two rabbyt functions:
def set_viewport(viewport, projection=None):
"""
``set_viewport(viewport, [projection])``
Sets how coordinates map to the screen.
``viewport`` gives the screen coordinates that will be drawn to. It
should be in either the form ``(width, height)`` or
``(left, top, right, bottom)``
``projection`` gives the sprite coordinates that will be mapped to the
screen coordinates given by ``viewport``. It too should be in one of the
two forms accepted by ``viewport``. If ``projection`` is not given, it
will default to the width and height of ``viewport``. If only the width
and height are given, ``(0, 0)`` will be the center point.
"""
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
if len(viewport) == 4:
l, t, r, b = viewport
else:
l, t = 0, 0
r, b = viewport
for i in (l,t,r,b):
if i < 0:
raise ValueError("Viewport values cannot be negative")
glViewport(l, t, r-l, b-t)
if projection is not None:
if len(projection) == 4:
l, t, r, b = projection
else:
w,h = projection
l, r, t, b = -w/2, w/2, -h/2, h/2
else:
w,h = r-l, b-t
l, r, b, t = -w/2, w/2, -h/2, h/2
glOrtho(l, r, b, t, -1, 1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
def set_default_attribs():
"""
``set_default_attribs()``
Sets a few of the OpenGL attributes that sprites expect.
Unless you know what you are doing, you should call this at least once
before rendering any sprites. (It is called automatically in
``rabbyt.init_display()``)
"""
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)
glEnable(GL_BLEND)
#glEnable(GL_POLYGON_SMOOTH)
Thanks,
Steffen
As l33tnerd suggested the origin can be placed at the center with glTranslatef...
I added the following below my screen initialization code:
pyglet.gl.glTranslatef(width/2, height/2, 0)
Thanks!

Categories