Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Pygame Groups
#5
I've had another run at this group thing. I think the way I've got it now offers greater flexibility. With the next project I have in mind I'll be wanting to make use of the LayeredUpdates group, and this approach gives me the option of working with more or less groups. Its still under construction but most of the meat is on the bones.

In theory I should still be able to call upon all the groups to draw by calling a single method from the SpriteGroup class.

Here's one of my earlier attempts.
import pygame

pygame.init()

class ScreenObject(pygame.sprite.Sprite):
    pass
# ScreenObject() ------------------------------------------------------------



"""
One of my earlier attempts.
"""

import pygame

def enum(*args):
# ---------------------------------------------------------------------------
#   Used to create enumate values.
#
#       Example:
#           fruits =enum("APPLES", "ORANGES", "PEARS")
#           if fruits.APPLES ==fruits.ORANGES:
#               print("they're the same.")
#
# ---------------------------------------------------------------------------
    enums =dict(zip(args, range(len(args))))
    return type('Enum', (), enums)
# enum() --------------------------------------------------------------------


SPRITE_GROUPS =enum("BACKGROUND", "ZPLANES", "INTERFACE", "OVERLAY")


class SpriteGroup:
    """Screen Object Group  - manages sprites within a group.
    Returns: n/a
    Functions: add, remove, remove_all, draw
    Attributes: n/a"""
    
    CONST =SPRITE_GROUPS
    groups = {
        CONST.BACKGROUND:   pygame.sprite.Group(),
        CONST.ZPLANES:      pygame.sprite.Group(),
        CONST.INTERFACE:    pygame.sprite.Group(),
        CONST.OVERLAY:      pygame.sprite.Group(),
        }
    
    @classmethod
    def add(cls, group, item, z_plane =None):
        'Function - adds an item to a group.'
        result =False
        if group ==SPRITE_GROUPS.ZPLANES:
            if isinstance(z_plane, int):
                pygame.sprite.LayeredUpdates.add(item, z_plane)
                result =True
        else:
            cls.groups[group].add(item )
            result =True
        return result
    # add() -----------------------------------------------------------------
        

    @classmethod
    def remove(cls, group, item):
        'Function - removes the item from the group.'
        cls.groups[group].remove(item)
    # remove() --------------------------------------------------------------
    

    @classmethod
    def remove_all(cls, item):
        'Function - removes the item from all of the groups.'
        #   Build up a list of all the groups managed by this class.
        grp_lst =[]
        grp_lst.append(SPRITE_GROUPS.BACKGROUND)
        grp_lst.append(SPRITE_GROUPS.ZPLANES)
        grp_lst.append(SPRITE_GROUPS.INTERFACE)
        grp_lst.append(SPRITE_GROUPS.OVERLAY)
        #   _.

        #   Search all of our groups and remove the item when found.
        for grp in grp_lst:
            if grp.has(item):
                grp.remove()
        #   _.
    # remove_all() ----------------------------------------------------------
    
        
    @classmethod
    def draw(cls, surface):
        'Function - draws all the sprite items from all of managed groups.'
        CONST =SPRITE_GROUPS
        cls.groups[CONST.BACKGROUND].draw(surface)
        cls.groups[CONST.ZPLANES].draw(surface)
        cls.groups[CONST.INTERFACE].draw(surface)
        cls.groups[CONST.OVERLAY].draw(surface)
    # draw() ----------------------------------------------------------------
    
# SpriteGroup ---------------------------------------------------------------


item =pygame.sprite.Sprite()
CONST =SPRITE_GROUPS
SpriteGroup.add( CONST.BACKGROUND, item ) 

item =pygame.sprite.Sprite()
SpriteGroup.add( CONST.ZPLANES, item ) 


SpriteGroup.CONST.BACKGROUND
SPRITE_GROUPS.BACKGROUND 
But the one I want to use is listed below.
import pygame

if __name__ =="__main__":
    pygame.init()

from clsExceptions import * # My custom error.



