Search code examples
python-3.xpyglet

Class player - Animation stop first frame


Quick question. I have my Player Class, working perfectly. Except for a small detail. This is the class:

from dict.entity_dict import player, player_class
from collections import OrderedDict
import pyglet, random

key = pyglet.window.key

class Player(pyglet.sprite.Sprite):

    dir_stand = "south"
    dir_run = "south"
    sprite_stand = 3
    sprite_run = 3
    image = None
    s = 0

    def __init__(self, game):
        self.game = game

        self.keyboard = key.KeyStateHandler()

        self.statistics_base = OrderedDict()

        self.image_stand = pyglet.resource.image(player.get("player_stand", {'x': None}).get("resource"))
        self.image_run = pyglet.resource.image(player.get("player_run", {'x': None}).get("resource"))
        self.image_stand_width = player.get("player_stand", {'x': None}).get("width")
        self.image_stand_height = player.get("player_stand", {'x': None}).get("height")
        self.image_run_width = player.get("player_run", {'x': None}).get("width")
        self.image_run_height = player.get("player_run", {'x': None}).get("height")

        self.vx = self.game.wd / 2
        self.vy = self.game.wh / 2

        self.load_sprite()

    def class_player(self, type):
        self.statistics_base["hp"] = player_class.get(type, {'x': None}).get("hp")
        self.statistics_base["atk"] = player_class.get(type, {'x': None}).get("atk")
        self.statistics_base["dif"] = player_class.get(type, {'x': None}).get("dif")
        self.statistics_base["atk_sp"] = player_class.get(type, {'x': None}).get("atk_sp")
        self.statistics_base["dif_sp"] = player_class.get(type, {'x': None}).get("dif_sp")
        self.statistics_base["vel"] = player_class.get(type, {'x': None}).get("vel")
        for stat in self.statistics_base:
            if self.statistics_base[stat] is None:
                self.statistics_base[stat] = 10


    def animation(self, image, da, width, height):
        frame_list = [image.get_region(x=width * i, y=height * da, width=46, height=58) for i in range(22)]
        image_animation = pyglet.image.Animation.from_image_sequence(frame_list, 0.10, True)

        return image_animation

    def direction_sprite(self):
        if self.dir_stand == "north":
            self.sprite_stand = 7
        elif self.dir_stand == "east":
            self.sprite_stand = 5
        elif self.dir_stand == "south":
            self.sprite_stand = 3
        elif self.dir_stand == "west":
            self.sprite_stand = 1
        if self.dir_run == "north":
            self.sprite_run = 7
        elif self.dir_run == "north-east":
            self.sprite_run = 6
        elif self.dir_run == "east":
            self.sprite_run = 5
        elif self.dir_run == "south-east":
            self.sprite_run = 4
        elif self.dir_run == "south":
            self.sprite_run = 3
        elif self.dir_run == "south-west":
            self.sprite_run = 2
        elif self.dir_run == "west":
            self.sprite_run = 1
        elif self.dir_run == "north-west":
            self.sprite_run = 0

    def load_sprite(self):
        if not self.keyboard[key.W] and not self.keyboard[key.S] and not self.keyboard[key.D] and not self.keyboard[key.A]:
            self.keyboard.clear()
            img = self.image_stand
            img_width = self.image_stand_width
            img_height = self.image_stand_height
            da = self.sprite_stand
        else:
            img = self.image_run
            img_width = self.image_run_width
            img_height = self.image_run_height
            da = self.sprite_run

        self.direction_sprite()
        self.image = self.animation(img, da, img_width, img_height)
        self.image.width, self.image.height = img_width, img_height
        self.image.anchor_x, self.image.anchor_y = img_width // 2, img_height // 2

        self.sprite = pyglet.sprite.Sprite(self.image, batch=self.game.Batch, group=self.game.GroupEntitySprite)


        self.sprite.x = self.vx
        self.sprite.y = self.vy

    def key_player(self):
        if self.keyboard[key.W]:
            self.vy += 1
            self.dir_stand = "north"
            self.dir_run = "north"
        if self.keyboard[key.S]:
            self.vy -= 1
            self.dir_stand = "south"
            self.dir_run = "south"
        if self.keyboard[key.D]:
            self.vx += 1
            self.dir_stand = "east"
            self.dir_run = "east"
        if self.keyboard[key.A]:
            self.vx -= 1
            self.dir_stand = "west"
            self.dir_run = "west"
        if self.keyboard[key.W] and self.keyboard[key.D]:
            random1 = random.randint(1, 2)
            if random1 == 1:
                self.dir_stand = "north"
            else:
                self.dir_stand = "east"
            self.dir_run = "north-east"
        if self.keyboard[key.S] and self.keyboard[key.D]:
            random2 = random.randint(1, 2)
            if random2 == 1:
                self.dir_stand = "south"
            else:
                self.dir_stand = "east"
            self.dir_run = "south-east"
        if self.keyboard[key.W] and self.keyboard[key.A]:
            random3 = random.randint(1, 2)
            if random3 == 1:
                self.dir_stand = "north"
            else:
                self.dir_stand = "west"
            self.dir_run = "north-west"
        if self.keyboard[key.S] and self.keyboard[key.A]:
            random4 = random.randint(1, 2)
            if random4 == 1:
                self.dir_stand = "south"
            else:
                self.dir_stand = "west"
            self.dir_run = "south-west"

    def update(self):
        self.key_player()
        self.load_sprite()

Since to update the Player's sprite, I need to call the "load_sprite" function, which causes the animation to be constantly called. As a result, the same animation does not appear and remains still at the first frame. So how could I solve?

Edit: Modified the script, including the for loop. If you see, I modified the group, using Batch and OrderedGroup instead. In this way we can clearly see the problem I am having.

The problem I have is due to the fact that pyglet.sprite.Sprite is called in order to update which animation is executed. However, doing so at the same time, the animations are not shown exactly because of the constant call of pyglet.sprite.Sprite.

enter image description here


Solution

  • First, you can change the sprite's animation by setting its image attribute, instead of creating a new Sprite instance. Second, only change its animation when you actually need to. That is when the direction changes.

    I've added a simple example (pseudo) code to roughly show what I mean.

    def animation(image, da, width, height):
        frame_list = [image.get_region(x=width * i, y=height * da, width=46, height=58) for i in range(22)]
        image_animation = pyglet.image.Animation.from_image_sequence(frame_list, 0.10, True)
        return image_animation
    
    
    class Player:
    
        def __init__(self, standing_animations, running_animations):
            # ... code ...
            self.standing_animations = standing_animations   # List of animations 
            self.running_animations  = running_animations    # List of animations
            self.current_animation = standing_animations[0]  # Just to have a default animation
            self.previous_running_direction  = None
            self.previous_standing_direction = None
    
        def load_sprite(self):
            self.sprite.x = self.vx
            self.sprite.y = self.vy
    
            if self.previous_running_direction == self.dir_run and self.previous_standing_direction == self.dir_stand:
                return  # Don't do anything more in here
    
            if not self.keyboard[key.W] and not self.keyboard[key.S] and not self.keyboard[key.D] and not self.keyboard[key.A]:
                self.keyboard.clear()
                self.current_animation = self.standing_animations[self.sprite_stand]
            else:
                self.current_animation = self.running_animations[self.sprite_run]
    
            self.sprite.image = self.current_animation
    
        def update(self):
            self.previous_running_direction  = self.dir_run
            self.previous_standing_direction = self.dir_stand
            self.key_player()
            self.load_sprite()