1

I am trying to create an animation of a rectangle in pygame but I struggle to animate it over the given frame-rate.

class Muscle(pygame.sprite.Sprite):
def __init__(self, screen, posX=200, posY=200, contraction=0.5, extension=1.5, length=10, thickness=5, min_len=0.2,max_len=1.5, power=20):
    super(Muscle, self).__init__()
    self.screen = screen
    self.contraction = contraction
    self.relaxation = relaxation
    self.length = length
    self.thickness = thickness
    if power < 1:
        self.power = 1
        power = 1
    else:
        self.power = power
    self.image = pygame.Surface((length, thickness))
    color_index = 5 * power
    self.image.fill((255, color_index, color_index))
    self.rect = self.image.get_rect()
    self.rect.x = posX
    self.rect.y = posY

def render(self):
    self.screen.blit(self.image, (self.rect.x, self.rect.y))

def contract(self):
    expected_width = self.length * self.contraction
    counter = 0
    while self.image.get_width() > expected_width:
        self.image = pygame.transform.scale(self.image, (self.rect.width, self.image.get_height()))
        self.rect.x += 1;
        self.rect.width -= 2
        self.render()
        counter += 1

def extend(self):
    expected_width = self.length * self.relaxation
    counter = 0
    while self.image.get_width() < expected_width:
        self.image = pygame.transform.scale(self.image, (self.rect.width, self.image.get_height()))
        self.rect.x -= 1;
        self.rect.width += 2
        self.render()
        #print("Relaxation:" + str(counter))
        counter += 1

If I call the contract and extend methods separately, they resize the image of the rectangle successfully, but I would like to perform it over time, without interrupting my main loop which draws the environment and the sprite.

1 Answer 1

1

The simplest solution would be to call extend or contract once per frame, but then the animation would be frame rate bound.

import pygame


class Muscle(pygame.sprite.Sprite):
    def __init__(self, screen, posX=200, posY=200, contraction=0.5,
                 extension=1.5, length=40, thickness=20, min_len=0.2,max_len=1.5, power=20):
        super(Muscle, self).__init__()
        self.screen = screen
        self.contracting = False  # To check if the muscle is contracting or extending.
        self.contraction = contraction
        self.relaxation = 1
        self.length = length
        self.thickness = thickness
        if power < 1:
            self.power = 1
            power = 1
        else:
            self.power = power
        self.image = pygame.Surface((length, thickness))
        color_index = 5 * power
        self.image.fill((255, color_index, color_index))
        self.rect = self.image.get_rect()
        self.rect.x = posX
        self.rect.y = posY

    # This method gets called every frame.
    def update(self):
        if self.contracting:
            self.contract()
        else:
            self.extend()

    def render(self):
        self.screen.blit(self.image, (self.rect.x, self.rect.y))

    def contract(self):
        expected_width = self.length * self.contraction
        counter = 0
        if self.image.get_width() > expected_width:
            self.image = pygame.transform.scale(self.image, (self.rect.width, self.image.get_height()))
            self.rect.x += 1
            self.rect.width -= 2
            self.render()
            counter += 1

    def extend(self):
        expected_width = self.length * self.relaxation
        counter = 0
        if self.image.get_width() < expected_width:
            self.image = pygame.transform.scale(self.image, (self.rect.width, self.image.get_height()))
            self.rect.x -= 1
            self.rect.width += 2
            self.render()
            counter += 1


def main():
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    muscle = Muscle(screen)
    all_sprites = pygame.sprite.Group(muscle)

    done = False

    while not done:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True
            # Change the state of the muscle.
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_c:
                    muscle.contracting = True
            elif event.type == pygame.KEYUP:
                if event.key == pygame.K_c:
                    muscle.contracting = False

        all_sprites.update()
        screen.fill((30, 30, 30))
        all_sprites.draw(screen)

        pygame.display.flip()
        clock.tick(30)


if __name__ == '__main__':
    pygame.init()
    main()
    pygame.quit()

Alternatively, you can call extend or contract after some time interval. Check out these answers to see how you can implement a timer.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much for the help !

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.