class SpriteGroup:
    """Class - manages groups from the pygame.sprite.Group collection.
    Returns: n/a
    Functions: add_item, add_group, remove_item, remove_all_items, draw, init, groups
    Attributes: n/a"""
    _groups_list =[] 
    _grp_cntr_id =0
    _surface =None      # Pygame surface.


    @classmethod
    def _new_group_name(cls):
        for tries in range(1000):
            cls._grp_cntr_id +=1
            name ="Group" +str(cls._grp_cntr_id)
            grp =cls._search_groups(name)
            if grp ==None:
                #   The name is unique.
                fnd =False
                break
        else:
            fnd =True

        if fnd:
            #   A unique name could not be found, so lets return nothing.
            name =None
            
        return name
    # _new_group_name() -----------------------------------------------------


    @classmethod
    def _search_groups(cls, name):
        for grp in cls._groups_list:
            if grp.name.lower() ==name.lower():
                fnd =True
                break
        else:
            fnd =False
            
        if not fnd:
            grp =None

        return grp
    # _search_groups() ------------------------------------------------------
    
    
    @classmethod
    def add_group(cls, name =None, layered_updates =False):
        'Function - adds a new group to the collection.'
        #   NOTE(S):
        #   -------
        #   There can be only ONE.
        #   
        #   The Layered-Updates is a special group, and there can only be
        #   one.

        if cls._surface ==None:
            #   The class hasn't been initialised yet.
            cls.init()
            
        
        if isinstance(name, str):
            #   Check if they passed an empty string.
            if len(name.strip()) <1:
                raise CustomError("Group name cannont be an empty string.")
        else:
            #   Check if a name was passed.
            if not name ==None:
                #   Something other than a string was passed as a name.
                raise TypeError("Wrong variable type passed.  Expected 'name' to be string.")
                
        if not isinstance(layered_updates, bool):
            raise TypeError("Wrong variable type passed.  Expected 'layered_updates' to be boolean.")

        if name ==None:
            #   Generate a unique name.
            name =cls._new_group_name()
            
        if name ==None:
            raise CustomError("Failed to generate a unique group name.")

        #   Have a look at all the groups in our collection to see if the
        # name supplied is already been used by a group.
##        for grp in cls._groups_list:
##            if grp.name.lower() ==name.lower():
##                fnd =True
##                break
##        else:
##            fnd =False
##            
##        if fnd:
##            raise CustomError("A group with that name already exists.")

        grp =cls._search_groups(name)
        if not grp ==None:
            raise CustomError("A group with that name already exists.")
        #   _.

        #   Search through our collection of groups to see if any of them
        # have been designated as being the Layered-Updates group.
        for grp in cls._groups_list:
            if grp.layered_updates:
                fnd =True
                break
        else:
            fnd =False
        #   _.

        if fnd ==True and layered_updates ==True:
            raise CustomError("There can only be one.  {0} has already been designated as the layered-Updates groups.".format(grp.name))

        #   Create the group and add it to the list.
        grp =pygame.sprite.Group()
        grp.name =name
        grp.layered_updates =layered_updates
        cls._groups_list.append(grp)
        #   _.
        
        return grp
    # add_group() -----------------------------------------------------------


    @classmethod
    def add_item(cls, group, item, z_plane =None):
        'Function - adds a sprite item to a group.'
        if not isinstance(item, pygame.sprite.Sprite):
            raise TypeError

        if not hasattr(item, "layer"):
            #   NOTE(S):
            #   -------
            #   The default layer is 0.
            #
            #   If the sprite you add has an attribute 'layer' then that layer
            # will be used when applying it to the layered updates group.
            if not z_plane ==None:
                item.layer =z_plane # Create the attribute.
            else:
                item.layer =0
                
        if isinstance(group, pygame.sprite.Group):
            group.add(item)
        elif isinstance(group, str):
            for grp in cls._groups_list:
                if grp.name.lower() ==group.lower():
                    fnd =True
                    break
            else:
                fnd =False

            if fnd:
                if grp.layered_updates:
                    if z_plane ==None:
                        #   Allow pygame to add the item to layer based on
                        # the value of the items 'layer' attribute.
                        pygame.sprite.LayeredUpdates.add(item)
                    else:
                        #   Make a request to pygame for the item to be added
                        # to the specified layer.
                        pygame.sprite.LayeredUpdates.add(item, z_plane)
                else:
                    grp.add(item)
    # add_item() ------------------------------------------------------------


    @classmethod
    def init(cls):
        'Function - initialises the class.'
        return_status =False
        
        #   The pygame draw methods require a reference to a surface.  By
        # calling this routine we ensure that a reference can easily passed.
        cls._surface =pygame.display.get_surface()
        return_status =True

        return return_status
    # init() ----------------------------------------------------------------
    
    
    @classmethod
    def draw(cls):
        'Function - paints any screen object found in the groups onto the screen.'
        return_status =False
        
        for grp in cls._groups_list:
            if grp.layered_updates:
                #   Call the appropriate draw method.
                pygame.sprite.LayeredUpdates.draw(cls._surface)
            else:
                grp.draw()
                
        return_status =True

        return return_status
    # draw() ---------------------------------------------------------------


    @classmethod
    def groups(cls):
        'Function - returns a diction of all the group in the collection.'
        group_dict ={}
        
        for grp in cls._groups_list:
            group_dict[grp.name] =grp

        return group_dict
    # groups() --------------------------------------------------------------


    @classmethod
    def remove_all_items(cls, item):
        'Function - removes the item from all the groups.'
        for grp in cls._groups_list:
            cls.remove_item(grp.name, item)
    # remove_all_items() ----------------------------------------------------
    

    @classmethod
    def remove_item(cls, group, item):
        'Function - removes the item from a group.'
        return_status =False
            
        if isinstance(group, pygame.sprite.Group):
            group.remove(item)
        elif isinstance(group, str):
            for grp in cls._groups_list:
                if grp.name.lower() ==group.lower():
                    fnd =True
                    break
            else:
                fnd =False

            if fnd:
                if grp.layered_updates:
                    sprites_list =pygame.sprite.LayeredUpdates.remove_sprites_of_layer(item.layer)
                    for index in range(len(sprites_list)):
                        if sprite_list[index] ==item:
                            fnd =True
                    else:
                        fnd =False

                    if fnd:
                        removed_item =sprites_list.pop(index)
                        pygame.sprite.LayeredUpdates.add(sprite_list)
                        return_status =True
                else:
                    grp.remove(item)
                    return_status =True
                                       
        return return_status
                                       
    # remove_item() --------------------------------------------------------------
    
