2

Reading the pygame tutorial here , you'll find this example: (arrows mine)

for o in objects:
    screen.blit(background, o.pos, o.pos) #<---
for o in objects:
    o.move()
    screen.blit(o.image, o.pos) #<---`

Reading the pygame documentation for blit here will say this: (italics mine)

blit(source, dest, area=None, special_flags = 0) -> Rect Draws a source Surface onto this Surface. The draw can be positioned with the dest argument. Dest can either be pair of coordinates representing the upper left corner of the source. A Rect can also be passed as the destination and the topleft corner of the rectangle will be used as the position for the blit. The size of the destination rectangle does not effect the blit.

Can anyone help me make sense of this? I have been pouring over my own code for days before I finally noticed in the example they used the 'pos' TWICE in one call, and once in another. I threw this at mine, and voila, the problem of incredibly unsynchronized, strobing, slow animation went away. But I don't understand why.


EDIT: the misunderstanding above was only part of the speed bottleneck. I did not understand (and still am struggling to) that one must multiply their movement increment by the clock tick. Suddenly, everything sprang to life. Here's an example, perhaps it will help some other studious newbie game makers:

clock = pygame.time.Clock()
FPS=60        
while True:
    timer = clock.tick(FPS)
    if label.x < label.target_x:
        label.x += (2*timer) #<-----

....the amount one increments their sprite's/surface's position is relative to the number returned by clock.tick. Suddenly, a modern laptop can make twenty images move around the screen at breakneck speed :) Thank you Ted Klein Bergman for your help!

1 Answers1

2

There is another line in the documentation:

An optional area rectangle can be passed as well. This represents a smaller portion of the source Surface to draw.

What's happening in the first for loop is that they're clearing the previous images by drawing the background image on top of all game objects. The background image is probably bigger than the game objects so every time we blit it we're drawing parts of the screen that doesn't needs to be redrawn. What they're doing is specifying how much of the background image to draw, which saves performance in this case.

Edit: The naming pos could be a bit misleading; it's actually a rectangle. If a rectangle is passed to the second argument (dest), then the blit function will use the topleft corner as the position of the source. The actual area of the rectangle will not be taken into account.

If a rectangle is passed to the third argument (area), then the blit function will take the area of the rectangle into account when blitting the source.

I created a little mock-up example to show how pygame is usually used. You often create a main loop that does 3 things: handle events, update objects and draw objects. In your example i would look something like this:

import random
import pygame
pygame.init()

SIZE = WIDTH, HEIGHT = 800, 600
FPS = 60


class AnimatedWord:

    def __init__(self, image, position, target, speed=1):
        self.image    = image
        self.target   = image.get_rect().move(*target)
        self.position = image.get_rect().move(*position)
        self.speed    = speed

    def update(self):
        if self.position.y > self.target.y:
            self.position.y -= self.speed
        elif self.position.y < self.target.y:
            self.position.y += self.speed

        if self.position.x > self.target.x:
            self.position.x -= self.speed
        elif self.position.x < self.target.x:
            self.position.x += self.speed

    def draw(self, screen):
        screen.blit(self.image, self.position)


def create_word_surfaces(words, font_size=30, color=(106, 90, 205, 0)):
    font = pygame.font.SysFont("Arial", font_size)

    surfaces = []
    for word in words:
        surface = font.render(word, True, color)
        surfaces.append(surface)

    return surfaces


def main():
    screen = pygame.display.set_mode(SIZE)
    background = screen.copy()
    background.fill((0, 0, 0, 0))
    screen.blit(background, (0, 0))
    clock = pygame.time.Clock()

    words = "loading loading loading loading loading loading loading loading loading loading vectors monkey banana reishi argonaut oneironaut purgatory interstitium marmalade savanna chinchilla gobies loading loading leadbetter ".split(" ")

    targets_x = [i for i in range(0, screen.get_width(), 50)]
    targets_y = [i for i in range(0, screen.get_height(), 20)]

    animated_words = []
    for surface in create_word_surfaces(words):
        target_x = random.choice(targets_x)
        target_y = random.choice(targets_y)
        animated_word = AnimatedWord(surface, position=(400, 300), target=(target_x, target_y), speed=1)
        animated_words.append(animated_word)

    running = True
    while running:   # Main loop

        clock.tick(FPS)  # Limit the framerate to FPS

        # HANDLE EVENTS
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        # UPDATE GAME OBJECTS
        for x in animated_words:
            x.update()

        # DRAW GAME OBJECTS
        screen.blit(background, (0, 0))  # Fill entire screen.
        for x in animated_words:
            x.draw(screen)

        pygame.display.update()

if __name__ == '__main__':
    main()
Ted Klein Bergman
  • 9,146
  • 4
  • 29
  • 50
  • ...even finding and correcting this, I still am unable to have 20 objects moving towards target coords at anything over a snail's pace. Here's what I've written. Is this how limited pygame is, or am I still missing something? https://gist.github.com/rocket-pig/ee2c7efb37e176fda4423e41fd173f5a I even wrote it up to create .png files out of the bits of text as I thought that just moving Font objects around might be the barrier. – Michael Meanswell Mar 31 '18 at 23:20
  • https://darknesseverytime.live/mirror/splosion.gif Here is a gif of how fast the resulting animation ends up being. On a 4-core computer, using Threading. (?) – Michael Meanswell Mar 31 '18 at 23:31
  • Im so new here I can't even upvote your post for usefulness. Thank you for taking time to answer me. – Michael Meanswell Mar 31 '18 at 23:36
  • Pygame shouldn't have any problems rendering that. I've looked at your code but your using some unconventional coding techniques that I have hard time reading right now. However, I don't think you should use 40 threads for your example though. I think it could case enough overhead for your program to run slow. I would avoid threads entirely. – Ted Klein Bergman Mar 31 '18 at 23:40
  • Thank you kindly for your help! Much obliged. I'll have to keep experimenting to understand what the bottleneck is. – Michael Meanswell Mar 31 '18 at 23:52
  • @MichaelMeanswell I edited an example of how pygame is usually used. It should run smoothly – Ted Klein Bergman Apr 01 '18 at 00:14
  • Oh wow! Just saw your edit. Sorry, am still learning this site also. Indeed, I was missing the **speed** factor of clock.tick as well. :) Thanks so much! – Michael Meanswell Apr 01 '18 at 02:50
  • You're truly my hero. I spent the afternoon continuing to learn from your rewrite, and got it connected to a word vector db like I was hoping to : It looks great! Thank you so much for your kindness. https://darknesseverytime.live/mirror/thrilled.gif – Michael Meanswell Apr 01 '18 at 05:39