Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!
  • Guest, before posting your code please take these rules into consideration:
    • It is required to use our BBCode feature to display your code. While within the editor click < / > or >_ and place your code within the BB Code prompt. This helps others with finding a solution by making it easier to read and easier to copy.
    • You can also use markdown to share your code. When using markdown your code will be automatically converted to BBCode. For help with markdown check out the markdown guide.
    • Don't share a wall of code. All we want is the problem area, the code related to your issue.


    To learn more about how to use our BBCode feature, please click here.

    Thank you, Code Forum.

Python Helping someone with a pygame project

menator01

Gold Coder
I've been helping someone with their project and decided to give it my own touch by using classes and implementing a scoring system for the game among other things.
Anyhow I've seem to hit a snag. I'm using pygame's collide group to detect collision and for some reason it will collide even if the sprites don't touch. I've checked the radius of the sprites and they do not collide. Any help would be great.
You can get the image and sound files from github
or download from my python scripts site

Updated code
FlappyBird.py
Python:
import pygame
from random import choice
from pathlib import Path
from os import listdir
import sqlite3 as sq
import tkinter as tk
 
# Get path to executing script
path = Path(__file__).parent
 
# some pygame variables
pygame.init()
 
clock = pygame.time.Clock()
fps = 60
 
screen_size = (1280, 740)
screen = pygame.display.set_mode(screen_size)
 
# Colors
black = (0,0,0)
white = (255,255,255)
 
# Create sprite groups
bird_sprite = pygame.sprite.Group()
pipe_sprite = pygame.sprite.Group()
allsprites = pygame.sprite.Group()
 
# Create channels
channel1 = pygame.mixer.Channel(1)
channel2 = pygame.mixer.Channel(2)
channel3 = pygame.mixer.Channel(3)
channel4 = pygame.mixer.Channel(4)

# Load logo image
logo = pygame.image.load(f'{path}/assets/my-python-logo.png').convert()
logo = pygame.transform.scale(logo,(logo.get_width()/2, logo.get_height()/2))
 
# Define the start and end page
def page():
    screen.fill((0,0,0))
    img = pygame.image.load(f'{path}/assets/message.png')
    img = pygame.transform.scale2x(img)
    screen.blit(img, (screen_size[0]/2 - img.get_width()/2, screen_size[1]/2 - img.get_height()/2))
 
    font = pygame.font.SysFont('04B_19.TTF', 40)
    text = 'Press spacebar to begin - esc to exit. During game play press q to end game.'
    surf = font.render(text, True, (150,150,150))
    screen.blit(surf, (screen_size[0]/2-font.size(text)[0]/2, screen_size[1]-font.size(text)[1]*2.5))
 
    screen.blit(logo, (50,20))

    surf = font.render(chr(169), True, (250,130,0))
    screen.blit(surf, (35, 20))

    surf = font.render(chr(8482), True, (250,130,0))
    screen.blit(surf, (160, 25))

    pygame.display.update()
 
 
def gameover(score):
    row = screen_size[1]/2 - screen_size[1]/2*0.2
    screen.fill((0,0,0))

    screen.blit(logo, (50,20))
    font = pygame.font.SysFont(f'{path}/04B_19.TTF', 35)

    surf = font.render(chr(169), True, (250,130,0))
    screen.blit(surf, (35, 20))

    surf = font.render(chr(8482), True, (250,130,0))
    screen.blit(surf, (160, 25))

    img = pygame.image.load(f'{path}/assets/gameover.png').convert()
    img = pygame.transform.scale2x(img)
    img.set_colorkey(white)
    screen.blit(img, ((screen_size[0]/2-img.get_width()/2, screen_size[1]*0.2)))
 
    font = pygame.font.SysFont(f'{path}/04B_19.TTF', 50)
    text1 = f'Your Final Score: {score}'
    row_align = screen_size[0]/2 - font.size(text1)[0]/2
    surface = font.render(text1, True, (255,180,0))
    screen.blit(surface, (screen_size[0]/2 - font.size(text1)[0]/2, screen_size[1]*0.38-35))
 
    font = pygame.font.SysFont(f'{path}/04B_19.TTF', 60)
    text2 = 'Press enter to play again or esc to quit'
    surface = font.render(text2, True, (255,180,0))
    screen.blit(surface, (screen_size[0]/2 - font.size(text2)[0]/2, screen_size[1]*0.85))
 
    font = pygame.font.SysFont(f'{path}/04B_19.TTF', 40)
    font.set_underline(True)
    text3 = 'Top Scores'
    surface = font.render(text3, True, (255,0,0))
    screen.blit(surface, (screen_size[0]/2-font.size(text3)[0]/2, row+10))
 
    entries = db.top5()
    if entries:
        font = pygame.font.SysFont(f'{path}/04B_19.TTF', 40)
        for entry in entries:
            name, score = entry
            row += 40
            surface = font.render(f'{name}: {score}', True, (255,255,255))
            screen.blit(surface, ((screen_size[0]/2-font.size(text3)[0]/2)+30, row+10))
        row = 105
    
    pygame.display.update()
 
        
