pyglet - loading/blitting image with alpha - python

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.

Related

Best way to load images pygame

Currently, I am loading all images at the start of the program. Is there a better solution for this? As some images may not be used at all throughout the entire game (I'm currently making a main menu).
Main.py
import pygame as pg
from Menu import Menu
pg.init()
"""Displays screen"""
winWidth = 1920
winHeight = 1080
size = (winWidth, winHeight)
win = pg.display.set_mode(size)
"""Load images"""
bg_img = pg.image.load('Pic/td-gui/PNG/menu/bg.png')
title_img = pg.image.load('transtitle.png')
settings_img = pg.image.load('Pic/td-gui/PNG/menu/button_settings.png')
play_img = pg.image.load('Pic/td-gui/PNG/menu/button_play.png')
reg_img = pg.image.load('Pic/td-gui/PNG/menu/button_registration.png')
quit_img = pg.image.load('Custom/Menu/Quit.png')
rope_img = pg.image.load('Pic/td-gui/PNG/menu/rope_small.png')
changeDiff_img = pg.image.load('Custom/Menu/Change_Difficulty.png')
score_img = pg.image.load('Custom/Menu/Scoreboard.png')
selectmap_img = pg.image.load('Custom/Menu/Select_map.png')
tutorial_img = pg.image.load('Custom/Menu/Tutorial.png')
"""Puts loaded images into the correct size"""
bg_img = pg.transform.scale(bg_img,(winWidth, winHeight))
title_img = pg.transform.scale(title_img, (750, 75))
play_img = pg.transform.scale(play_img, (250, 250))
rope_img = pg.transform.scale(rope_img, (27, 290))
clock = pg.time.Clock()
win.blit(bg_img, (0, 0))
mainMenu = True
settingsMenu = False
run = True
running = True
while run:
clock.tick(30)
pg.display.update()
for event in pg.event.get():
if event.type == pg.QUIT:
run = False
if not run:
pg.quit()
"""Places correct images in specified location"""
title = Menu(winWidth // 2 - 375, winHeight // 2 - 500, title_img)
if mainMenu:
reg_button = Menu(winWidth // 2 - 150, winHeight // 2 - 100, reg_img)
ropeR = Menu(winWidth // 2 - 100, 200, rope_img)
ropeL = Menu(winWidth // 2 + 70, 200, rope_img)
scoreboard_button = Menu(winWidth//2 - 330, winHeight//2 + 275, score_img)
play_button = Menu(winWidth // 2 - 125, winHeight // 2 - 375, play_img)
reg_button = Menu(winWidth // 2 - 161, winHeight // 2 - 100, reg_img)
quit_button = Menu(winWidth//2 + 30, winHeight//2 + 275, quit_img)
changeDiff_button = Menu(winWidth//2 - 330, winHeight//2 + 80, changeDiff_img)
selectmap_button = Menu(winWidth//2 + 30, winHeight//2 + 80, selectmap_img)
settings_button = Menu(winWidth - 200, winHeight - 200, settings_img)
"""Draws images in specified location and makes buttons if specified"""
title.draw_img(win)
if reg_button.draw_button(win):
print('Reg button clicked')
ropeL.draw_img(win)
ropeR.draw_img(win)
if scoreboard_button.draw_button(win):
print("options Clicked")
if play_button.draw_button(win):
print("Play Clicked")
if quit_button.draw_button(win):
pg.quit()
if changeDiff_button.draw_button(win):
print('Change Difficulty button pressed')
if selectmap_button.draw_button(win):
print('Select Map button preseed')
if settings_button.draw_button(win):
print('Settings button pressed')
mainMenu = False
settingsMenu = True
if settingsMenu:
win.blit(bg_img, (0, 0))
title.draw_img(win)
Menu.py
import pygame as pg
pg.init()
class Menu():
def __init__(self, x, y, img):
self.img = img
self.rect = self.img.get_rect()
self.rect.y = y
self.rect.x = x
self.clicked = False
def draw_button(self, win):
action = False
#get mouse position
pos = pg.mouse.get_pos()
#check mouseover and clicked conditions
if self.rect.collidepoint(pos):
if pg.mouse.get_pressed()[0] == 1 and not self.clicked:
action = True
self.clicked = True
if pg.mouse.get_pressed()[0] == 0:
action = False
#draw image
win.blit(self.img, (self.rect.x, self.rect.y))
return action
def draw_img(self, win):
win.blit(self.img, (self.rect.x, self.rect.y))
Say I'm making a music button, which is accessible through the settings button, I would currently load the music button image at the start of the program. Is there a way to make it so the music image would only be loaded once the settings button has been pressed?
To give a quick and simple overview of what you could do, here's an example. You define an abstract Scene class. Then, every "scene" in your game inherits from it. Each scene has a setup method that loads everything the scene needs (or might need).
import pygame
class Scene:
def __init__(self):
""" Create all attributes """
def setup(self):
""" Load everything in and initialize attributes """
def handle_events(self, events):
""" Handle the events for this scene """
def update(self, dt):
""" Run logic """
def render(self, screen):
""" Draw to the screen """
class Menu(Scene):
def __init__(self):
""" Create all attributes """
self.image1 = None
self.image2 = None
# ... and so on ...
def setup(self):
""" Load everything in and initialize attributes """
self.image1 = pygame.image.load("my_image1.png")
self.image2 = pygame.image.load("my_image2.png")
# ... and so on ...
def handle_events(self, events):
""" Handle the events for this scene """
def update(self, dt):
""" Run logic """
def render(self, screen):
""" Draw to the screen """
screen.blit(self.image1, (0, 0))
# ... and so on ...
class Game(Scene):
def __init__(self):
""" Create all attributes """
self.player = None
self.enemies = None
# ... and so on ...
def setup(self):
""" Load everything in and initialize attributes """
self.player = Player(position=(100, 100), image=pygame.image.load("player.png"))
self.enemies = [
Enemy(position=(200, 400), pygame.image.load("enemy1.png")),
Enemy(position=(200, 500), pygame.image.load("enemy2.png")),
]
# ... and so on ...
def handle_events(self, events):
""" Handle the events for this scene """
for event in events:
if event.type == pygame.KEY_DOWN:
print('PAM!')
def update(self, dt):
""" Run logic """
self.player.update(dt)
for enemy in self.enemies:
enemy.update()
def render(self, screen):
""" Draw to the screen """
screen.blit(self.player.image, self.player.position)
for enemy in self.enemies:
screen.blit(enemy.image, enemy.position)
# ... and so on ...
Here's roughly how a game loop would look like.
def main():
screen = pygame.display.set_mode((1024, 1024))
clock = pygame.time.Clock()
running = True
scene = Menu()
scene.setup()
while running:
dt = clock.tick(60)
events = list(pygame.event.get())
scene.handle_events(events)
scene.update(dt)
scene.render(screen)
# Have some way of determining when to switch scene.
if should_switch_to_game:
scene = Game()
scene.setup()
elif should_switch_to_menu:
scene = Menu()
scene.setup()
# ... and so on ...
This is a basic starting point, but there are other ways to handle the situation, like having an asset manager that dynamically load and unload assets based on usage. However, these are quite complicated and will make development and debugging much harder. It's always best to keep things simple when you can. If you can load all images at the beginning of the program, then I'd suggest doing so.
Also, remember that there's nothing that's "best" or "optimized" as all you're doing is tradeoffs. If you're trying to optimize for memory, then you're often making your program slower. If you're optimizing for speed, then you're often making your program use more memory.
If you are loading things just when you need them, you're going to have a loading time switching between different views in your menu. This is often not something you want as the user expects it to be fast. Switching from the game to the menu, it's more forgiving to have a couple of extra ms. But if you feel you want to go the other way then different views in the menu could be considered different scenes.
In general, no it's not a problem to load images that are never used, as long as the images are small. It's better to load them all at once so that the interface never has to wait for loading.
However, if you have a very large amount of images or your images are very big, it might be worth loading some images only when you need them. You could do something like this when you start your music field:
def start_music_menu():
global music_icon # Sorry about the global
if music_icon is None:
music_icon = pygame.image.load('...')
In this case your interface will freeze while loading the extra image. You can also use threading or asyncio to keep your interface responsive while a image is loading.
Properly use OOP concept make separate class for various loading image and then use them accordingly .

after adding a button to my pygame, several things that previously gave me no issues are now giving me errors?

im not sure why im getting errors, as it was working perfectly fine before i started to add a button to my game. Ultimately my goal is to create a game menu with the options to start game which then moves onto the next screen, the level platform, and quit, which ultimately will close the window. Bonus, if i can get help on this, im thinking about also creating a sign up and login page, i already have one set up using tkinter but im not sure how to access the stored information using pygames, so it saves game progress from username and password. Also, what website do you guys use to get free images for your games? I can't draw that well and am having trouble finding images that match what im trying to accomplish.
# imports
import pygame, sys
import pygame.freetype
from pygame.sprite import Sprite
from pygame.rect import Rect
# colors
green = (0, 255, 0)
black = (0, 0, 0)
# buttons
def create_text(text, font_size, text_color, bg_color):
font = pygame.freetype.Sysfont("Courioer", font_size, bold=True)
surface, _ = font.render(text=text, fgcolor=text_color, bgcolor = bg_color)
return surface.convert_alpha()
class UIElement(Sprite):
def __init__(self, center_position, text, font_size, bg_color, text_color):
super().__init__()
self.mouse_over = False
default_image = create_text(text, font_size, text_color, bg_text)
highlighted_image = create_text(text, font_size * 1.2, text_color, bg_text)
self.images = [default_image, highlighted_image]
self.rects = [
default_image.get_rect(center = center_position),
highlighted_image.get_rect(center = center_position)]
super().__init__()
#property
def image(self):
return self.image[1] if self.mouse_over else self.image[0]
#property
def rect(self):
return self.rects[1] if self.mouse_over else self.rects[0]
def update(self, mouse_pos):
if self.rect.collidepoint(mouse_pos):
self.mouse_over = True
else:
self.mouse_over = False
def draw(self, surface):
surface.blit(self.image, self.rect)
# Title
pygame.display.set_caption("Zombrio")
icon = pygame.image.load("zombie.png")
pygame.display.set_icon(icon)
# character
survivorImg = pygame.image.load("frankenstein.png")
survivorx = 1
survivory = 400
def player():
screen.blit(survivorImg, (survivorx, survivory))
def main():
pygame.init()
# screen
screen = pygame.display.set_mode((800, 800))
clock = pygame.time.Clock()
uielement = UIElement(
center_position = (400, 400),
font_size = 30,
bg_color = black,
text_color = green,
text = "Start"
)
# Game Loop
while True:
screen.fill(black) # not defined error, only occurred after creating buttons
for event in pygame.event.get(): # video system not initialized
if event.type == pygame.QUIT: # indent error
pygame.quit()
sys.exit()
uielement.update(pygame.mouse.get_pos()) # says uielement is not defined
uielement.draw(screen)
pygame.display.flip()
player()
pygame.display.update()
clock.tick(120)
main()
pygame.quit()
So """Text""" is a string literal. You can use it for multi-line strings.
https://developers.google.com/edu/python/strings#:~:text=String%20literals%20inside%20triple%20quotes,go%20to%20represent%20computed%20values.
You can also use it to create a docstring.
The way you are using it here does not look correct. If you want to print those statements, then trying print("""Text""") on the next line.
If you want to use it as a comment on a line of code, then follow it with the "#"
For more examples of using tripple quotes, see here: https://www.geeksforgeeks.org/triple-quotes-in-python/

Pyglet Into Class

Given that so far I have always used Pygame, I wanted to start using Pyglet, to understand a little how it works.
import pyglet, os
class runGame():
widthDisplay = 1024
heightDiplay = 576
title = "Pokémon Life and Death: Esploratori del proprio Destino"
wra = pyglet.image.load("wra.png")
wrb = pyglet.image.load("wrb.png")
def __init__(self):
platform = pyglet.window.get_platform()
display = platform.get_default_display()
screen = display.get_default_screen()
self.widthScreen = screen.width
self.heightScreen = screen.height
self.xDisplay = int(self.widthScreen / 2 - self.widthDisplay / 2)
self.yDisplay = int(self.heightScreen / 2 - self.heightDiplay / 2)
self.Display = pyglet.window.Window(width=self.widthDisplay, height=self.heightDiplay, caption=self.title, resizable=False)
self.Display.set_location(self.xDisplay, self.yDisplay)
pyglet.app.run()
game = runGame()
Up to here everything is fine and everything works correctly. But I was wrong, in the sense, now that I have to draw something, how should I do? In the sense, can Pyglet stay in a class like Pygame or not?
The marked solution might solve the problem.
However, in my opinion it doesn't really add more functionality than you can already achieve.
If you truly want to make Pyglet into a class, you actually gotta inherit something, or make use of the possibility of programming in a OOP way i recon.
Here's my two cents:
import pyglet
from pyglet.gl import *
from collections import OrderedDict
from time import time
key = pyglet.window.key
class main(pyglet.window.Window):
def __init__ (self, width=1024, height=576, caption="Pokémon Life and Death: Esploratori del proprio Destino", fps=True, *args, **kwargs):
super(main, self).__init__(width, height, *args, **kwargs)
platform = pyglet.window.get_platform()
display = platform.get_default_display()
screen = display.get_default_screen()
self.xDisplay = int(screen.width / 2 - self.width / 2)
self.yDisplay = int(screen.height / 2 - self.height / 2)
self.set_location(self.xDisplay, self.yDisplay)
self.sprites = OrderedDict()
if fps:
self.sprites['fps_label'] = pyglet.text.Label('0 fps', x=10, y=10)
self.last_update = time()
self.fps_count = 0
self.keys = OrderedDict()
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_release(self, x, y, button, modifiers):
print('Released mouse at {}x{}'.format(x, y))
def on_mouse_press(self, x, y, button, modifiers):
if button == 1:
print('Pressed mouse at {}x{}'.format(x, y))
def on_mouse_drag(self, x, y, dx, dy, button, modifiers):
self.drag = True
print('Dragging mouse at {}x{}'.format(x, y))
def on_key_release(self, symbol, modifiers):
try:
del self.keys[symbol]
except:
pass
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
self.keys[symbol] = True
def pre_render(self):
pass
def render(self):
self.clear()
# FPS stuff (if you want to)
self.fps_count += 1
if time() - self.last_update > 1: # 1 sec passed
self.sprites['fps_label'].text = str(self.fps_count)
self.fps_count = 0
self.last_update = time()
#self.bg.draw()
self.pre_render()
for sprite in self.sprites:
self.sprites[sprite].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()
This way, you can actually interact with your class as if it were the pyglet class. For instance, you got on_key_press without having to use decorators etc.
You should make more functions for each thing you are doing. For example if you want to make a line you might have
import pyglet, os
class runGame():
widthDisplay = 1024
heightDiplay = 576
title = "Pokémon Life and Death: Esploratori del proprio Destino"
wra = pyglet.image.load("wra.png")
wrb = pyglet.image.load("wrb.png")
def __init__(self):
platform = pyglet.window.get_platform()
display = platform.get_default_display()
screen = display.get_default_screen()
self.widthScreen = screen.width
self.heightScreen = screen.height
self.xDisplay = int(self.widthScreen / 2 - self.widthDisplay / 2)
self.yDisplay = int(self.heightScreen / 2 - self.heightDiplay / 2)
self.Display = pyglet.window.Window(width=self.widthDisplay, height=self.heightDiplay, caption=self.title, resizable=False)
self.Display.set_location(self.xDisplay, self.yDisplay)
pyglet.app.run()
def drawLine(x, y):
drawLine(x, y)
You will have to make the functions based on what you want to do, but if you aren't going to use the runGame class multiple times before you kill the program, you shouldn't use OOP programming and use Procedural Programming.
You can create a custom pyglet window, by inheriting from the pyglet.window.Window class. After that in your init method call the super classes init (this will call the the init inside pyglet.window.Window class )Then overwrite the inherited methods (on_draw, on_key_press, on_key_release etc.). After that create an update method, which will be called by the pyglet.clock.schedule_interval 60 times in a second.Lastly just call the pyglet.app.run() method.
import pyglet
class GameWindow(pyglet.window.Window):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def on_draw(self):
self.clear()
def on_mouse_press(self, x, y, button, modifiers):
pass
def on_mouse_release(self, x, y, button, modifiers):
pass
def on_mouse_motion(self, x, y, dx, dy):
pass
def update(self, dt):
pass
if __name__ == "__main__":
window = GameWindow(1280, 720, "My Window", resizable=False)
pyglet.clock.schedule_interval(window.update, 1/60.0)
pyglet.app.run()

Sprite in Pyglet not doing what I want

I am trying to have a circle that, when clicked, moves somewhere else on the screen. However, when I click the circle, nothing happens.
#IMPORT STUFF
import pyglet as pg
from random import randint
mouse = pg.window.mouse
#VARS
window = pg.window.Window(width = 640, height = 480)
score = 0
circleImg = pg.image.load("circle.png")
circle = pg.sprite.Sprite(circleImg, randint(1, window.width), randint(1, window.height))
text = pg.text.Label("Click red!", font_name = "Times New Roman", font_size = 18, x = 260, y = 10)
#DETECT MOUSE PRESS ON CIRCLE
#window.event
def on_mouse_press(x, y, button, modifiers):
if x == circle.x and y == circle.y:
circle.x = randint(1, window.width)
circle.y = randint(1, window.height)
#window.event
def on_draw():
window.clear()
text.draw()
circle.draw()
pg.app.run()
import pyglet
from pyglet.gl import *
from random import randint
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
class Circle(pyglet.sprite.Sprite):
def __init__(self, radiance=5, x=0, y=0):
self.texture = pyglet.image.load('circle.png')
super(Circle, self).__init__(self.texture)
def click(self, x, y):
if x >= self.x and y >= self.y:
if x <= self.x + self.texture.width and y <= self.y + self.texture.height:
return self
mouse = pyglet.window.mouse
#VARS
window = pyglet.window.Window(width = 640, height = 480)
score = 0
#circleImg = pyglet.image.load("circle.png")
#circle = pyglet.sprite.Sprite(circleImg, randint(1, window.width), randint(1, window.height))
circle = Circle(x=50, y=50)
text = pyglet.text.Label("Click red!", font_name = "Times New Roman", font_size = 18, x = 260, y = 10)
#DETECT MOUSE PRESS ON CIRCLE
#window.event
def on_mouse_press(x, y, button, modifiers):
if circle.click(x, y):
print('Clicked in circle')
circle.x = randint(0, window.width - 10)
circle.y = randint(0, window.height - 10)
#window.event
def on_draw():
window.clear()
text.draw()
circle.draw()
pyglet.app.run()
A short description of what this does is it creates a custom class called Circle that inherits the Sprite class. It loads the circle.png as a texture with a alpha channel that gets blended by the GL library.
We add a custom function called click that checks if the lowest x,y coordinates are higher than the circles lowest x,y, then we check if the cursor is below x+width and same for y of the image region.
If that's the case, we return the circle sprite class as a True value in case we want to use the sprite.
Future enhancements:
You should draw the circle using gl functions, hence why I've defined radiance in the class definitions. However radiance here is never used, it's a placeholder for the future.
This is so you can use math to defined if you actually clicked within the circle, but this is beyond my scope of quick answers.. I would have to do a lot of debugging myself in order to get the math to add up (it's not my strong side).
What makes it work now is that we use the image width, height, x and y data to crudely check if we're within the image, aka "the circle".
trying to draw over sprite or change picture pyglet
As a bonus, I'll add this answer to the list of enhancements because it contains some stuff that might be useful. One would be to replace 90% of your code with a custom pyglet.window.Window class to replace global variables and decorators and stuff.
And it would look something like this:
import pyglet
from pyglet.gl import *
from random import randint
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
key = pyglet.window.key
class Circle(pyglet.sprite.Sprite):
def __init__(self, radiance=5, x=0, y=0):
self.texture = pyglet.image.load('circle.png')
super(Circle, self).__init__(self.texture)
def click(self, x, y):
if x >= self.x and y >= self.y:
if x <= self.x + self.texture.width and y <= self.y + self.texture.height:
return self
class MainScreen(pyglet.window.Window):
def __init__ (self):
super(MainScreen, self).__init__(800, 600, fullscreen = False)
self.x, self.y = 0, 0
self.bg = pyglet.sprite.Sprite(pyglet.image.load('background.jpg'))
self.sprites = {}
self.sprites['circle'] = Circle(x=50, y=50)
self.sprites['label'] = pyglet.text.Label("Click red!", font_name = "Times New Roman", font_size = 18, x = 260, y = 10)
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_mouse_press(self, x, y, button, modifiers):
if self.sprites['circle'].click(x, y):
print('Clicked in circle')
self.sprites['circle'].x = randint(0, self.width - 10)
self.sprites['circle'].y = randint(0, self.height - 10)
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
def render(self):
self.clear()
self.bg.draw()
for sprite_name, sprite_obj in self.sprites.items():
sprite_obj.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()
x = MainScreen()
x.run()
I'm not familiar with pyglet, but I'm guessing the problem is that you're checking whether x == circle.x etc, which means it only moves when you click the single pixel at the exact centre of the circle. Try some kind of maximum distance from the centre (e.g. a hypotenuse math.sqrt( (x-circle.x)**2 + (y-circle.y)**2) < circle.radius

FPS with Pyglet half of monitor refresh rate

I'm new to working with Pyglet and I've written a small program which moves a ball around the screen. Right now I'm having difficulty establishing a steady frame rate of 60 fps. While Pyglet is supposed to sync with my monitor's refresh rate of 60Hz, Pyglet is setting my fps to half of my refresh rate (ex. when 60Hz, 30 fps). Is there something wrong in my code that is causing this?
import pyglet
import physicalobject
import random
from pyglet.window import mouse
pyglet.resource.path = ['./resources']
pyglet.resource.reindex()
ball_image = pyglet.resource.image("ball2.png")
#sets clock format
fps_display = pyglet.clock.ClockDisplay(format='%(fps).2f fps')
def center_image(image):
image.anchor_x = image.width/2
image.anchor_y = image.height/2
center_image(ball_image)
ball = physicalobject.PhysicalObject(img=ball_image, x = 400, y = 300)
ball.scale = .2
ball.velocity_x = random.randint(-4,4)*150
ball.velocity_y = random.randint(-4,4)*150
#Calls update function to set new ball position based on velocity
def update(dt):
ball.update(dt)
#window.event
def on_mouse_drag(x, y, dx, dy, button, modifiers):
ball.x = x
ball.y = y
ball.velocity_x = dx * 20
ball.velocity_y = dy * 20
#window.event
def on_draw():
window.clear()
ball.draw()
fps_display.draw()
def main():
pyglet.clock.schedule_interval(update, 1/120.0)
pyglet.app.run()
if __name__ == '__main__':
main()
Pyglet simply doesn't handle it correctly on some systems, you have to disable your application window's vsync in order to get it to work. Here's an example script you can run to get a feel for how it works:
import pyglet
# Show FPS
fps = pyglet.clock.ClockDisplay()
# The game window
class Window(pyglet.window.Window):
def __init__(self):
super(Window, self).__init__(vsync = False)
# 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)
# You need the dt argument there to prevent errors,
# it does nothing as far as I know.
def update(self, dt):
pass
def on_draw(self):
pyglet.clock.tick() # Make sure you tick the clock!
self.clear()
fps.draw()
# Create a window and run
win = Window()
pyglet.app.run()
import pyglet
from time import time, sleep
class Window(pyglet.window.Window):
def __init__(self, refreshrate):
super(Window, self).__init__(vsync = False)
self.frames = 0
self.framerate = pyglet.text.Label(text='Unknown', font_name='Verdana', font_size=8, x=10, y=10, color=(255,255,255,255))
self.last = time()
self.alive = 1
self.refreshrate = refreshrate
def on_draw(self):
self.render()
def render(self):
self.clear()
if time() - self.last >= 1:
self.framerate.text = str(self.frames)
self.frames = 0
self.last = time()
else:
self.frames += 1
self.framerate.draw()
self.flip()
def on_close(self):
self.alive = 0
def run(self):
while self.alive:
self.render()
event = self.dispatch_events()
sleep(1.0/self.refreshrate)
win = Window(23) # set the fps
win.run()
Note the lack of the clock feature.
Also, try setting vsync = True and removing the sleep(1.0/self.refreshrate), this will lock the refresh rate to your monitor.
Also, note that i don't use pyglet.app.run() to lock the rendering process, i call self.dispatch_events() instead.. it doesn't really do anything except let the graphic "poll" and move on, without it.. pyglet waits for a poll to occur, which pyglet.app.run() normally does.

Categories

Resources