Python Forum
pygame, sprites, and rects
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
pygame, sprites, and rects
#1
Taking the following snippet, is it possible to create a pygame.Rect() and add it to a sprite group?
Been looking for awhile and not really found anything. Maybe just tired. lol.

In the following example, I can get func1 to display but, not sure how to add func2 to a sprites group.
Any help would be great.

import pygame
import sys

# Initiate pygame
pygame.init()

# Create some settings
size = (1280, 720)
screen = pygame.display.set_mode(size)
pygame.display.set_caption('Shmup')
clock = pygame.time.Clock()

hud = pygame.sprite.Group()

def func1():
    arect = pygame.Rect(30,10,125,25)
    pygame.draw.rect(screen, 'green', arect)
    
def func2():
    arect = pygame.Rect(30, 35, 125, 25)
    hud.add(arect)

while True:
    event = pygame.event.poll()
    if event.type == pygame.QUIT:
        sys.exit()

    hud.update()
    hud.draw(screen)

    func1()

    pygame.display.update()
    clock.tick(60)

pygame.quit()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#2
Things in a sprite group must act like sprites. A sprite must have a rectangle attribute, not be a rectangle. A sprite must have an image (a surface), though I think you can get around that one by supplying your own update and draw methods. A function is not going to satisfy the requirements of being sprite-like.
Reply
#3
(Oct-25-2023, 04:25 AM)menator01 Wrote: Taking the following snippet, is it possible to create a pygame.Rect() and add it to a sprite group?
Been looking for awhile and not really found anything. Maybe just tired. lol.

In the following example, I can get func1 to display but, not sure how to add func2 to a sprites group.
Any help would be great.

import pygame
import sys

# Initiate pygame
pygame.init()

# Create some settings
size = (1280, 720)
screen = pygame.display.set_mode(size)
pygame.display.set_caption('Shmup')
clock = pygame.time.Clock()

hud = pygame.sprite.Group()

def func1():
    arect = pygame.Rect(30,10,125,25)
    pygame.draw.rect(screen, 'green', arect)
    
def func2():
    arect = pygame.Rect(30, 35, 125, 25)
    hud.add(arect)

while True:
    event = pygame.event.poll()
    if event.type == pygame.QUIT:
        sys.exit()

    hud.update()
    hud.draw(screen)

    func1()

    pygame.display.update()
    clock.tick(60)

pygame.quit()

pygame.sprite.Group only except sprites.
sprite = pygame.sprite.Sprite()
# Sprite must have image and a rect variable.
sprite.rect = pygame.Rect(30, 35, 125, 25)
sprite.image = pygame.Surface(sprite.rect.size)
sprite.image.fill('green')

hud.add(sprite)
When you learn classes.
class MySprite(pygame.sprite.Sprite):
    def __init__(self, color, x, y, w, h):
        super().__init__()
        self.rect = pygame.Rect(x, y, w, h)
        self.image = pygame.Surface(self.rect.size)
        self.image.fill(color)

hud.add(MySprite('green', 30, 35, 125, 25))
99 percent of computer problems exists between chair and keyboard.
Reply
#4
I figured out how to do it but, took another route as I wasn't able to figure out how to make the bar decrease and change colors the lower it got.
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#5
Something like this?
import pygame
import sys
import math


class StatusBar(pygame.sprite.Sprite):
    def __init__(
        self,
        rect=(0, 0, 100, 10),
        max_=1,
        min_=0,
        fg="white",
        bg="black",
        center=None,
        colormap=None,
    ):
        super().__init__()
        self.rect = pygame.Rect(rect)
        self.bar = pygame.Rect(0, 0, self.rect.width, self.rect.height)
        self.image = pygame.Surface(self.rect.size)
        self.colormap = dict(sorted(colormap.items())) if colormap else {}
        self.fg = fg
        self.bg = bg
        self.max = max_
        self.min = min_
        self.range = self.max - self.min
        self.center = center
        self._value = 0

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, new_value):
        self._value = max(self.min, min(new_value, self.max))

    def update(self):
        if self.center is None:
            self.bar.width = int(
                self.rect.width * (self.value - self.min) / (self.max - self.min)
            )
        else:
            a = self.rect.width * (self._value - self.min) / self.range
            b = self.rect.width * (self.center - self.min) / self.range
            self.bar.x = min(a, b)
            self.bar.width = abs(a - b)
        self.image.fill(self.bg)
        fg = self.fg
        for value, color in self.colormap.items():
            if value <= self._value:
                fg = color
            else:
                break
        pygame.draw.rect(self.image, fg, self.bar)


