Pygame - Keep Sprites from overlapping
Question: I'm making a space invaders type game using python and pygame. I currently
have where I can move my user ship back and forth along the bottom with my
arrow keys, and my enemies spawn and move back and forth across the top. My
problem is with the enemies. I have it set so if they hit 100 pixels from the
edge of the game screen, they start going back the other direction. This
results in an overlap while the ones that have hit the limit are already
heading back and the ones that haven't hit the limit are still going the
original direction. I want it so that when the enemies on the edge hit the
limit, all the enemies immediately go back in the other direction, just like
in space invaders. My code is below. I have KeepFormation under enemies to try
to do what I wanted, but it isn't working. Thank you!

    
    
    from pygame import *
    
    size_x = 900
    size_y = 650
    
    class Object:
        def disp(self, screen):
            screen.blit(self.sprite, self.rect)
    
    class User_Ship(Object):
        def __init__(self):
            self.sprite = image.load("mouse.bmp")
            self.rect = self.sprite.get_rect()
            self.rect.centerx = size_x/2
            self.rect.centery = size_y - 40
            self.count = 0
            self.move_x = 0
            self.move_y = 0
    
        def checkwith(self, otherrect):
            if self.rect.colliderect(otherrect):
                exit()
    
        def cycle(self):
            self.rect.centerx += self.move_x
            if self.rect.centerx < 100:
                self.rect.centerx = 100
            if self.rect.centerx > size_x - 100:
                self.rect.centerx = size_x - 100
    
            self.rect.centery += self.move_y
            if self.rect.centery < 0:
                self.rect.centery = 800
    
        def right(self):
            self.move_x += 10
    
        def left(self):
            self.move_x -= 10
    
        def stop_x(self):
            self.move_x = 0
    
        def stop_y(self):
            self.move_y = 0
    
        def shoot(self):
            if keys[pygame.K_SPACE]:
                self.Bullet.x = self.rect.centerx
                self.Bullet.y = self.rect.centery
                self.Bullet.speed = 10
    
    class Enemys(Object):
        def __init__(self):
            self.sprite = image.load("ball.bmp")
            self.rect = self.sprite.get_rect()
            self.rect.centerx = size_x/3
            self.rect.centery = 200
            self.mval = -2
    
        def KeepFormation(self, otherrect):
            if self.rect.colliderect(otherrect):
                self.rect.centerx += 5
    
        def cycle(self):
            self.rect.centerx += self.mval
            if self.rect.centerx < 100:
                self.mval = 2
            elif self.rect.centerx > (size_x - 100):
                self.mval = -2
    
    class Bullet(Object):
        def __init__(self):
            self.sprite = image.load("missile.png")
            self.rect = self.sprite.get_rect()
            self.rect.centerx = -100
            self.rect.centery = 100
            self.speed = 0
    
    EnemyList = []
    for i in range(14):
        EnemyList.append(Enemys())
        EnemyList[i].rect.centerx = (i) * 50
    #   EnemyList[i].count = i * 20 
    
    init()
    screen = display.set_mode((size_x, size_y))
    m = User_Ship()
    en = Enemys()
    b = Bullet()
    clock = time.Clock()
    
    while True:
        for e in event.get():
            if e.type == QUIT:
                quit()
            if e.type == KEYDOWN:
                if e.key == K_RIGHT:
                    m.right()
                elif e.key == K_LEFT:
                    m.left()
            if e.type == KEYUP:
                if e.key == K_RIGHT or e.key == K_LEFT:
                    m.stop_x()
    
        m.cycle()
        screen.fill((255,255,255))
        for enemy in EnemyList:
            enemy.cycle()
            enemy.disp(screen)
            enemy.KeepFormation(enemy.rect)
        m.disp(screen)
        b.disp(screen)
        display.flip()
        clock.tick(60)  
    


Answer: Add a flag in

    
    
    def cycle(self):
        self.rect.centerx += self.mval
        if self.rect.centerx < 100:
            self.mval = 2
            return True # Direction change!
        elif self.rect.centerx > (size_x - 100):
            self.mval = -2
            return True
        return False # no change
    

But then you need to make this change happen only once. Thus...

    
    
    done_once = False
    for enemy in EnemyList:
        if not done_once:
            change = enemy.cycle()
        if change and not done_once:
            # call the new enemy function that changes every enemy's direction
            done_once = True
        enemy.disp(screen)
        enemy.KeepFormation(enemy.rect)
    

Process this flag such that everyone should change direction now. You may need
another function & direction_tracking variable in your `Enemy` class. Good
luck & good job!