class Database:
    def __init__(self):
        self.connect = sq.connect(f'{path}/bird_score.db')
        self.cursor = self.connect.cursor()
 
    def insert(self, args):
        ''' Method will write player initials and score to database '''
        if len(args) == 2:
            query = 'insert into flappy (name, score) values (?,?)'
 
            self.cursor.execute(query, args)
            self.connect.commit()
            return True
        return False
        
 
    def top5(self):
        ''' Method will get top 5 scores '''
        query = '''select * from flappy order by score desc limit 5'''
        data = [info for info in self.cursor.execute(query)]
        if len(data) > 0:
            return data
        return False
        
        
 
    def create(self):
        ''' Method will create a table in the database if not exists '''
        query = '''create table if not exists flappy (
                    name text,
                    score integer
                )'''
 
        self.connect.execute(query)
        self.connect.commit()
 
 
class Background:
    ''' Background class handles the creation of backgrounds
        be it day or night. Also handles the floor creation '''
    def __init__(self):
        # set an instance variable for floor x pos
        self.floor_x = 0
        
    def day(self):
        ''' Method changes background to day mode '''
        bg = pygame.image.load(f'{path}/assets/background-day.png').convert()
        bg = pygame.transform.scale(bg, screen_size)
        return bg
 
    def night(self):
        ''' Method changes background to night mode '''
        bg = pygame.image.load(f'{path}/assets/background-night.png').convert()
        bg = pygame.transform.scale(bg, screen_size)
        return bg
 
    def floor(self):
        ''' Method displays the floor image '''
        _floor = pygame.image.load(f'{path}/assets/base.png').convert()
        _floor = pygame.transform.scale(_floor, (screen_size[0], 80))
 
        screen.blit(_floor, (self.floor_x,screen_size[1]-80))
        screen.blit(_floor, (self.floor_x + screen_size[0], screen_size[1]-80))
 
 
class Pipe(pygame.sprite.Sprite):
    ''' Class for creating pipes '''
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(f'{path}/assets/pipe-green.png')
        self.rect = self.image.get_rect()
        self.rect.x = screen_size[0] + self.rect.width
        self.rect.bottom = screen_size[1] - 80
 
        self.active = True
 
        # Add pipe object to sprite groups
        pipe_sprite.add(self)
        allsprites.add(pipe_sprite)
 
    def draw_pipe(self, top, bottom, rotate=False):
        ''' Method for drawing the pipes '''
        if rotate:
            self.rect.height = top
            self.image = pygame.transform.scale(self.image, (self.rect.width, self.rect.height))
            self.image = pygame.transform.flip(self.image, False, True)
            self.rect.x = screen_size[0] + self.rect.width
            self.rect.top = 0

        else:
            self.rect.height = bottom
            self.image = pygame.transform.scale(self.image, (self.rect.width, self.rect.height))
            self.rect = self.image.get_rect()
            self.rect.x = screen_size[0] + self.rect.width
            self.rect.bottom = screen_size[1] - 80
 
    def update(self):
        ''' Method updates the sprite object '''
        # If sprite goes off screen, kill it
        if self.rect.right <= 0:
            self.kill()
 
        for sprite in bird_sprite:
            if sprite.rect.left >= self.rect.right and self.active:
                # Setting score to 0.5 because ther are two pipes.
                # We only want one point
                score.score += 0.5
                self.active = False
                channel2.play(sound.play('point'))
                
        # Scrolls the pipes across the screen
        self.rect.x -= speed
 
 
class Bird(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.gravity = 0.25
        
        # Load the images
        down = pygame.image.load(f'{path}/assets/bluebird-downflap.png').convert_alpha()
        mid = pygame.image.load(f'{path}/assets/bluebird-midflap.png').convert_alpha()
        up = pygame.image.load(f'{path}/assets/bluebird-upflap.png').convert_alpha()
 
        # Scale images
        down = pygame.transform.scale2x(down)
        mid = pygame.transform.scale2x(mid)
        up = pygame.transform.scale2x(up)
 
        # Load the frame list with images and set index
        self.frames = [down, mid, up]
        self.index = 0
 
        # Load first image
        self.image = self.frames[self.index]     
 
        # Get image rect and set start position to middle of screen
        self.rect = self.image.get_rect()
        self.rect.x = screen_size[0]/2 - self.rect.width/2
        self.rect.y = screen_size[1]/2 - self.rect.height/2
 
        # Add sprite to sprite groups
        # bird_sprite for collison detection and allsprites for animation
        bird_sprite.add(self)
        allsprites.add(bird_sprite)
 
    def rotate(self, angle):
        ''' Method for pointer bird up or down '''
        img = pygame.transform.rotate(self.frames[self.index], angle)
        rect = img.get_rect(center=self.rect.center)
        return img
 
    def update(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_SPACE]:
            self.image = self.rotate(30)

            # Speed for when pointing up           
            self.rect.y -= 3.6
        else:
            self.image = self.rotate(-30)
        
        # Default speed when going down
        self.rect.y += 2
 
        # Keep bird from flying off top of screen
        if self.rect.top <= 10:
            self.rect.top = 10
            self.image = self.frames[self.index]
 
 
class Score:
    def __init__(self):
        self.score = 0
 
    def show(self):
        font = pygame.font.SysFont('04B_19.TTF', 35)
        text = f'Score: {self}'
        surface = font.render(text, True, (0,0,0))
        screen.blit(surface, (screen_size[0]*0.01, screen_size[1]*0.02))
 
    def __repr__(self):
        score = int(self.score)
        return str(score)
 
 
class Sound:
    def __init__(self):
        self.files = listdir(f'{path}/sound')
 
    def play(self, sfx):
        for file in self.files:
            if sfx in file:
                return pygame.mixer.Sound(f'{path}/sound/{file}')
 
    def play_wings(self, fx):
        for file in self.files:
            if fx in file:
                pygame.mixer.music.load(f'{path}/sound/{file}')
                pygame.mixer.music.play(-1)
        
 
class Window:
    def __init__(self, parent):
        self.parent = parent
        font = (None, 14, 'normal')
        label = tk.Label(parent, text='Enter Initials:', font=font)
        label.grid(column=0, row=0, padx=(4,2), pady=(4,2))
 
        self.myvar = tk.StringVar()
        self.entry = tk.Entry(parent, textvariable=self.myvar, font=font)
        self.entry.grid(column=1, row=0, padx=(2,4), pady=2)
        self.entry.focus()
 
        self.btn = tk.Button(parent, text='Submit', command=self.submit)
        self.btn.grid(column=0, columnspan=2, sticky='e', padx=4, pady=(2,4))
 
        self.entry.bind('<Return>', self.submit)
        self.entry.bind('<KP_Enter>', self.submit)
 
        self.myvar.trace('w', self.validate)
 
    def validate(self, var=None, index=None, mode=None):
        if len(self.myvar.get().strip()) == 0:
            return False
        elif len(self.myvar.get().strip()) > 3:
            self.entry.delete(3, 'end')
            return False
        else:
            return True
    
    def submit(self, event=None):
        if self.validate():
            db.insert((self.myvar.get().strip(), score.score))
            self.parent.destroy()
        else:
            return False
 
# Initialize some classes
sound = Sound()
score = Score()
 
# Inialize Database class and create the table
# if it doesnt exist
db = Database()
db.create()
# Game Timers
mode = True
 
# Mode timer changes from day/night every 30 seconds
timer = pygame.USEREVENT + 1
pygame.time.set_timer(timer, 30000)
 
# Timer for creating pipes
create_pipe = pygame.USEREVENT + 2
pygame.time.set_timer(create_pipe, 4500)
 
# Timer for bird wing flap
birdflap = pygame.USEREVENT + 3
pygame.time.set_timer(birdflap, 100)
 
# Set default background to day mode
background = Background()
bg = background.day()
 
# background speed
speed = 3

# Setting for going to the start page
gamestate = 'startpage'
running = True
active = True

while running:
######## Event Section ##########################
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
 
        if event.type == birdflap:
            bird.index += 1
            if bird.index >= len(bird.frames):
                bird.index = 0
            bird.image = bird.frames[bird.index]
            
        # Event for creating pipes
        if event.type == create_pipe:
            top = choice([135,275,415])
            bottom = 0
 
            # We want the correct size for the bottom pipe to match top
            if top == 135:
                bottom = 415
            elif top == 275:
                bottom = 275
            else:
                bottom = 135
 
            # Create the pipes
            pipe = Pipe()
            pipe.draw_pipe(top, bottom)
            pipe = Pipe()
            pipe.draw_pipe(top, bottom, True)
 
        # Away to exit the game with esc key
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                running = False
 
            if event.key == pygame.K_SPACE:
                if active:
                    channel3.play(sound.play('swooshing'))
                    channel4.play(sound.play('wing'), loops = -1)
                
            if event.key == pygame.K_RETURN or event.key == pygame.K_KP_ENTER:
                gamestate = 'play'
                score.score = 0
 
            if event.key == pygame.K_q:
                gamestate = 'startpage'
 
 
            # Check if we are in day or night mode. Change accordingly
            if event.key == pygame.K_m:
                if mode:
                    bg = background.night()
                    mode = False
                elif not mode:
                    bg = background.day()
                    mode = True
 
        if event.type == timer:
            mode = False if mode else True
            if mode:
                bg = background.day()
            else:
                bg = background.night()
 
################ gamestate section ##################
    # Are we on the start page or play
    if gamestate == 'play':
        screen.fill((0,255,0))
        screen.blit(bg, (0,0))
        score.show()
        active = True
 
        # Disply and scroll the floor background image
        background.floor_x -= speed
        if background.floor_x <= -screen_size[0]:
            background.floor_x = 0
        background.floor()
 
        # If bird crashes into ground, go to gameover page
        if bird.rect.bottom >= screen_size[1] - 80:
            bird.kill()
            channel1.play(sound.play('hit'))
            channel1.queue(sound.play('die'))
            gamestate = 'form'
 
 
        collisions = pygame.sprite.groupcollide(bird_sprite, pipe_sprite, True, False)
        if collisions:
            channel1.play(sound.play('hit'))
            channel1.queue(sound.play('die'))
            gamestate = 'form'
 
        # Update all game sprites
        allsprites.update()
        allsprites.draw(screen)
 
        pygame.display.update()
        clock.tick(fps)
 
    # gamestate has changed go to start/end page
    if gamestate == 'startpage':
        # Go to start page and clear all sprite groups
        page()
        pipe_sprite.empty()
        allsprites.empty()
        bird_sprite.empty()
        bird = Bird()
        active = False
 
    if gamestate == 'gameover':
        # Go to game over page
        gameover(int(score.score))
        pipe_sprite.empty()
        allsprites.empty()
        bird_sprite.empty()
        bird = Bird()
        active = False
        
 
    if gamestate == 'form':
        if score.score > 0:
            channel4.stop()
            root = tk.Tk()
            Window(root)
            root.mainloop()
        active = False
        channel4.stop()
        gamestate = 'gameover'
        
pygame.quit()
 
Last edited:

New Threads

Buy us a coffee!

Back
Top Bottom