pygame.init()
screen = pygame.display.set_mode((220, 90))
b1 = StatusBar((10, 10, 200, 10), min_=-1)
b2 = StatusBar(
    (10, 30, 200, 10), min_=-1, colormap={-1: "green", 0: "yellow", 0.5: "red"}
)
b3 = StatusBar((10, 50, 200, 10), min_=-1, center=0)
b4 = StatusBar(
    (10, 70, 200, 10),
    min_=-1,
    center=0,
    colormap={
        -1: "red",
        -0.75: "orange",
        -0.5: "yellow",
        -0.25: "green",
        0.25: "yellow",
        0.5: "orange",
        0.75: "red",
    },
)
group = pygame.sprite.Group()
group.add((b1, b2, b3, b4))
clock = pygame.time.Clock()
for i in range(1000):
    event = pygame.event.poll()
    if event.type == pygame.QUIT:
        sys.exit()
    b1.value = math.sin(math.radians(i))
    b2.value = math.cos(math.radians(i))
    b3.value = math.sin(math.radians(i))
    b4.value = math.cos(math.radians(i))
    group.update()
    group.draw(screen)
    pygame.display.update()
    clock.tick(60)
pygame.quit()
Reply
#6
@deanhystad That looks good. I will have to study the math module. I'm not really familiar with cos and sin. Your example will help a great deal. Thanks.
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#7
I've uploaded the files to git if anyone wants to check it out. Other than some unknown bugs I haven't come across yet, only thing left is for me too add the form to enter scores.
https://github.com/menator01/shmup2


I've finished the re-write.
The old script had everything in one file. The re-writes has modules that are imported. I broke down the classes into modules.
Depending on what that class does. There still may be some bugs that I've not found.
I've added some things too.
On the powerups that fall, if you catch one (there are only two - laser and shield), icons will apprear in the bottom right of the game window. They will disappear when done. Example - if you catch the shield icon and it gets destroyed, the icon will disappear. The laser is a timed powerup. To keep them, just have to keep catching them when they fall.

Requirements:
  • python 3.12
  • pygame 2.5.2
  • sqlite3

I welcome any feedback.


I've changed the shmup.py. Added a couple variables to set the range of mobs spawned. Had it hard coded so to speak to 8 - 10 mobs.
Now it just can be set the min_mob and max_mob right before the while loop starts.

import pygame
from modules import abient, pages, character, mysprites, display
from modules.character import player_sprite, mob_sprite, weapon_sprite
from modules.powerups import PowerUp, power_sprite, Shield, shield_sprite
from modules.effects import Explosion
import os
from random import random, randrange

# Initiate pygame
pygame.init()

# pygame clock
clock = pygame.time.Clock()

# Get the working directory path
path = os.path.realpath(os.path.dirname(__file__))

# Setup screen window
screen = pygame.display.set_mode((1280, 720))
pygame.display.set_caption('Shmup')

# Background music and image
music = abient.Music(path)
bgimg = pygame.image.load(f'{path}/media/images/backgrounds/space.png')
bgimg_rect = bgimg.get_rect()

# Set some variables
gameover = True
doform = False

# range of mobs to spawn
min_mob = 10
max_mob = 15

