I have to evaluate the performance/functionality of Pyopengl vs Pyglet. The main concern is that it can't drop any frame while using high FPS. Before I start learning one of then, I need to see if it can do what the client needs.
I am trying to alternate (on vsync) between red and green (in fullscreen mode). If anybody can give me a good tutorial site, or help me with an example, it would be very nice.
I have looked at this post (and more):
FPS with Pyglet half of monitor refresh rate
Made a modification, but can't see how to switch from one color to another one on Vsync.
import pyglet
from pyglet.gl import *
fps = pyglet.clock.ClockDisplay()
# The game window
class Window(pyglet.window.Window):
def __init__(self):
super(Window, self).__init__(fullscreen=True, vsync = False)
self.flipScreen = 0
glClearColor(1.0, 1.0, 1.0, 1.0)
# Run "self.update" 128 frames a second and set FPS limit to 128.
pyglet.clock.schedule_interval(self.update, 1.0/128.0)
pyglet.clock.set_fps_limit(128)
def update(self, dt):
self.flipScreen = not self.flipScreen
pass
def on_draw(self):
pyglet.clock.tick() # Make sure you tick the clock!
if self.flipScreen == 0:
glClearColor(0, 1, 0, 1.0)
else:
glClearColor(1, 0, 0, 1.0)
self.clear()
fps.draw()
# Create a window and run
win = Window()
pyglet.app.run()
I have seen a lot of tutorials, but I haven't been able to understand how to run this one test.
Thanks for your help.
Here is a code that does it, using pyglet. It has been tested on 3 monitors at 60 Hz and 120Hz. Please note that the use of global variable is bad. It may not be very clean, but it clearly shows that vsync is taken into account.This is the purpose of the code to test the functionnality of vsync.
import pyglet
from pyglet.gl import *
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
# Direct OpenGL commands to this window.
config = Config(double_buffer = True)
window = pyglet.window.Window(config = config)
window.set_vsync(True)
window.set_fullscreen(True)
colorSwap = 0
fullscreen = 1
fps_display = pyglet.clock.ClockDisplay()
def on_draw(dt):
global colorSwap
glClear(GL_COLOR_BUFFER_BIT) # Clear the color buffer
glLoadIdentity() # Reset model-view matrix
glBegin(GL_QUADS)
if colorSwap == 1:
glColor3f(1.0, 0.0, 0.0)
colorSwap = 0
else:
glColor3f(0.0, 1.0, 0.0)
colorSwap = 1
glVertex2f(window.width, 0)
glVertex2f(window.width, window.height)
glVertex2f(0.0, window.height)
glVertex2f(0.0, 0.0)
glEnd()
fps_display.draw()
#window.event
def on_key_press(symbol, modifiers):
global fullscreen
if symbol == pyglet.window.key.F:
if fullscreen == 1:
window.set_fullscreen(False)
fullscreen = 0
else:
window.set_fullscreen(True)
fullscreen = 1
elif symbol == pyglet.window.key.ESCAPE:
print ''
dt = pyglet.clock.tick()
pyglet.clock.schedule_interval(on_draw, 0.0001)
pyglet.app.run()
Related
from os import environ
environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
import pygame # import after disabling prompt
screen = pygame.display.set_mode((800, 800))
screen.fill((50, 50, 50)) # Dark gray color
pygame.display.update()
Yes, I did my research already, and couldn't find anything helpful: hence this question.
Every time I run the program the pygame window opens below other windows. I want it to behave in 2 ways based on code: Pin the window on top and spawn on top but no pin.
Here is the simplest solution I found:
(It also requires tkinter to get system screen metrics)
from os import environ
environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
import pygame # import after disabling environ prompt
from win32gui import SetWindowPos
import tkinter as tk
root = tk.Tk() # create only one instance for Tk()
root.withdraw() # keep the root window from appearing
screen_w, screen_h = root.winfo_screenwidth(), root.winfo_screenheight()
win_w = 250
win_h = 300
x = round((screen_w - win_w) / 2)
y = round((screen_h - win_h) / 2 * 0.8) # 80 % of the actual height
# pygame screen parameter for further use in code
screen = pygame.display.set_mode((win_w, win_h))
# Set window position center-screen and on top of other windows
# Here 2nd parameter (-1) is essential for putting window on top
SetWindowPos(pygame.display.get_wm_info()['window'], -1, x, y, 0, 0, 1)
# regular pygame loop
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
done = True
I have created a complete snake game using C++ and OpenGL before, and I want to do the same using Python, pygame, and PyOpenGL. The current problem I have is that after I spawn a fruit, it does not appear on the screen. Here's the code for my main function:
def main(): # Main function
# Initialize game components
game = Game(800, 600)
test_fruit = game.spawn_fruit(Point(100, 100))
# Initialize pygame module
pygame.init()
pygame.display.set_mode(game.get_window_size(), DOUBLEBUF | OPENGL)
pygame.display.set_caption("Python Game")
# Define variable to control main loop
running = True
# Main loop
while running:
# event handling, gets all event from the event queue
for event in pygame.event.get():
# only do something if the event is of type QUIT
if event.type == pygame.QUIT:
# change the value to False, to exit the main loop
running = False
# Modify game properties
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
game.draw_shapes()
pygame.display.flip()
pygame.time.wait(5)
It's possible I'm missing a pygame or pyopengl function, but I'm not sure. I've also tried changing pygame.display.flip() to pygame.display.update(), yet it gives me an error ("cannot update an OpenGL display") instead.
Here's the code for the shape I am attempting to display:
class Circle:
def __init__(self, pivot: Point, radius: int, sides: int, fill: bool, color: Color):
self.pivot = pivot
self.radius = radius
self.sides = sides
self.fill = fill
self.color = color
# Draw the shape of the circle
def draw(self):
glColor3f(self.color.r, self.color.g, self.color.b)
if self.fill:
glBegin(GL_POLYGON)
else:
glBegin(GL_LINE_LOOP)
for i in range(100):
cosine = self.radius * cos(i*2*pi/self.sides) + self.pivot.x
sine = self.radius * sin(i*2*pi/self.sides) + self.pivot.y
glVertex2f(cosine, sine)
glEnd()
OpenGL coordinates are in range [-1.0, 1.0] (Normalized Device Space). The Normalized device space is a unique cube from the left, bottom, near (-1, -1, -1) to the right, top, far (1, 1, 1).
If you want to use "window" coordinates, you must specify an Orthographic projection using glOrtho:
glOrtho(0, 800, 600, 0, -1, 1)
Choose the matrix mode with glMatrixMode and load the Identity matrix with glLoadIdentity.
Example:
def main(): # Main function
# Initialize game components
game = Game(800, 600)
test_fruit = game.spawn_fruit(Point(100, 100))
# Initialize pygame module
pygame.init()
pygame.display.set_mode(game.get_window_size(), DOUBLEBUF | OPENGL)
pygame.display.set_caption("Python Game")
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0, 800, 600, 0, -1, 1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
# Define variable to control main loop
running = True
# [...]
I'm trying to make a simple application with pyglet. My main problem so far is that I can't seem to blit an image with alpha - all of the transparent pixels are converted into black pixels. I'm not sure whether the problem is with the loading of the image or the blitting. Here is a very basic overview of how I'm trying to render the image:
import pyglet
import pyglet.clock
window = pyglet.window.Window()
window.config.alpha_size = 8
#fancy text
text = pyglet.resource.image("text.png")
#background image
bg = pyglet.resource.image("bg.png")
bg.blit(0, 0)
text.blit(100, 100)
pyglet.app.run()
Any help is appreciated. Thanks in advance.
You most likely just need to enable GL ALPHA blends.
from pyglet.gl import *
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
But first of all, your code is not able to run.
Mostly because you don't declare a window.event function to handle the on_draw where you normally render things.
Secondly, you never clear your window (which will cause a mess).
Here's a minimal working example of your code:
import pyglet
import pyglet.clock
window = pyglet.window.Window()
window.config.alpha_size = 8
#fancy text
text = pyglet.resource.image("text.png")
#background image
bg = pyglet.resource.image("bg.png")
#window.event
def on_draw():
window.clear()
bg.blit(0, 0)
text.blit(100, 100)
pyglet.app.run()
Now this generates this:
And here's a working example of how you use the GL_BLEND feature:
import pyglet
import pyglet.clock
from pyglet.gl import *
window = pyglet.window.Window()
window.config.alpha_size = 8
#fancy text
text = pyglet.resource.image("text.png")
#background image
bg = pyglet.resource.image("bg.png")
#window.event
def on_draw():
window.clear()
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
bg.blit(0, 0)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
text.blit(100, 100)
pyglet.app.run()
This yields a result like so:
However, this code will quickly become messy.
So there's two things you can do. You can first, put your images into sprite objects. Secondly, make this a bit more object oriented.
First, we'll use sprites.
self.fancy_background = pyglet.sprite.Sprite(pyglet.image.load('bg.png'))
self.fancy_background.draw() # not blit!
Sprites automatically uses transparency, which makes your life (and code) a lot easier.
Secondly, we'll put these into a batch.
Batches are made to bunch A LOT of sprites so you can call .draw() on the batch, and all sprites in that batch gets insta-rendered.
self.background = pyglet.graphics.Batch()
self.fancy_background = pyglet.sprite.Sprite(pyglet.image.load('bg.png'), batch=self.background)
self.background.draw() # background, not fancy_background! And also not blit!!
Last and most certainly not least.
We'll put this into a class so we can do cool stuff later on.
import pyglet
import pyglet.clock
from pyglet.gl import *
key = pyglet.window.key
class main(pyglet.window.Window):
def __init__ (self, width=800, height=600, fps=False, *args, **kwargs):
super(main, self).__init__(width, height, *args, **kwargs)
self.x, self.y = 0, 0
self.background = pyglet.graphics.Batch()
self.texts = pyglet.graphics.Batch()
self.fancy_background = pyglet.sprite.Sprite(pyglet.image.load('bg.png'), batch=self.background)
self.fancy_text = pyglet.sprite.Sprite(pyglet.image.load('text.png'), batch=self.texts)
self.mouse_x = 0
self.mouse_y = 0
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_mouse_motion(self, x, y, dx, dy):
self.mouse_x = x
self.mouse_y = y
def on_mouse_press(self, x, y, button, modifiers):
if button == 1: # Left click
pass
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
def render(self):
self.clear()
self.background.draw()
self.texts.draw()
self.flip()
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
if __name__ == '__main__':
x = main()
x.run()
BAM.
This code will enable you to create custom functions and custom "player objects" later on for instance. Also you can do collision detection easier and the code just looks a lot more structured (I threw in a little bonus features such as keyboard and mouse events).
Note tho, that the position of the sprites will default to x=0, y=0 as shown in the last picture. You can set the position with x=100 either on the variable/handle or when creating the sprite.
couple of days ago, I made a code that plays a video in pygame window.
Code works just fine, just as I originally intended. However, when I print debug statement to see its fps, it's somewhere around 30fps. If I were to increase fps, what should I do?
Here is the code I used.
import sys
from color import *
import pyglet
pygame.init()
running = True
gameDisplay= pygame.display.set_mode((800,600))
window = pyglet.window.Window(visible=False)
background_vid = pyglet.media.Player()
background_vid.queue(pyglet.media.load(".\\music_folder\\music_vid/servant_of_evil_converted.mp4"))
background_vid.play()
def hellow():
print "hellow bloody world"
def on_draw():
#We have to convert the Pyglet media player's image to a Pygame surface
rawimage = background_vid.get_texture().get_image_data()
print "rawimage "+str(rawimage)
pixels = rawimage.get_data('RGBA', rawimage.width *8)
video = pygame.image.frombuffer(pixels, (rawimage.width*2,rawimage.height), 'RGBA')
#Blit the image to the screen
gameDisplay.blit(video, (0, 0))
circle_x=300
while True:
pyglet.clock.tick()
on_draw()
print "fps: "+str(pyglet.clock.get_fps())
for event in pygame.event.get():
if(event.type == pygame.QUIT):
sys.exit()
pygame.quit()
pygame.draw.rect(gameDisplay, red, (circle_x, 300, 300, 300), 5)
circle_x+=1
pygame.display.update()
So what #pydude said is not completely wrong.
However, in order to actually messure FPS I'd put a custom counter in the on_draw function, that will give better accuracy.
Further more, the only real problem with your code is that you don't insert vsync=False into your Window() decorator.
I've reworked your code to make it a little bit more modular, I've also removed potential bottle-necks and added my own custom FPS counter (via GL and not console), here - have a go and see if it works better for you.
(Note: Pressing Escape will exit the application)
import sys
from color import *
import pyglet
from pyglet.gl import *
from time import time # Used for FPS calc
key = pyglet.window.key
class main(pyglet.window.Window):
def __init__ (self):
super(main, self).__init__(800, 800, fullscreen = False, vsync = True)
self.running = True
self.background_vid = pyglet.media.Player()
self.background_vid.queue(pyglet.media.load(".\\music_folder\\music_vid/servant_of_evil_converted.mp4"))
self.background_vid.play()
self.fps_counter = 0
self.last_fps = time()
self.fps_text = pyglet.text.Label(str(self.fps_counter), font_size=12, x=10, y=10)
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.running = False
def on_draw(self):
self.render()
#We have to convert the Pyglet media player's image to a Pygame surface
def render(self):
self.clear()
rawimage = background_vid.get_texture().get_image_data()
pixels = rawimage.get_data('RGBA', rawimage.width *8)
video = pygame.image.frombuffer(pixels, (rawimage.width*2,rawimage.height), 'RGBA')
#Blit the image to the screen
self.blit(video, (0, 0))
# And flip the GL buffer
self.fps_counter += 1
if time() - self.last_fps > 1:
self.fps_text.text = str(self.fps_counter)
self.fps_counter = 0
self.last_fps = time()
self.fps_text.draw()
self.flip()
def run(self):
while self.running is True:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
if event and event.type == pygame.QUIT:
self.running = False
x = main()
x.run()
Try toggling vsync = True to vsync = False and watch the difference in the FPS counter.
With python, printing is very slow. Try just printing every once and a while.
Example:(requires import random):
if random.random()>0.09:print "fps: "+str(pyglet.clock.get_fps())
In my main loop I have:
clock.tick_busy_loop(60)
pygame.display.set_caption("fps: " + str(clock.get_fps()))
However, the readout says the game is reading at 62.5fps.
I then tried to input clock.tick_busy_loop(57.5), which gave me a readout of 58.82...fps. When I set clock.tick_busy_loop(59) I get 62.5fps again. It looks like there is a threshold between 58.8fps and 62.5fps that I can't overcome here. How do I get my game to actually run at 60fps? I'm mainly looking for this kind of control because I executing events that are contingent on musical timing.
So I whipped up a simple demo based on my comment above that uses the system time module instead of the pygame.time module. You can ignore the OpenGL stuff as I just wanted to render something simple on screen. The most important part is the timing code at the end of each frame, which I have commented about in the code.
import pygame
import sys
import time
from OpenGL.GL import *
from OpenGL.GLU import *
title = "FPS Timer Demo"
target_fps = 60
(width, height) = (300, 200)
flags = pygame.DOUBLEBUF|pygame.OPENGL
screen = pygame.display.set_mode((width, height), flags)
rotation = 0
square_size = 50
prev_time = time.time()
while True:
#Handle the events
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
#Do computations and render stuff on screen
rotation += 1
glClear(GL_COLOR_BUFFER_BIT)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0, width, 0, height, -1, 1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glTranslate(width/2.0, height/2.0, 0)
glRotate(rotation, 0, 0, 1)
glTranslate(-square_size/2.0, -square_size/2.0, 0)
glBegin(GL_QUADS)
glVertex(0, 0, 0)
glVertex(50, 0, 0)
glVertex(50, 50, 0)
glVertex(0, 50, 0)
glEnd()
pygame.display.flip()
#Timing code at the END!
curr_time = time.time()#so now we have time after processing
diff = curr_time - prev_time#frame took this much time to process and render
delay = max(1.0/target_fps - diff, 0)#if we finished early, wait the remaining time to desired fps, else wait 0 ms!
time.sleep(delay)
fps = 1.0/(delay + diff)#fps is based on total time ("processing" diff time + "wasted" delay time)
prev_time = curr_time
pygame.display.set_caption("{0}: {1:.2f}".format(title, fps))