Samuel Cabrera
Samuel Cabrera

Reputation: 73

How to properly inherit the Surface class in Pygame

To put OOP in practice, I created my own Window class that inherits from pygame.Surface, and adds functionality. According to the following source, this is what I should do:

class MyWindow(pygame.Surface):
    def __init__(self, w, h):
        pygame.Surface.__init__(self, size=(w, h))
        self.screens: list[Screen] = []  # For example

I tried the following code, and it seems to work fine, up until the point where display.update() is called. This throws a pygame.error.

myWin: MyWindow = MyWindow(200, 200)
print(myWin)    # This correctly prints: <Surface(200x200x32 SW)>

pygame.draw.rect(myWin, (255, 255, 255), (90, 90, 20, 20))
pygame.display.update()
pygame.time.delay(1000)

That code doesn't even create an actual window. In contrast, the following code works just fine, instead relying on the display.set_mode() function to initialize the Surface.

pyWin: pygame.Surface = pygame.display.set_mode((200, 200))
pygame.draw.rect(pyWin, (255, 255, 255), (90, 90, 20, 20))
pygame.display.update()  # Actually shows the square
pygame.time.delay(1000)

The desired output

Clearly display.set_mode() does something that Surface.__init__() doesn't.

TL;DR: How can I initialize an object inheriting from Surface, in such a way that it actually shows and updates the window?

Thanks!

Upvotes: 2

Views: 784

Answers (1)

Valentino
Valentino

Reputation: 7361

TL;DR: How can I initialize an object inheriting from Surface, in such a way that it actually shows and updates the window?

I'd say you can't do it in the way you think, and there is a good reason for not ever wanting to do it.
A couple of things to keep in mind:

  1. A Surface is simply a 2D array of pixels, with convenient methods to read / edit / write the values of those pixels. Write where? To other surfaces. You can have as many surfaces you want in a game, each one storing the image of a different element of the game (your main character, an enemy, an element of the map, etc). You can write the content of a Surface on top of another Surface, to make complex images.
    A Surface, by itself, it has no connection with the screen (i.e. with what is shown on the window you see on your PC monitor).
  2. The display is a special Surface, instantiated by pygame.display.set_mode(). It is a Surface like all the other you can create using the Surface class, but that, and only that Surface, represents the display, i.e. what you see on your monitor.

This means that a pygame.Surface instance is never shown on the screen. To show stuffs on the screen, you need to blit the other Surfaces (i.e. copy their content) somewhere on the special display Surface.

Now let's go back to your question. With what I have just explained in mind, you could ask: Then can I write a Surface subclass such that each time the Surface value changes, it is automatically blit on the display and display.update() is called`?

Well, it will be complicated. If you use the function from pygame.draw the Surface is an argument, so you need some good trick to let an argument understanding when it is used.
However, trust me: you don't want to do it. As soon as your game becomes a little more complex, it will be highly inefficient. The reason is that blitting and drawing requires time, so you want to optimize these operations, performing them only when needed. If you have many surfaces in your game, you want to blit them on the display each iteration of your main loop (or each frame if you prefer) to "prepare" the new frame, and call display.update only once.

Upvotes: 2

Related Questions