# Start the game loop
while True:
    # Start the background music
    if not pygame.mixer.music.get_busy():
        music.play()

    # Display the background image
    screen.blit(bgimg, bgimg_rect)

    # Poll pygame events
    event = pygame.event.poll()

    # Ways to exit the game
    if event.type == pygame.QUIT:
        break

    # Has a key been pressed
    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_ESCAPE:
            break

        # Fire weapons
        if event.key == pygame.K_m:
            player.missile()

        # We don't want the laser to fire without catching the icon first
        if player.laser_fire:
            if event.key == pygame.K_l:
                player.laser()
        else:
            player.laser_fire = False

    # We are not playing yet. Go to start title page
    if gameover:
        pages.Title(screen)
        gameover = False
        player = character.Player(path)
        shield = Shield(path)

        # Create some starting mobs
        for i in range(randrange(min_mob, max_mob)):
            mob = character.Mob(path)

        # Used for displaying the three life ships
        col = 0
        for i in range(3):
            ship = display.Hud(path, 1120+col)
            col += 60
            ship.newship()
            
    # Is the game over? Do we need to go to the form
    if doform:
        pages.Form(screen, player)
        doform = False
        gameover = True

    # Statusbars - Displays the info across the top including the life ships
    display.StatusBar(screen, 100, 15, player.life, text='Life')
    display.StatusBar(screen, 100, 45, shield.strength, text='Shield')
    ship.hud_sprites.update()
    ship.hud_sprites.draw(screen)
    display.Score(screen, player.score)
    display.Tips(screen, 880, 680)



    # If player looses a life, remove ship from top
    for item in ship.hud_sprites:
        if len(ship.hud_sprites) > player.lives:
            ship.hud_sprites.remove(item)

    # Draw and update all the sprites
    mysprites.allsprites.update()
    mysprites.allsprites.draw(screen)

    # Having to keep these seperate from allsprites to detect collisions
    mob_sprite.update()
    mob_sprite.draw(screen)
    display.show_sprite.update()
    display.show_sprite.draw(screen)
    
    # Set weapon to True will get destroyed
    weapon = True

    # Loop through weapon_sprite if weapon type is laser, 
    # set weapon to True. laser will not be destroyed
    for weapons in weapon_sprite:
        weapon = True if weapons.type == 'missile' else False

    # Check for weapon hitting mob
    weaponhits = pygame.sprite.groupcollide(weapon_sprite, mob_sprite, weapon, True)
    for weaponhit in weaponhits:
        character.Mob(path)
        player.score += int(mob.radius * random())
        Explosion(path, weaponhit.rect.center)
        if random() > 0.8:
            PowerUp(path, weaponhit.rect.center)

    #Detect powerup hit
    powerhits = pygame.sprite.groupcollide(power_sprite, player_sprite, True, False)
    
    # Loop through the powerup and apply accordingly 
    for powerhit in powerhits:

        # Caught a shield icon
        if powerhit.type == 'shield':
            for shield in shield_sprite:
                shield.kill()
            shield = Shield(path)
            shield.strength = 100
            shield.raise_shield(player.rect.centerx, player.rect.top)
            show_shield = display.Show(path, 'shield.png', 1195, 690)
            display.show_sprite.add(show_shield)

        # Caught a partial heal icon
        if powerhit.type == 'heal':
            sound = pygame.mixer.Sound(f'{path}/media/sounds/partial_heal.wav')
            sound.set_volume(0.9)
            sound.play()
            if player.life == 100:
                player.score += int(player.life * random())
            else:
                player.life += int(player.life * random())
                if player.life > 100:
                    player.life = 100

        # Caught a laser icon
        if powerhit.type == 'laser':
            player.laser_timer = pygame.time.get_ticks()
            player.laser_fire = True
            laser = display.Show(path, 'laser2.png', 1165, 690)
            display.show_sprite.add(laser)

        # Caught a full heal icon
        if powerhit.type == 'fullheal':
            sound = pygame.mixer.Sound(f'{path}/media/sounds/full_heal.wav')
            sound.set_volume(0.9)
            sound.play()
            if player.life == 100:
                player.score += int(randrange(10, 50) * random())
            else:
                player.life = 100

        # Timer for laser. Laser ends after 10 seconds unless another
        # laser powerup is caught
        if player.laser_timer:
            if pygame.time.get_ticks() - player.laser_timer > 10000:
                player.laser_fire = False
                player.laser_timer = None

                # Laser end remove from icon from game screen
                for item in display.show_sprite:
                    if item.type == 'laser2':
                        item.kill()      

    # Checking for mobs hitting the shield
    shieldhits = pygame.sprite.groupcollide(mob_sprite, shield_sprite, True, False)
    for shieldhit in shieldhits:
        mob = character.Mob(path)
        Explosion(path, shieldhit.rect.center)

        # Shield was hit, remove some of the strength
        shield.strength -= int(shield.radius *0.3)

        # Shield strength reached 0, remove it
        if shield.strength <= 1:
            shield.strength = 0
            for item in shield_sprite:
                item.kill()
            shield.hidden = True

            # Lost shield remove icon from the game screen
            for item in display.show_sprite:
                if item.type == 'shield':
                    item.kill()
        

    # Player takes hits from mobs
    ship_destroy = False
    if player.life <= 0:
        ship_destroy = True
        player.lives -= 1
        player.life = 100
        player.hide()
        player.laser_fire = False
        player.laser_timer = None
        display.show_sprite.empty()

    # If player looses all lifes, reset everything and go to form
    if player.lives <= 0 and not player.explosion.alive():
        mysprites.allsprites.empty()
        display.show_sprite.empty()
        mob_sprite.empty()
        shield_sprite.empty()
        ship.hud_sprites.empty()
        doform = True

    # Detect if any mobs has hit the player. We need to create a newmob for the one killed
    mobhits = pygame.sprite.groupcollide(mob_sprite, player_sprite, True, False)
    for mobhit in mobhits:
        Explosion(path, mobhit.rect.center)
        player.life -= int(mobhit.radius)
        character.Mob(path)

    # Update the display
    pygame.display.update()
    clock.tick(60)

