How to get a pixel color out of the screen limit in pygame?

Asked

Viewed 331 times

1

I know to get a color of a pixel in Pygame I use get_at. But I have a huge background image and a lot of it is outside the screen area delimited by pygame.

For example, a 1000x1000 image inside a 500x500 screen will be only 50% visible, the rest will be "outside" of the screen. So I need to access this part OFF the pygame screen.

But if I make reference to get_at with a coordinate larger or smaller than the screen dimensions, Pygame gives an error:

Indexerror: pixel index out of range

import pygame
W = 600
H = 400
pygame.init()
screen = pygame.display.set_mode([W, H])
image = pygame.image.load('huge_background.png').convert_alpha()
screen.blit(image, [-5000, -420])
pygame.display.update()
screen.get_at([100, -5]) # gera o erro "IndexError: pixel index out of range"

Would anyone know how I can access a pixel beyond the visible area of the pygame screen?


It is a simulation of vehicles inside a track.

Here is the currently visible pygame screen (1600 x 600). The 3 lines (magenta) are the collision "detectors":

enter image description here

And here is the background (track) that will roll according to the position of the car (8000 x 8000):

enter image description here

This would be a view of the screen (quado in red) and the global plane with the background image (track): enter image description here

Thus, collision detector lines may exceed pygame screen limits, but need to "read" external pixels to detect collision.

1 answer

2


Your screen is the screen, and it only has 500x500 pixels - you don’t know, and there’s nothing outside it.

However, to draw the background image, you load it on the line:

image = pygame.image.load('huge_background.png').convert_alpha()

The object saved in the variable image in this case, as well as your screen, in the variable screen, is a Surface pygame. The method .get_at is a Surface class method.

So all you need is to keep your disk image read in variable image available, and check the pixel value on it, instead of in Surface - in a complete project, instead of in this demonstration, the background image offset values will also be in variables, instead of the fixed values there [-5000, -420] -

Let’s say you keep that amount in a tuple, or better yet, in a Vector2:

 offset = pygame.math.Vector2(-5000, -420)

When asking for the value of the pixel "off-screen", just add in the coordinates of the screen, the negative value of this displacement - that is, the pixel (0,0) of the screen corresponds to the pixel "5000,420" of image - So just do it:

offset = pygame.math.Vector2(-5000, -420)
...
# No corpo do código do jogo, mantenha o "offset" atualizado
# se mudar a posição do fundo
...
color = image.get_at([100, -5] - offset) 
# (em vez de screen.get_at([100, -5]) )

The Vector2 class is relatively new in Pygame, it is available from of 1.9, if I’m not mistaken - realize that its great advantage is that if it’s summed or subtracted a with a tuple, or another sequence of two coordinates, it already does the right number for each component of the coordinate.

If offset was a normal tuple instead of a Vector2, the code would have to be:

color = image.get_at([100 - offset[0], -5 - offset[1]) 

Important

Another thing of note - the methods .get_at and .set_at pygame surfaces are good for spot checks - for example, seeing if a pixel is within an extensive area of a color, or changing a very small detail on an image. However, both for larger checks (if you are looking for specific colors in an area of the image, for example), they are quite slow - because they involve a function call and dynamic creation of several objects in Python for each call. That is to say: a call to get_at returns a tuple of 4 whole components - internally, the computer will take the numbers in the memory representing the image, take the reference to 4 objects int Python and create a tuple containing them. This can be 1000 to 10000 times slower than just checking the values in sequence in memory.

So if you’re manipulating the image, the ideal is to turn the manipulated area temporariametne into a surfarray - this returns an array of the numpy, that can access the "inplace" numbers in memory, tremendously more efficiently.

  • Thank you so much for the excellent explanation! Little by little I’ll get acquainted with the concepts of pygame. I understood that the concept of Surface, where the background image is a Surface and the screen as well and can be manipulated independently.

  • I edited the original question and added some images and explanations. I understand that I can use the get_at independently on the background image and not on the screen. But in addition to the background image, there may be OTHER CARS and other previously 'blighted' objects. That is, it would be a composition of the background image with other objects on top. How to treat the collision in this case' composite'?

  • 1

    the collision with other cars has to be compared with the coordinates of the other cars in the game, not with respect to their pixels on the screen - for this we will use the concept of Sprites and groups, and the collision functions embedded in the pygame. I suggest you unfold the question, and make the doubt in the above comment another question here in stackoverflow - since they will be distinct concepts. I or someone else will answer.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.