I'm following a tutorial for a Mario / Platformer type game in pygame and tiled, and currently I'm adding the 'player goal' part to display where the goal is and for some reason currently when I run the Mario / Platformer type game I get the error "AttributeError: 'int' object has no attribute 'sprites' " for self.goal.update(self.world_shift) in line 150
Here's the code:
import pygame
from support import import_csv_layout, import_cut_graphics
from settings import tile_size
from tiles import Tile, StaticTile, Crate, Coin, Palm
from enemy import Enemy
class Level:
def __init__(self, level_data, surface):
super().__init__()
# general setup
self.display_surface = surface
self.world_shift = 0
# player setup
player_layout = import_csv_layout(level_data['player'])
self.player = pygame.sprite.GroupSingle
self.goal = pygame.sprite.GroupSingle
self.player_setup(player_layout)
# terrain setup
terrain_layout = import_csv_layout(level_data['terrain'])
self.terrain_sprites = self.create_tile_group(terrain_layout, 'terrain')
# grass setup
grass_layout = import_csv_layout(level_data['grass'])
self.grass_sprites = self.create_tile_group(grass_layout, 'grass')
# crates
crate_layout = import_csv_layout(level_data['crates'])
self.crate_sprites = self.create_tile_group(crate_layout, 'crates')
# coins
coin_layout = import_csv_layout(level_data['coins'])
self.coin_sprites = self.create_tile_group(coin_layout, 'coins')
# foreground palms
fg_palm_layout = import_csv_layout(level_data['fg palms'])
self.fg_palm_sprites = self.create_tile_group(fg_palm_layout, 'fg palms')
# background palms
bg_palm_layout = import_csv_layout(level_data['bg palms'])
self.bg_palm_sprites = self.create_tile_group(bg_palm_layout, 'bg palms')
# enemy
enemy_layout = import_csv_layout(level_data['enemies'])
self.enemy_sprites = self.create_tile_group(enemy_layout, 'enemies')
# constraint
contraint_layout = import_csv_layout(level_data['constraints'])
self.contraint_sprites = self.create_tile_group(contraint_layout, 'constraints')
def create_tile_group(self, layout, type):
global sprite
sprite_group = pygame.sprite.Group()
for row_index, row in enumerate(layout):
for col_index, val in enumerate(row):
if val != '-1':
x = col_index * tile_size
y = row_index * tile_size
if type == 'terrain':
terrain_tile_list = import_cut_graphics('../graphics/terrain/terrain_tiles.png')
tile_surface = terrain_tile_list[int(val)]
sprite = StaticTile(tile_size, x, y, tile_surface)
sprite_group.add(sprite)
if type == 'grass':
grass_tile_list = import_cut_graphics('../graphics/decoration/grass/grass.png')
tile_surface = grass_tile_list[int(val)]
sprite = StaticTile(tile_size, x, y, tile_surface)
if type == 'crates':
sprite = Crate(tile_size, x, y)
if type == 'coins':
if val == '0':
sprite = Coin(tile_size, x, y, '../graphics/coins/gold')
elif val == '1':
sprite = Coin(tile_size, x, y, '../graphics/coins/silver')
if type == 'fg palms':
if val == '0':
sprite = Palm(tile_size, x, y, '../graphics/terrain/palm_small', 38)
elif val == '1':
sprite = Palm(tile_size, x, y, '../graphics/terrain/palm_large', 64)
if type == 'bg palms':
sprite = Palm(tile_size, x, y, '../graphics/terrain/palm_bg', 64)
if type == 'enemies':
sprite = Enemy(tile_size, x, y)
if type == 'constraints':
sprite = Tile(tile_size, x, y)
sprite_group.add(sprite)
return sprite_group
def player_setup(self, layout):
for row_index, row in enumerate(layout):
for col_index, val in enumerate(row):
x = col_index * tile_size
y = row_index * tile_size
if val == '0':
pass
if val == '1':
hat_surface = pygame.image.load('../graphics/character/hat.png').convert_alpha()
sprite = StaticTile(tile_size, x, y, hat_surface)
self.goal.add(sprite)
def enemy_collision_reverse(self):
for enemy in self.enemy_sprites.sprites():
if pygame.sprite.spritecollide(enemy, self.contraint_sprites, False):
enemy.reverse()
def run(self):
# background palms
self.bg_palm_sprites.update(self.world_shift)
self.bg_palm_sprites.draw(self.display_surface)
# terrain
self.terrain_sprites.update(self.world_shift)
self.terrain_sprites.draw(self.display_surface)
# enemy
self.enemy_sprites.update(self.world_shift)
self.contraint_sprites.update(self.world_shift)
self.enemy_collision_reverse()
self.enemy_sprites.draw(self.display_surface)
# crates
self.crate_sprites.update(self.world_shift)
self.crate_sprites.draw(self.display_surface)
# grass
self.grass_sprites.update(self.world_shift)
self.grass_sprites.draw(self.display_surface)
# coins
self.coin_sprites.update(self.world_shift)
self.coin_sprites.draw(self.display_surface)
# foreground palms
self.fg_palm_sprites.update(self.world_shift)
self.fg_palm_sprites.draw(self.display_surface)
# player spites
self.goal.update(self.world_shift)
self.goal.draw(self.display_surface)
These do not create a group object that you can add a sprite to, they instead just assign the
GroupSingleclass to your variables. To create groups, you would need:This creates an object of the class and calls the dunder-init function to initialise it, then returns that initialised object to you. Classes (as well as functions, modules, and packages) are first-class objects in Python, as demonstrated with a similar case:
Here's a more germane example of omitting the parentheses:
It's not exactly the same message that you have but that's probably because I'm not going through the full route that you do (i.e., through the sprite processing code).
The error shown is what you see when you call an object function through the class variable (no
selfis automatically added in that scenario).I suspect that, based on the error you're seeing, the class object is being treated as an integer somewhere along that route, though I can't be certain of that without a deep dive into the
pygamesource code.And, for completeness, this is an example with the parentheses, where you can see it working properly: