Trouble using the same image for multiple objects - python

I'm making a game with Pygame and I'm trying to animate asteroids that get hit with a bullet. The animation works fine if there's just one asteroid type on the screen. I run into trouble when there's two identical asteroids on the screen. It seems that the first one on is the only one that uses my animation.
For example, say the game spawns two small asteroids, these are the exact same and I'm using glob.glob to get the 4 animation images, then I'll just use those images to create a little animation by looping over them. This works with one of the asteroids, the other will not play the animation. I'm thinking it must be because only one object can use specific images at any one time?
I'm sorry for the terrible explaination.
Here's the asteroid class I have and the call to create the animation:
class Asteroid(pygame.sprite.Sprite):
def __init__(self, screen, size):
pygame.sprite.Sprite.__init__(self)
self.screen = screen
self.size = size
self.pos_x = random.randint(100,900)
pos_y= random.randint(100, 700)
self.pos_y = -pos_y
self.vel_y = 3
self.update_main = True
self.animation_path = ""
#load the image then put it above the screen, ready to drop
if self.size == "small":
self.asteroid_image = pygame.image.load("asteroid_small.tga")
self.animation_path = "C:/Programming/Stupid Games/Alien Invasion/animations/asteroid_small/*.tga"
elif self.size == "medium":
self.asteroid_image = pygame.image.load("asteroid_medium.tga")
self.animation_path = "C:/Programming/Stupid Games/Alien Invasion/animations/asteroid_medium/*.tga"
elif self.size == "med_large":
self.asteroid_image = pygame.image.load("asteroid_med_large.tga")
self.animation_path = "C:/Programming/Stupid Games/Alien Invasion/animations/asteroid_med_large/*.tga"
elif self.size == "small_med":
self.asteroid_image = pygame.image.load("asteroid_small_med.png")
elif self.size == "small_1":
self.asteroid_image = pygame.image.load("small1.tga")
self.animation_path = "C:/Programming/Stupid Games/Alien Invasion/animations/small/*.tga"
else:
self.asteroid_image = pygame.image.load("asteroid_large.tga")
self.animation_path = "C:/Programming/Stupid Games/Alien Invasion/animations/asteroid_large/*.tga"
#make the asteroid transparent
corner = self.asteroid_image.get_at((0,0))
self.asteroid_image.set_colorkey(corner, RLEACCEL)
#randomly rotate the image
self.new_asteroid_image = pygame.transform.rotate(self.asteroid_image, random.randint(0,340))
self.rect = self.new_asteroid_image.get_rect()
self.rect.x, self.rect.y = ((self.pos_x, self.pos_y))
self.start_animate = False
self.times = 0
#load the asteroid animation
self.animation = glob.glob(self.animation_path)
self.animation.sort()
self.animation_pos = 0
self.animation_max = len(self.animation)-1
self.animation_speed_init = 50
self.animation_speed = self.animation_speed_init
def update(self):
#if asteroid has left the screen, place it back to the top
if self.rect.y > 900:
pos_y = random.randint(100,500)
self.rect.y = -pos_y
self.rect.x = random.randint(100,900)
self.rect.y += self.vel_y
self.position = self.rect.x, self.rect.y
self.screen.blit(self.new_asteroid_image.convert(), self.position)
if self.start_animate == True:
self.animation_speed += 25
if self.animation_speed > 60:
self.update_main = False
self.new_asteroid = pygame.image.load(self.animation[self.times])
corner = self.new_asteroid.get_at((0,0))
self.new_asteroid.set_colorkey(corner, RLEACCEL)
self.new_asteroid_image = self.new_asteroid
self.animation_speed = 0
self.animation_pos += 1
self.times += 1
if self.times > self.animation_max:
self.kill()
def start_animation(self):
self.start_animate = True
#create asteroids at the start of the game
if keys[K_SPACE]:
game_over = False
player = Space_Ship("space_ship.tga", screen)
asteroid_small = Asteroid(screen, "small")
asteroid_large = Asteroid(screen, "large")
asteroid_medium = Asteroid(screen, "medium")
asteroid_med_large = Asteroid(screen, "med_large")
asteroid_group.add(asteroid_small, asteroid_large, asteroid_medium, asteroid_med_large)
player_group.add(player)
score = 0
#add asteroids after the game has started
if len(asteroid_group) < difficulty:
random_asteroid_number = random.randint(0,5)
if random_asteroid_number == 0:
#large asteroid
asteroid_large = Asteroid(screen, "large")
asteroid_group.add(asteroid_large)
elif random_asteroid_number == 1:
#medium asteroid
asteroid_medium = Asteroid(screen, "medium")
asteroid_group.add(asteroid_medium)
elif random_asteroid_number == 2:
#medium large asteroid
asteroid_med_large = Asteroid(screen, "med_large")
asteroid_group.add(asteroid_med_large)
elif random_asteroid_number == 3:
#small medium asteroid
asteroid_small_med = Asteroid(screen, "small_med")
asteroid_group.add(asteroid_small_med)
elif random_asteroid_number == 4:
#small_1 asteroid
asteroid_small_1 = Asteroid(screen, "small_1")
asteroid_group.add(asteroid_small_1)
else:
#small asteroid
asteroid_small = Asteroid(screen, "small")
asteroid_group.add(asteroid_small)
#bullet - asteroid collision
collisions = pygame.sprite.groupcollide(bullet_group, asteroid_group, True, False)
if collisions != None:
for i in collisions.values():
for sprite in i:
channel2.play(asteroid_audio)
if sprite == asteroid_large:
score += 3
sprite.start_animation()
elif sprite == asteroid_small_1 or sprite == asteroid_med_large or sprite == asteroid_medium or sprite == asteroid_small:
score += 1
sprite.start_animation()
else:
score += 1
sprite.kill()

Although I can't be sure because the bit where you call update is not shown, I have a few ideas of why it is doing what it is. It seems that the second asteroid's update is not being called. This could be for several reasons. The first reason is if you do not implement groups to update you asteroid. Basically, if you only call your single instance of the asteroid, the second one will not do anything, even show up on the screen. If this is the case, you will need to make a group, perhaps a render updates group but even a list would be fine. This would mean you could do something like:
asteroids.update()
or
for asteroid in asteroids:
asteroid.update()
the above two codes assume that you have some kind of grouping of asteroids, called asteroids.
Another possible problem, if you are implementing groups, is if the second asteroid is not getting added to this group. If you tell it to add itself to the group in the init function, this problem will not be there, however, if you call it outside of this function, you may not be doing so when you make your second asteroid. To address this problem just move the part where you add the asteroid into a group that will be updated to the init function of asteroids.

