Search code examples
pythonpython-3.xpyglet

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

Solution

  • 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".


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