# SpriteGroup ------------------------------------------------------------------


if __name__ =="__main__":
    grp ={}
    
    #   Generate a new group by just providing a name.
    SpriteGroup.add_group("background")
    #   _.
    
    #   Generate new groups and store them in a dict object.
    grp["z_layers"] =SpriteGroup.add_group("z_layers", layered_updates=True)
    grp["interface"] =SpriteGroup.add_group("interface")
    #   _.

    #   Generate a new group and modiying the group name later.
    new_group =SpriteGroup.add_group()
    new_group.name ="overlay"
    #   _.

    #   Obtain a full list of groups stored in our collection.
    full_grp_listing =SpriteGroup.groups()
    #   _.
        
    #   Generate a new sprite and add it to the special layers group
    # (LayeredUpdates).
    player =pygame.sprite.Sprite()
    SpriteGroup.add_item("z_layers", player)
    #   _.
    
    print("Group '{0}' layered group status ={1}.".format(grp["z_layers"].name, grp["z_layers"].layered_updates))
    print("\nThe common group var ('grp') has the following groups:")
    for g in grp:
        print("   ",g)
    print()
        
    print("The full list of groups returned by the class is as follows:")

    for g in full_grp_listing:
        print("   ",g)

    print("\nPS: Not sure why the groups are listed in Shell output the way they are.\n    The groups aren't listed in the order they'll draw to the screen.")
and here's my [clsExceptions] exeption class file.
class CustomError(Exception):
    pass
Output:
Group 'z_layers' layered group status =True. The common group var ('grp') has the following groups: z_layers interface The full list of groups returned by the class is as follows: z_layers background interface overlay PS: Not sure why the groups are listed in Shell output the way they are. The groups aren't listed in the order they'll draw to the screen.
Reply


Messages In This Thread
Pygame Groups - by microphone_head - Aug-27-2018, 03:28 PM
RE: Pygame Groups - by Windspar - Aug-27-2018, 08:20 PM
RE: Pygame Groups - by microphone_head - Aug-28-2018, 07:30 AM
RE: Pygame Groups - by microphone_head - Aug-28-2018, 09:53 AM
RE: Pygame Groups - by microphone_head - Aug-28-2018, 10:40 PM

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020