Related

Pygame animation list index continuously goes out of range

I followed a clear code tutorial to make a platformer and ended up finishing it, however one thing always continously messed up. That being the animation, at times the game just would not run and would only run in debug mode due to the animation list being out of index which makes no sense to me since every item in the list is accounted for. Below is my player.py which has all of the functions and such that the video mentions. If more information is needed I will gladly provide it. (the animation part is specifically erroring at self.image = self.animations['idle'][self.frame_index])
import pygame
from support import import_folder
class Player(pygame.sprite.Sprite):
def __init__(self,pos,surface,create_jump_particles):
super().__init__()
self.import_character_assets()
self.frame_index = 0
self.animation_speed = 0.15
self.image = self.animations['idle'][self.frame_index]
self.rect = self.image.get_rect(topleft = pos)
# dust particles
self.import_dust_run_particles()
self.dust_frame_index = 0
self.dust_animation_speed = 0.15
self.display_surface = surface
self.create_jump_particles = create_jump_particles
# player movement
self.direction = pygame.math.Vector2(0,0)
self.speed = 8
self.gravity = 0.8
self.jump_speed = -16
# player status
self.status = 'idle'
self.facing_right = True
self.on_ground = False
self.on_ceiling = False
self.on_left = False
self.on_right = False
def import_character_assets(self):
character_path = '../graphics/character/'
self.animations = {'idle':[],'run':[],'jump':[],'fall':[]}
for animation in self.animations.keys():
full_path = character_path + animation
self.animations[animation] = import_folder(full_path)
def import_dust_run_particles(self):
self.dust_run_particles = import_folder('../graphics/character/dust_particles/run')
def animate(self):
animation = self.animations[self.status]
# loop over frame index
self.frame_index += self.animation_speed
if self.frame_index >= len(animation):
self.frame_index = 0
image = animation[int(self.frame_index)]
if self.facing_right:
self.image = image
else:
flipped_image = pygame.transform.flip(image,True,False)
self.image = flipped_image
# set the rect
if self.on_ground and self.on_right:
self.rect = self.image.get_rect(bottomright = self.rect.bottomright)
elif self.on_ground and self.on_left:
self.rect = self.image.get_rect(bottomleft = self.rect.bottomleft)
elif self.on_ground:
self.rect = self.image.get_rect(midbottom = self.rect.midbottom)
elif self.on_ceiling and self.on_right:
self.rect = self.image.get_rect(topright = self.rect.topright)
elif self.on_ceiling and self.on_left:
self.rect = self.image.get_rect(topleft = self.rect.topleft)
elif self.on_ceiling:
self.rect = self.image.get_rect(midtop = self.rect.midtop)
def run_dust_animation(self):
if self.status == 'run' and self.on_ground:
self.dust_frame_index += self.dust_animation_speed
if self.dust_frame_index >= len(self.dust_run_particles):
self.dust_frame_index = 0
dust_particle = self.dust_run_particles[int(self.dust_frame_index)]
if self.facing_right:
pos = self.rect.bottomleft - pygame.math.Vector2(6,10)
self.display_surface.blit(dust_particle,pos)
else:
pos = self.rect.bottomright - pygame.math.Vector2(6,10)
flipped_dust_particle = pygame.transform.flip(dust_particle,True,False)
self.display_surface.blit(flipped_dust_particle,pos)
def get_input(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
self.direction.x = 1
self.facing_right = True
elif keys[pygame.K_LEFT]:
self.direction.x = -1
self.facing_right = False
else:
self.direction.x = 0
if keys[pygame.K_SPACE] and self.on_ground:
self.jump()
self.create_jump_particles(self.rect.midbottom)
def get_status(self):
if self.direction.y < 0:
self.status = 'jump'
elif self.direction.y > 1:
self.status = 'fall'
else:
if self.direction.x != 0:
self.status = 'run'
else:
self.status = 'idle'
def apply_gravity(self):
self.direction.y += self.gravity
self.rect.y += self.direction.y
def jump(self):
self.direction.y = self.jump_speed
def update(self):
self.get_input()
self.get_status()
self.animate()
self.run_dust_animation()
I've tried everything, I've even gone to his website and downloaded his prototype and tried adding my own files to it and it still returns the same error. Whats supposed to be happening is that my character does an animation depending on which state they are in.
So it's likely that the code is not loading any of the animation frames for the 'idle' character animation.
The initialisation code first loads all the assets:
class Player(pygame.sprite.Sprite):
def __init__(self,pos,surface,create_jump_particles):
super().__init__()
self.import_character_assets()
Where self.import_character_assets() is:
def import_character_assets(self):
character_path = '../graphics/character/'
self.animations = {'idle':[],'run':[],'jump':[],'fall':[]}
for animation in self.animations.keys():
full_path = character_path + animation
self.animations[animation] = import_folder(full_path)
Which is iterating through 'idle', 'run', 'jump', 'fall', loading images from '../graphics/character/idle', '../graphics/character/run', etc. into a dictionary-of-lists.
We don't have the code for import_folder(), but it's obviously intended to load some set of images.
After importing, the self.animations dictionary is expected to have at least a single image in self.animations['idle']. This is because the __init__() is defaulting to 'idle', and index 0:
self.frame_index = 0
self.animation_speed = 0.15
self.image = self.animations['idle'][self.frame_index] # <<-- HERE, 0
So we can conclude that because we know, self.animations['idle'] is out of bounds, even on [0], no images were loaded.
Check that there are images available for this. Or maybe the filenames are not what's expected (e.g.: .jpg, not .png), or some other file-system error (e.g.: read only, zero byte files, ... ). Maybe the load-path is incorrect?
It might be a good idea to check the load, and write an error:
def import_character_assets(self):
character_path = '../graphics/character/'
self.animations = {'idle':[],'run':[],'jump':[],'fall':[]}
for animation in self.animations.keys():
full_path = character_path + animation
self.animations[animation] = import_folder(full_path)
# check we loaded something
if ( len( self.animations[animation] ) == 0 ):
print( "Failed to load any animations for \"" + animation + "\" type" )

I am trying to make enemies spawn over time in my python arcade game, but i get a simple error that makes no sense

im working on a game in python arcade, where the player runs around, shoots zombies, etc. very basic game. just recently, i started implementing the spawn over time part, but i get an error that just makes no sense:
AttributeError: 'MyGame' object has no attribute 'total_time'
it makes no sense because i have stated self.total_time in MyGame.
how do i fix this?
import arcade
import random
import math
import arcade.gui
import time
import timeit
SPRITE_SCALING = 0.35
SPRITE_SCALING_LASER = 0.8
SCREEN_WIDTH = 1280
SCREEN_HEIGHT = 720
SCREEN_TITLE = "zombier shooter"
ENEMY_COUNT = 20
BULLET_SPEED = 30
MOVEMENT_SPEED = 5
SPRITE_SPEED = 1
INDICATOR_BAR_OFFSET = 32
ENEMY_ATTACK_COOLDOWN = 1
PLAYER_HEALTH = 5
SCENE_MENU = 'SCENE_MENU'
SCENE_GAME = 'SCENE_GAME'
class QuitButton(arcade.gui.UIFlatButton):
def on_click(self, event: arcade.gui.UIOnClickEvent):
arcade.exit()
class Player(arcade.Sprite):
def update(self):
""" moves the player """
# move player.
self.center_x += self.change_x
self.center_y += self.change_y
# check for out of bounds
if self.left < 0:
self.left = 0
elif self.right > SCREEN_WIDTH - 1:
self.right = SCREEN_WIDTH - 1
if self.bottom < 0:
self.bottom = 0
elif self.top > SCREEN_HEIGHT - 1:
self.top = SCREEN_HEIGHT - 1
class Enemy(arcade.Sprite):
"""
This class represents the enemies on our screen.
"""
def follow_sprite(self, player_sprite):
"""
This function will move the current sprite towards whatever
other sprite is specified as a parameter.
"""
if self.center_y < player_sprite.center_y:
self.center_y += min(SPRITE_SPEED, player_sprite.center_y - self.center_y)
elif self.center_y > player_sprite.center_y:
self.center_y -= min(SPRITE_SPEED, self.center_y - player_sprite.center_y)
if self.center_x < player_sprite.center_x:
self.center_x += min(SPRITE_SPEED, player_sprite.center_x - self.center_x)
elif self.center_x > player_sprite.center_x:
self.center_x -= min(SPRITE_SPEED, self.center_x - player_sprite.center_x)
class MyGame(arcade.Window):
"""
main game class
"""
def __init__(self, width, height, title):
"""
initialises stuff
"""
# call the parent class initializer
super().__init__(width, height, title)
self.scene = SCENE_MENU
# variables that will hold sprite lists
self.player_list = None
# set up the player info
self.player_sprite = None
# track the current state of what key is pressed
self.left_pressed = False
self.right_pressed = False
self.up_pressed = False
self.down_pressed = False
# --- Required for all code that uses UI element,
# a UIManager to handle the UI.
self.manager = arcade.gui.UIManager()
self.manager.enable()
# Set background color
arcade.set_background_color(arcade.color.DARK_BLUE_GRAY)
# Create a vertical BoxGroup to align buttons
self.v_box = arcade.gui.UIBoxLayout()
# Create the buttons
start_button = arcade.gui.UIFlatButton(text="Start Game", width=200)
self.v_box.add(start_button.with_space_around(bottom=20))
settings_button = arcade.gui.UIFlatButton(text="Settings", width=200)
self.v_box.add(settings_button.with_space_around(bottom=20))
# Again, method 1. Use a child class to handle events.
quit_button = QuitButton(text="Quit", width=200)
self.v_box.add(quit_button)
# --- Method 2 for handling click events,
# assign self.on_click_start as callback
start_button.on_click = self.on_click_start
# --- Method 3 for handling click events,
# use a decorator to handle on_click events
#settings_button.event("on_click")
def on_click_settings(event):
print("Settings:", event)
# Create a widget to hold the v_box widget, that will center the buttons
self.manager.add(
arcade.gui.UIAnchorWidget(
anchor_x="center_x",
anchor_y="center_y",
child=self.v_box)
)
def setup(self):
""" Set up the game and initialize the variables. """
# sprite lists
self.player_list = arcade.SpriteList()
self.enemy_list = arcade.SpriteList()
self.bullet_list = arcade.SpriteList()
#setup timer
self.total_time = 0.0
# setup score
self.score = 0
self.score_text = None
# setup health info
self.health = 5
self.health_text = None
self.dead = None
# set up the player
self.player_sprite = Player(":resources:images/animated_characters/female_person/femalePerson_idle.png",
SPRITE_SCALING)
self.player_sprite.center_x = 50
self.player_sprite.center_y = 50
self.player_list.append(self.player_sprite)
def on_draw(self):
""" render the screen. """
# clear the screen
self.clear()
if self.scene == SCENE_MENU:
self.manager.draw()
elif self.scene == SCENE_GAME:
# draw all the sprites.
self.player_list.draw()
self.enemy_list.draw()
self.bullet_list.draw()
# put score text on the screen
output = f"Score: {self.score}"
arcade.draw_text(output, 10, 20, arcade.color.WHITE, 14)
# put helth text on the screen
output = f"Health: {self.health}"
arcade.draw_text(output, 10, 40, arcade.color.WHITE, 14)
if self.health <= 0:
self.player_sprite.remove_from_sprite_lists()
# put u died text on the screen
output = f"YOU DIED"
arcade.draw_text(output, 500, 400, arcade.color.RED, 50)
output = f"Click to Exit"
arcade.draw_text(output, 550, 300, arcade.color.BLACK, 30)
def on_click_start(self, event):
self.setup()
self.scene = SCENE_GAME
self.manager.disable()
print("Start:", event)
def on_mouse_press(self, x, y, button, modifiers):
""" Called whenever the mouse button is clicked. """
if self.health <= 0:
exit()
# create a bullet
bullet = arcade.Sprite(":resources:images/space_shooter/laserBlue01.png", SPRITE_SCALING_LASER)
# Position the bullet at the player's current location
start_x = self.player_sprite.center_x
start_y = self.player_sprite.center_y
bullet.center_x = start_x
bullet.center_y = start_y
# Get from the mouse the destination location for the bullet
# IMPORTANT! If you have a scrolling screen, you will also need
# to add in self.view_bottom and self.view_left.
dest_x = x
dest_y = y
# Do math to calculate how to get the bullet to the destination.
# Calculation the angle in radians between the start points
# and end points. This is the angle the bullet will travel.
x_diff = dest_x - start_x
y_diff = dest_y - start_y
angle = math.atan2(y_diff, x_diff)
# Angle the bullet sprite so it doesn't look like it is flying
# sideways.
bullet.angle = math.degrees(angle)
print(f"Bullet angle: {bullet.angle:.2f}")
# Taking into account the angle, calculate our change_x
# and change_y. Velocity is how fast the bullet travels.
bullet.change_x = math.cos(angle) * BULLET_SPEED
bullet.change_y = math.sin(angle) * BULLET_SPEED
# Add the bullet to the appropriate lists
self.bullet_list.append(bullet)
def update_player_speed(self):
# calculate speed based on the keys pressed
self.player_sprite.change_x = 0
self.player_sprite.change_y = 0
if self.up_pressed and not self.down_pressed:
self.player_sprite.change_y = MOVEMENT_SPEED
elif self.down_pressed and not self.up_pressed:
self.player_sprite.change_y = -MOVEMENT_SPEED
if self.left_pressed and not self.right_pressed:
self.player_sprite.change_x = -MOVEMENT_SPEED
elif self.right_pressed and not self.left_pressed:
self.player_sprite.change_x = MOVEMENT_SPEED
def on_update(self, delta_time):
""" updates values n stuff """
if self.scene == SCENE_GAME:
# call update to move the sprite
self.player_list.update()
# Call update on all sprites
self.bullet_list.update()
# go through each bullet
for bullet in self.bullet_list:
# check each bullet to see if it hit a zombie
hit_list = arcade.check_for_collision_with_list(bullet, self.enemy_list)
# if it did, remove the bullet
if len(hit_list) > 0:
bullet.remove_from_sprite_lists()
# for each enemy we hit with a bullet, remove enemy and add to the score
for enemy in hit_list:
enemy.remove_from_sprite_lists()
self.score += 1
# if bullet goes off screen, then remove it
if bullet.bottom > self.width or bullet.top < 0 or bullet.right < 0 or bullet.left > self.width:
bullet.remove_from_sprite_lists()
for enemy in self.enemy_list:
Enemy.follow_sprite(enemy, self.player_sprite)
# create a list of all sprites that had a collision with the player.
hit_list = arcade.check_for_collision_with_list(self.player_sprite, self.enemy_list)
# go through each sprite, if it got hit, then remove the sprite and lower score and health
for enemy in hit_list:
enemy.remove_from_sprite_lists()
self.score -= 1
self.health -= 1
# Accumulate the total time
self.total_time += delta_time
# Calculate minutes
minutes = int(self.total_time) // 60
# Calculate seconds by using a modulus (remainder)
seconds = int(self.total_time) % 60
# Calculate 100s of a second
seconds_100s = int((self.total_time - seconds) * 100)
if self.total_time > 5:
for i in range(5):
# enemy texture
enemy = arcade.Sprite(":resources:images/animated_characters/zombie/zombie_idle.png", SPRITE_SCALING)
enemy.center_x = random.randrange(SCREEN_WIDTH)
enemy.center_y = random.randrange(SCREEN_HEIGHT)
self.enemy_list.append(enemy)
self.total_time = 0.0
def on_key_press(self, key, modifiers):
"""called when user presses a key. """
if key == arcade.key.UP:
self.up_pressed = True
self.update_player_speed()
elif key == arcade.key.DOWN:
self.down_pressed = True
self.update_player_speed()
elif key == arcade.key.LEFT:
self.left_pressed = True
self.update_player_speed()
elif key == arcade.key.RIGHT:
self.right_pressed = True
self.update_player_speed()
def on_key_release(self, key, modifiers):
"""called when user releases a key. """
if key == arcade.key.UP:
self.up_pressed = False
self.update_player_speed()
elif key == arcade.key.DOWN:
self.down_pressed = False
self.update_player_speed()
elif key == arcade.key.LEFT:
self.left_pressed = False
self.update_player_speed()
elif key == arcade.key.RIGHT:
self.right_pressed = False
self.update_player_speed()
def main():
""" Main function """
MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
arcade.run()
if __name__ == "__main__":
main()
You defined self.total_time in setup() and not in __init__(), which should be a red-flag, because it could happen that self.total_time is actually accessed before setup() is called. And exactly this happened.
The error appears in on_update() when you try to run self.total_time += delta_time - in order for this line to work, self.total_time has to be initialised before - but setup() wasn't executed before, so self.total_time 'does not exist' yet.
So you can fix your code by moving the line self.total_time = 0.0 from setup() to __init__() - this way you make sure that you create this variable once the class gets initialised and before anything else gets executed.
If you plan on using variables in a class across different methods it's better to define them in __init__() to avoid such problems.

Animation doubles duration every time it appears in pygame

I'm fairly new to coding and I am working on a little game with pygame. I have a Sprite for an explosion animation, the first times it appears it works great for the whole game, but once you are playing a second or third game the explosions lasts more every time even though I empty the sprite group every time the game restarts, I think the problem might be that the explosion update function doesn't works well for more than one game, but I don't really know how to fix it. Thanks in advance for the help!
class Explosion(pygame.sprite.Sprite):
expl_img = []
def __init__(self, center):
pygame.sprite.Sprite.__init__(self)
self.image = self.cargaImagenes()
self.rect = self.image.get_rect()
self.rect.center = center
self.frame = 0
self.last_update = pygame.time.get_ticks()
self.frame_rate = 50
def update(self):
now = pygame.time.get_ticks()
if now -self.last_update > self.frame_rate:
self.last_update = now
self.frame +=1
if self.frame == len(self.expl_img):
self.kill()
else:
center = self.rect.center
self.image = self.expl_img[self.frame]
self.rect = self.image.get_rect()
self.rect.center = center
def cargaImagenes(self):
for i in range(9):
filename = 'regularExplosion0{}.png'.format(i)
img = pygame.image.load(path.join(img_dir, filename)).convert()
img.set_colorkey(BLACK)
img_def = pygame.transform.scale(img, (75, 75))
self.expl_img.append(img_def)
return self.expl_img[0]
Here is where the explosion appears:
if self.player.estado == self.player.Estado.volando:
anticolision = False
colisiones = pygame.sprite.spritecollide(self.player, self.asteroides, True, pygame.sprite.collide_circle)
for colision in colisiones:
start_ticks = pygame.time.get_ticks()
if colision and not anticolision:
anticolision = True
print("anticolision true")
expl = Explosion(colision.rect.center)
self.explosionSound.play()
self.all_sprites.add(expl)
self.vidas -= 1
segundos = (pygame.time.get_ticks()-start_ticks)/1000
if segundos > 3:
anticolision = False
print("anticolision false")
elif colision and anticolision:
pass
if self.vidas == 0:
self.gameOver()
running= False
self.cargaImagenes() is called every time when you create a new instance of Explosion. Therefore the number of elements in the class variable expl_img grows continuously. Only add new items to the list when the list is empty:
class Explosion(pygame.sprite.Sprite):
expl_img = []
# [...]
def cargaImagenes(self):
if not expl_img: # <---
for i in range(9):
filename = 'regularExplosion0{}.png'.format(i)
img = pygame.image.load(path.join(img_dir, filename)).convert()
img.set_colorkey(BLACK)
img_def = pygame.transform.scale(img, (75, 75))
self.expl_img.append(img_def)
return self.expl_img[0]

atan2 isn't providing me with the angle I want

I'm trying to write a game in pygame, involving a moving object with a "turret" that swivels to follow a mouse. As of now, I'm mostly trying to expand upon examples, so the code's not entirely mine (credit to Sean J. McKiernan for his sample programs); however, this portion is. Below is my code; I use the center of rect (the "base" shape and the point around which the "turret" swivels) as the base point, and the position of the mouse as the other point. By subtracting the mouse's displacement from the displacement of the "center," I effectively get a vector between the two points and find the angle between that vector and the x-axis with atan2. Below is the code I use to do that:
def get_angle(self, mouse):
x_off = (mouse[0]-self.rect.centerx)
y_off = (mouse[1]-self.rect.centery)
self.angle = math.degrees(math.atan2(-y_off, x_off) % 2*math.pi)
self.hand = pg.transform.rotate(self.original_hand, self.angle)
self.hand_rect = self.hand.get_rect(center=self.hand_rect.center)
According to multiple tutorials I've reviewed, this SHOULD be the correct code; however, I discovered later that those tutorials (and, in fact, this tutorial) were all for Python 2.7, while I am trying to write in Python 3.6. I don't think that should make a difference in this scenario, though. As it stands, the view appears to depend entirely upon the "character's" position on the screen. If the "character" is in one corner, the reaction of the "turret" is different than the reaction if the "character" is in the middle of the screen. However, this shouldn't matter; the position of the "character" relative to the mouse is the exact same no matter where on the screen they are. Any ideas, or do I need to supply more code?
Edit: Apparently, more code is required. Rather than attempt to extricate only the entirely necessary parts, I've provided the entire code sample, so everyone can run it. As a side note, the "Bolt" things (intended to fire simple yellow blocks) don't work either, but I'm just trying to get the arm working before I start in on debugging that.
Edit the second: I have discovered that the "Bolt" system works within a certain distance of the origin (0,0 in the window coordinate system), and that the arm also works within a much lesser distance. I added the line Block(pg.Color("chocolate"), (0,0,100,100)) under the "walls" grouping as a decision point, and the block was positioned in the top left corner. I've corrected Bolt by changing screen_rect to viewport in the control loop; however, I don't know why the "arm" swinging is dependent on adjacency to the origin. The positions of the mouse and "character" SHOULD be absolute. Am I missing something?
"""
Basic moving platforms using only rectangle collision.
-Written by Sean J. McKiernan 'Mekire'
Edited for a test of "arms"
"""
import os
import sys
import math
import pygame as pg
CAPTION = "Moving Platforms"
SCREEN_SIZE = (700,700)
BACKGROUND_COLOR = (50, 50, 50)
COLOR_KEY = (255, 255, 255)
class _Physics(object):
"""A simplified physics class. Psuedo-gravity is often good enough."""
def __init__(self):
"""You can experiment with different gravity here."""
self.x_vel = self.y_vel = 0
self.grav = 0.4
self.fall = False
def physics_update(self):
"""If the player is falling, add gravity to the current y velocity."""
if self.fall:
self.y_vel += self.grav
else:
self.y_vel = 0
class Player(_Physics, object):
def __init__(self,location,speed):
_Physics.__init__(self)
HAND = pg.image.load("playertst2.png").convert()
HAND.set_colorkey(COLOR_KEY)
self.image = pg.image.load('playertst.png').convert()
self.rect = self.image.get_rect(topleft=location)
self.speed = speed
self.jump_power = -9.0
self.jump_cut_magnitude = -3.0
self.on_moving = False
self.collide_below = False
self.original_hand = HAND
self.fake_hand = self.original_hand.copy()
self.hand = self.original_hand.copy()
self.hand_rect = self.hand.get_rect(topleft=location)
self.angle = self.get_angle(pg.mouse.get_pos())
def check_keys(self, keys):
"""Find the player's self.x_vel based on currently held keys."""
self.x_vel = 0
if keys[pg.K_LEFT] or keys[pg.K_a]:
self.x_vel -= self.speed
if keys[pg.K_RIGHT] or keys[pg.K_d]:
self.x_vel += self.speed
def get_position(self, obstacles):
"""Calculate the player's position this frame, including collisions."""
if not self.fall:
self.check_falling(obstacles)
else:
self.fall = self.check_collisions((0,self.y_vel), 1, obstacles)
if self.x_vel:
self.check_collisions((self.x_vel,0), 0, obstacles)
def check_falling(self, obstacles):
"""If player is not contacting the ground, enter fall state."""
if not self.collide_below:
self.fall = True
self.on_moving = False
def check_moving(self,obstacles):
"""
Check if the player is standing on a moving platform.
If the player is in contact with multiple platforms, the prevously
detected platform will take presidence.
"""
if not self.fall:
now_moving = self.on_moving
any_moving, any_non_moving = [], []
for collide in self.collide_below:
if collide.type == "moving":
self.on_moving = collide
any_moving.append(collide)
else:
any_non_moving.append(collide)
if not any_moving:
self.on_moving = False
elif any_non_moving or now_moving in any_moving:
self.on_moving = now_moving
def check_collisions(self, offset, index, obstacles):
"""
This function checks if a collision would occur after moving offset
pixels. If a collision is detected, the position is decremented by one
pixel and retested. This continues until we find exactly how far we can
safely move, or we decide we can't move.
"""
unaltered = True
self.rect[index] += offset[index]
self.hand_rect[index] += offset[index]
while pg.sprite.spritecollideany(self, obstacles):
self.rect[index] += (1 if offset[index]<0 else -1)
self.hand_rect[index] += (1 if offset[index]<0 else -1)
unaltered = False
return unaltered
def check_above(self, obstacles):
"""When jumping, don't enter fall state if there is no room to jump."""
self.rect.move_ip(0, -1)
collide = pg.sprite.spritecollideany(self, obstacles)
self.rect.move_ip(0, 1)
return collide
def check_below(self, obstacles):
"""Check to see if the player is contacting the ground."""
self.rect.move_ip((0,1))
collide = pg.sprite.spritecollide(self, obstacles, False)
self.rect.move_ip((0,-1))
return collide
def jump(self, obstacles):
"""Called when the user presses the jump button."""
if not self.fall and not self.check_above(obstacles):
self.y_vel = self.jump_power
self.fall = True
self.on_moving = False
def jump_cut(self):
"""Called if player releases the jump key before maximum height."""
if self.fall:
if self.y_vel < self.jump_cut_magnitude:
self.y_vel = self.jump_cut_magnitude
def get_angle(self, mouse):
x_off = (mouse[0]-self.rect.centerx)
y_off = (mouse[1]-self.rect.centery)
self.angle = math.degrees(math.atan2(-y_off, x_off) % (2*math.pi))
self.hand = pg.transform.rotate(self.original_hand, self.angle)
self.hand_rect = self.hand.get_rect(center=self.hand_rect.center)
"""
offset = (mouse[1]-self.hand_rect.centery, mouse[0]-self.hand_rect.centerx)
self.angle = math.atan2(-offset[0], offset[1]) % (2 * math.pi)
self.angle = math.degrees(self.angle)
self.hand = pg.transform.rotate(self.original_hand, self.angle)
self.hand_rect = self.hand.get_rect(center=self.rect.center)
self.angle = 135-math.degrees(math.atan2(*offset))
self.hand = pg.transform.rotate(self.original_hand, self.angle)
self.hand_rect = self.hand.get_rect(topleft=self.rect.topleft)
"""
def pre_update(self, obstacles):
"""Ran before platforms are updated."""
self.collide_below = self.check_below(obstacles)
self.check_moving(obstacles)
def update(self, obstacles, keys):
"""Everything we need to stay updated; ran after platforms update."""
self.check_keys(keys)
self.get_position(obstacles)
self.physics_update()
def get_event(self, event, bolts):
if event.type == pg.MOUSEBUTTONDOWN and event.button == 1:
bolts.add(Bolt(self.rect.center))
elif event.type == pg.MOUSEMOTION:
self.get_angle(event.pos)
def draw(self, surface):
"""Blit the player to the target surface."""
surface.blit(self.image, self.rect)
surface.blit(self.hand, self.hand_rect)
class Bolt(pg.sprite.Sprite):
def __init__(self, location):
pg.sprite.Sprite.__init__(self)
"""self.original_bolt = pg.image.load('bolt.png')"""
"""self.angle = -math.radians(angle-135)"""
"""self.image = pg.transform.rotate(self.original_bolt, angle)"""
"""self.image = self.original_bolt"""
self.image=pg.Surface((5,10)).convert()
self.image.fill(pg.Color("yellow"))
self.rect = self.image.get_rect(center=location)
self.move = [self.rect.x, self.rect.y]
self.speed_magnitude = 5
"""self.speed = (self.speed_magnitude*math.cos(self.angle), self.speed_magnitude*math.sin(self.angle))"""
"""self.speed = (5,0)"""
self.done = False
def update(self, screen_rect, obstacles):
self.move[0] += self.speed_magnitude
"""self.move[1] += self.speed[1]"""
self.rect.topleft = self.move
self.remove(screen_rect, obstacles)
def remove(self, screen_rect, obstacles):
if not self.rect.colliderect(screen_rect):
self.kill()
class Block(pg.sprite.Sprite):
"""A class representing solid obstacles."""
def __init__(self, color, rect):
"""The color is an (r,g,b) tuple; rect is a rect-style argument."""
pg.sprite.Sprite.__init__(self)
self.rect = pg.Rect(rect)
self.image = pg.Surface(self.rect.size).convert()
self.image.fill(color)
self.type = "normal"
class MovingBlock(Block):
"""A class to represent horizontally and vertically moving blocks."""
def __init__(self, color, rect, end, axis, delay=500, speed=2, start=None):
"""
The moving block will travel in the direction of axis (0 or 1)
between rect.topleft and end. The delay argument is the amount of time
(in miliseconds) to pause when reaching an endpoint; speed is the
platforms speed in pixels/frame; if specified start is the place
within the blocks path to start (defaulting to rect.topleft).
"""
Block.__init__(self, color, rect)
self.start = self.rect[axis]
if start:
self.rect[axis] = start
self.axis = axis
self.end = end
self.timer = 0.0
self.delay = delay
self.speed = speed
self.waiting = False
self.type = "moving"
def update(self, player, obstacles):
"""Update position. This should be done before moving any actors."""
obstacles = obstacles.copy()
obstacles.remove(self)
now = pg.time.get_ticks()
if not self.waiting:
speed = self.speed
start_passed = self.start >= self.rect[self.axis]+speed
end_passed = self.end <= self.rect[self.axis]+speed
if start_passed or end_passed:
if start_passed:
speed = self.start-self.rect[self.axis]
else:
speed = self.end-self.rect[self.axis]
self.change_direction(now)
self.rect[self.axis] += speed
self.move_player(now, player, obstacles, speed)
elif now-self.timer > self.delay:
self.waiting = False
def move_player(self, now, player, obstacles, speed):
"""
Moves the player both when on top of, or bumped by the platform.
Collision checks are in place to prevent the block pushing the player
through a wall.
"""
if player.on_moving is self or pg.sprite.collide_rect(self,player):
axis = self.axis
offset = (speed, speed)
player.check_collisions(offset, axis, obstacles)
if pg.sprite.collide_rect(self, player):
if self.speed > 0:
self.rect[axis] = player.rect[axis]-self.rect.size[axis]
else:
self.rect[axis] = player.rect[axis]+player.rect.size[axis]
self.change_direction(now)
def change_direction(self, now):
"""Called when the platform reaches an endpoint or has no more room."""
self.waiting = True
self.timer = now
self.speed *= -1
"""class Spell(pg.sprite.Sprite):
def __init__(self, location, angle)"""
class Control(object):
"""Class for managing event loop and game states."""
def __init__(self):
"""Initalize the display and prepare game objects."""
self.screen = pg.display.get_surface()
self.screen_rect = self.screen.get_rect()
self.clock = pg.time.Clock()
self.fps = 60.0
self.keys = pg.key.get_pressed()
self.done = False
self.player = Player((50,875), 4)
self.viewport = self.screen.get_rect()
self.level = pg.Surface((1000,1000)).convert()
self.level_rect = self.level.get_rect()
self.win_text,self.win_rect = self.make_text()
self.obstacles = self.make_obstacles()
self.bolts = pg.sprite.Group()
def make_text(self):
"""Renders a text object. Text is only rendered once."""
font = pg.font.Font(None, 100)
message = "You win. Celebrate."
text = font.render(message, True, (100,100,175))
rect = text.get_rect(centerx=self.level_rect.centerx, y=100)
return text, rect
def make_obstacles(self):
"""Adds some arbitrarily placed obstacles to a sprite.Group."""
walls = [Block(pg.Color("chocolate"), (0,980,1000,20)),
Block(pg.Color("chocolate"), (0,0,20,1000)),
Block(pg.Color("chocolate"), (980,0,20,1000))]
static = [Block(pg.Color("darkgreen"), (250,780,200,100)),
Block(pg.Color("darkgreen"), (600,880,200,100)),
Block(pg.Color("darkgreen"), (20,360,880,40)),
Block(pg.Color("darkgreen"), (950,400,30,20)),
Block(pg.Color("darkgreen"), (20,630,50,20)),
Block(pg.Color("darkgreen"), (80,530,50,20)),
Block(pg.Color("darkgreen"), (130,470,200,215)),
Block(pg.Color("darkgreen"), (20,760,30,20)),
Block(pg.Color("darkgreen"), (400,740,30,40))]
moving = [MovingBlock(pg.Color("olivedrab"), (20,740,75,20), 325, 0),
MovingBlock(pg.Color("olivedrab"), (600,500,100,20), 880, 0),
MovingBlock(pg.Color("olivedrab"),
(420,430,100,20), 550, 1, speed=3, delay=200),
MovingBlock(pg.Color("olivedrab"),
(450,700,50,20), 930, 1, start=930),
MovingBlock(pg.Color("olivedrab"),
(500,700,50,20), 730, 0, start=730),
MovingBlock(pg.Color("olivedrab"),
(780,700,50,20), 895, 0, speed=-1)]
return pg.sprite.Group(walls, static, moving)
def update_viewport(self):
"""
The viewport will stay centered on the player unless the player
approaches the edge of the map.
"""
self.viewport.center = self.player.rect.center
self.viewport.clamp_ip(self.level_rect)
def event_loop(self):
"""We can always quit, and the player can sometimes jump."""
for event in pg.event.get():
if event.type == pg.QUIT or self.keys[pg.K_ESCAPE]:
self.done = True
elif event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
self.player.jump(self.obstacles)
elif event.type == pg.KEYUP:
if event.key == pg.K_SPACE:
self.player.jump_cut()
elif event.type == pg.MOUSEMOTION or event.type == pg.MOUSEBUTTONDOWN:
self.player.get_event(event, self.bolts)
def update(self):
"""Update the player, obstacles, and current viewport."""
self.keys = pg.key.get_pressed()
self.player.pre_update(self.obstacles)
self.obstacles.update(self.player, self.obstacles)
self.player.update(self.obstacles, self.keys)
self.update_viewport()
self.bolts.update(self.screen_rect, self.obstacles)
def draw(self):
"""
Draw all necessary objects to the level surface, and then draw
the viewport section of the level to the display surface.
"""
self.level.fill(pg.Color("lightblue"))
self.obstacles.draw(self.level)
self.level.blit(self.win_text, self.win_rect)
self.player.draw(self.level)
self.bolts.draw(self.level)
self.screen.blit(self.level, (0,0), self.viewport)
def display_fps(self):
"""Show the programs FPS in the window handle."""
caption = "{} - FPS: {:.2f}".format(CAPTION, self.clock.get_fps())
pg.display.set_caption(caption)
def main_loop(self):
"""As simple as it gets."""
while not self.done:
self.event_loop()
self.update()
self.draw()
pg.display.update()
self.clock.tick(self.fps)
self.display_fps()
if __name__ == "__main__":
os.environ['SDL_VIDEO_CENTERED'] = '1'
pg.init()
pg.display.set_caption(CAPTION)
pg.display.set_mode(SCREEN_SIZE)
PLAYERIMG = pg.image.load("playertst.png").convert()
PLAYERIMG.set_colorkey(COLOR_KEY)
run_it = Control()
run_it.main_loop()
pg.quit()
sys.exit()
The % 2*pi unnecessary, and your get_angle function has no return value, but you do an assignment to self.angle = self.get_angle, but that is not the issue. The issue is that the mouse position is relative to the screen (i.e. clicking in the top right area of your game screen will always yield (0,480) if your screen is 640x480), while the position of the (character) rectangle is given in your game play area, which is larger than the screen, ergo if you move the character and thus the view shifts, you are getting coordinates in two different coordinate systems. You will have to keep track of where the view is in your game play area and add the offset to the mouse coordinates.

Pygame 2d tile scrolling edges don't load

Im trying to make a 2d game in pygame that kinda works like pokemon. Ive gotten really stuck on a problem now that I dont know how to solve.
When I move my character I scroll the map instead so the player stays centered. I've created an "animation" by offsetting the distance by 2 pixels at a time instead of moving the full tile size so I get smooth movemen. The problem I have is that when I move, the screen doesn't load the new tiles in the edges that i'm moving towards so the edges end up a white space until i've completed the full animation. I'll link my code and hopefully someone can help me :)
TILESIZE = 32
MAP_WIDTH = 25
MAP_HEIGHT = 25
class Player(pygame.sprite.Sprite):
def __init__(self, color, width, height):
# Call the parent class (Sprite) constructor
super().__init__()
self.name = "Player"
self.width = width
self.height = height
self.image = pygame.Surface([width, height])
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.x = int(TILESIZE * (MAP_WIDTH / 2)) - TILESIZE / 2
self.rect.y = int(TILESIZE * (MAP_HEIGHT / 2)) - TILESIZE / 2
class World:
def __init__(self):
self.shiftX = 0
self.shiftY = 0
self.tile_map = [ [DIRT for w in range(MAP_WIDTH)] for h in range(MAP_HEIGHT)]
for row in range(MAP_HEIGHT):
for column in range(MAP_WIDTH):
try:
if real_map[row + self.shiftY][column + self.shiftX] == 0:
tile = DIRT
elif real_map[row + self.shiftY][column + self.shiftX] == 1:
tile = GRASS
elif real_map[row + self.shiftY][column + self.shiftX] == 2:
tile = WATER
else:
tile = DIRT
self.tile_map[row][column] = tile
except:
self.tile_map[row][column] = WATER
def shiftWorld(self):
for row in range(MAP_HEIGHT):
for column in range(MAP_WIDTH):
try:
if real_map[row + self.shiftY][column + self.shiftX] == 0:
tile = DIRT
elif real_map[row + self.shiftY][column + self.shiftX] == 1:
tile = GRASS
elif real_map[row + self.shiftY][column + self.shiftX] == 2:
tile = WATER
else:
tile = DIRT
self.tile_map[row][column] = tile
except:
self.tile_map[row][column] = WATER
def okToMove(self, key):
if key[K_w]:
if self.tile_map[int(MAP_WIDTH/2 - 1)][int(MAP_HEIGHT/2)] != 2:
return True
elif key[K_s]:
if self.tile_map[int(MAP_WIDTH/2 + 1)][int(MAP_HEIGHT/2)] != 2:
return True
elif key[K_a]:
if self.tile_map[int(MAP_WIDTH/2)][int(MAP_HEIGHT/2) - 1] != 2:
return True
elif key[K_d]:
if self.tile_map[int(MAP_WIDTH/2)][int(MAP_HEIGHT/2) + 1] != 2:
return True
def start_game():
pygame.init()
clock = pygame.time.Clock()
#HÄR KAN VI MÅLA UPP MER
#SCREEN = pygame.display.set_mode((MAP_WIDTH*TILESIZE, MAP_HEIGHT*TILESIZE))
world = World()
SCREEN = pygame.display.set_mode((TILESIZE * (MAP_WIDTH-2), TILESIZE * (MAP_HEIGHT-4)))
running = True
player = Player(BLACK, 32, 32)
sprites = pygame.sprite.Group()
sprites.add(player)
movement = 0
offsetY = 0
offsetX = 0
animation_north = False
animation_south = False
animation_west = False
animation_east = False
while running:
for event in pygame.event.get():
if event.type==QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
#Get keyinput and do whatever needs to be done
key = pygame.key.get_pressed()
if key[K_ESCAPE]:
pygame.quit()
sys.exit()
if animation_east or animation_north or animation_south or animation_west:
pass
else:
if key[K_w]:
okToMove = world.okToMove(key)
if okToMove == True:
animation_north = True
else:
pass
elif key[K_a]:
okToMove = world.okToMove(key)
if okToMove == True:
animation_west = True
elif key[K_s]:
okToMove = world.okToMove(key)
if okToMove == True:
animation_south = True
elif key[K_d]:
okToMove = world.okToMove(key)
if okToMove == True:
animation_east = True
if animation_north == True:
if movement == 32:
movement = 0
world.shiftY -= 1
world.shiftWorld()
offsetY = 0
animation_north = False
else:
offsetY += 4
movement += 4
if animation_south == True:
if movement == 32:
movement = 0
world.shiftY += 1
world.shiftWorld()
offsetY = 0
animation_south = False
intY = 0
else:
offsetY -= 4
movement += 4
if animation_west == True:
if movement == 32:
movement = 0
world.shiftX -= 1
world.shiftWorld()
offsetX = 0
animation_west = False
else:
offsetX += 4
movement += 4
if animation_east == True:
if movement == 32:
world.shiftX += 1
world.shiftWorld()
movement = 0
offsetX = 0
animation_east = False
else:
offsetX -= 4
movement += 4
SCREEN.fill(WHITE)
for row in range(MAP_HEIGHT):
for column in range(MAP_WIDTH):
SCREEN.blit(textures[world.tile_map[row][column]], (column*TILESIZE + offsetX, row*TILESIZE + offsetY))
sprites.draw(SCREEN)
pygame.display.update()
pygame.display.flip()
clock.tick(60)
start_game()
I am writing a similar game and I will share my logic with you.
my blocks are 32x32 so each block is 32 pixesl.
The outer border is the sprites screen and the inner square is the monitor.
You always have one sprite extra on all sides of the screen. now if you count the pixel movement on any side of the screen it's easy for you to keep track of when you need to draw the next row or column of sprites but not that they are always DRAWN OFF SCREEN. if my pixel movement is -8 (left movement) I draw a column of sprites on the right side just on the border os the screen but OUTSIDE the visible area. Same goes for the other side.
Here is some code from my program. This is the sprite adding code.
def add_sprites(self):
"""sprites are added to the group which appear on screen right. the column number is the value in ColDrawn. We selct columns from the list according to this value. Once the end of the column list is reached we start again from the first one. We cycle thru the list depending on the NumCycle[0] value."""
if self.ColDrawn < self.Columns_in_Dungeon - 1:
self.ColDrawn += 1
else: # all the columns drawn so increment the flag
self.ColDrawn = 0
self.NumCycle[1] += 1
if self.NumCycle[1] >= self.NumCycle[0]: # if the flag is equal to the number of cycles the screen is scrolled then set numcycle[2] to True
self.NumCycle[2] = True
else: # screen can be scrolled
spritecol = self.all_wall_sprite_columns_list[self.ColDrawn]
self.wallspritegroup.add(spritecol) # add column of sprites to the sprite group
return
and here is the sprite removing code.
def remove_sprites(self):
"""sprites are removed from the group as they exit from screen left."""
for sprt in self.wallspritegroup: # remove_basic sprites that move_basic off screen on left
if sprt.rect.x <= -48:
sprt.rect.x = self.screenw # reset the x position and
sprt.kill()
#spritegrp.remove_basic(sprt) # remove_basic the sprite from the sprite group
return
The code is quite easy to follow as I have commented them. Hope this helps.

Categories