Salviati
Salviati

Reputation: 788

Printing small rectangles to the screen in a for loop. (pygame)

I'm trying to get a code to print small rectangles all over my screen in pygame with the help of for loops, but having trouble. I have solved parts of it with this code but it looks ugly and preforms bad:

x = 0
y = 0

for y_row in range(60):
    y = y + 10
    pygame.draw.rect(screen, GREEN, [x, y, 5, 5], 0)
    for x_row in range(70):
        pygame.draw.rect(screen, GREEN, [x, y, 5, 5], 0)
        x = x + 10
    x = 0

To start of, I do not believe I have to assign a value to x and y if I just can figure out how to implement the value of y_row and x_row at x and y's places instead, now it increases with 1, it should increase with 10, than I can implement it instead.

Another problem with the code is that it leaves a blank row at the top, this is because I had to add the y = y + 10 above the pygame draw, otherwise it just printed one rectangle there witch made it more visible.

The template I'm using to get the code working you can find Here.

Upvotes: 2

Views: 2154

Answers (2)

Anthony Blackshaw
Anthony Blackshaw

Reputation: 2549

Drawing 4,200 rectangles to the screen every 60th of a second is probably a significant task for the CPU. I suspect that the pygame.draw.rect() function is fairly high-level and calls are not batched by pygame making it sub-optimal, there is a hint in the documentation (https://www.pygame.org/docs/ref/draw.html#pygame.draw.rect) that Surface.fill(color, rect=None, special_flags=0) can be hardware accelerated and may be a faster option if you're filling the rectangles.

Note: the code examples below are pseudo ... just means you need to fill in the gaps.

You only need 1 call to pygame.draw.rect per iteration of the loop not 2 as you have now, e.g.

for row in rows:
    y = ...
    for col in cols:
        x = ...
        ... draw rect ...

One easy win for performance is to not draw anything that's off-screen, so test your x, y coordinates before rendering, e.g:

screen_width = 800
screen_height = 600

for ...
    y = y += 10
    if y > screen_height:
        break

    for ...
        x += 10
        if x > screen_width:
            break

        ... draw block ...

The same approach could also be used (with a continue) to implement an offset (e.g a starting offset_x, offset_y value) where rectangles with negative x, y values are not rendered (the test is not x < 0 however, but x < -block_size).

There's nothing wrong with calculating the x and y values from a loop index as you are doing, it's often useful to have an index (for example the index [row][col] might give you the location of data for a tile in a 2D matrix representing game tiles). I would calculate the x, y values myself from the indexes using a multiplier (this also solves the blank first row issue):

block_size = 10

for row in ...
    y = row * block_size
    if y > screen_height:
        break

    for col in ...
        x = col * block_size 
        if x > screen_width:
            break

        ... draw block ...

If you're using Python2 then you might consider using xrange to predefine the loop ranges to improve performance (though I imagine only a small amount and as always with optimization testing the performance difference is key). For example:

rows = xrange(60)
cols = xrange(70)

for row in rows:
    ...
    for cols in cols:
        ... draw block ...

Upvotes: 2

elegent
elegent

Reputation: 4017

As @bshuster13 mentioned you can use pythons range() function and pass an optional step and stop argument to create a list containing arithmetic progressions.

numberOfRows = 60
numberOfColumns = 70

stepBetweenRects = 10

for y in range(0, numberOfRows * stepBetweenRects, stepBetweenRects):
    for x in range(0, numberOfColumns * stepBetweenRects, stepBetweenRects):
        pygame.draw.rect(screen, GREEN, (x, y, 5, 5), 0)

Upvotes: 2

Related Questions