pygame.quit()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#8
Why are you using pygame.event.poll ? That does 1 event per frame. This will give you unwanted side effects.
running = True
while running:
    # Event Loop
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # Get Key States
    keys = pygame.key.get_pressed()
    if keys[pygame.K_m]:
        # Fire Missle. Would need a timer for a refresh rate.
        
99 percent of computer problems exists between chair and keyboard.
Reply
#9
Perhaps but, it satisfies the need for the game. I've not found any side effects yet.
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#10
You probably don't know what side effects it will cause.
If you had more events trigger. You would notice lag on input.
99 percent of computer problems exists between chair and keyboard.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyGame] Sprites just randomly appear and dissapear in my pygame.sprite.GoupeSingle trueShadoWrr 2 2,005 Feb-13-2023, 09:34 AM
Last Post: Vadanane
  [PyGame] I found a way to generate sprites without .blit. Is it effecient? xBlackHeartx 19 8,521 Dec-07-2019, 01:06 PM
Last Post: metulburr
  [pygame] transparent rects SheeppOSU 2 2,305 Jun-10-2019, 03:41 PM
Last Post: SheeppOSU
  [PyGame] Having 4 players(Sprites) all being able to jump ElijahCastle 5 4,032 May-07-2019, 05:04 PM
Last Post: SheeppOSU
  Sprites and Actor error ajlconsulting 6 9,395 Jan-30-2019, 12:50 AM
Last Post: metulburr
  draw not showing all sprites ethanstrominger 0 2,603 Jan-25-2019, 10:10 PM
Last Post: ethanstrominger
  [PyGame] move randomly sprites reutB 4 8,262 Mar-29-2017, 01:12 PM
Last Post: metulburr
  How can I get rid of the line following the sprites? Houston11 1 3,762 Jan-06-2017, 10:14 PM
Last Post: Mekire

Forum Jump:

User Panel Messages

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