Understanding & making infinite bullets to shoot in Pygame - python-3.x

I'm learning python and also pygame, and I want to know why the bullets doesn't display, I tough that maybe the screen.update_screen() can be interfering but no, that's not the case, I need help to undertand how Bullets work in python because clearly my method is not working, I've seen many methods in other posts, and they use a limited ammount of bullets to shoot (don't know why, in my case I want infinite bullets) so what should I add to see the Bullets, I know that I need to add a remover for seing a "movement" in the display, but I don't know how, any help is appreciated.
# -*- coding: utf-8 -*-
import pygame
class Screen(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
pygame.display.set_caption("Space Game")
self.screen_width = 800
self.screen_heigh = 600
self.picture = pygame.image.load("screen.png")
self.screen = pygame.display.set_mode((self.screen_width, self.screen_heigh))
def update_screen(self):
self.screen.blit(self.picture, (0, 0))
def update_obj(self, object):
self.screen.blit(object.picture, object.rect)
def update_shoot(self, object):
for y in range(object.rect.centery, 600, 10):
self.screen.blit(object.picture, (object.rect.centerx, object.rect.centery + y))
class Ship(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.picture = pygame.image.load("ship.png")
self.rect = self.picture.get_rect()
self.rect.centerx = 400
self.rect.centery = 500
class Bullet(pygame.sprite.Sprite):
def __init__(self, object):
pygame.sprite.Sprite.__init__(self)
self.picture = pygame.image.load("shoot.png")
self.rect = self.picture.get_rect()
self.rect.centerx = object.rect.centerx
self.rect.centery = (object.rect.centery + 25)
def main():
pygame.init()
done = False
clock = pygame.time.Clock()
screen = Screen()
ship = Ship()
bullet = Bullet(ship)
while not done:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
ship.rect.centerx -= 5
if keys[pygame.K_RIGHT]:
ship.rect.centerx += 5
if keys[pygame.K_UP]:
ship.rect.centery -= 5
if keys[pygame.K_DOWN]:
ship.rect.centery += 5
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
done = True
if event.key == pygame.K_SPACE:
print ("shoot")
#for some weird reason the bullet doesn't display
screen.update_shoot(bullet)
screen.update_screen()
screen.update_obj(ship)
pygame.display.update()
clock.tick(10)
pygame.quit()
if __name__ == "__main__":
main()

To shoot bullets you usually create instances of a Bullet class and add them to a list, pygame.sprite.Group or another container. Then you iterate over this container and call the update method of the bullet in which its position is changed. To blit the images of the sprites/objects you iterate again over the container and just blit the images onto the screen. With sprite groups you can just call sprite_group.update() and sprite_group.draw(screen) instead of iterating yourself. BTW, pygame sprites have to have a self.image attribute not a self.picture in order to work with sprite groups (take a look at Program Arcade Games for more information).
I started to modify a few things in your example to show you how to use sprite groups, but then ended up changing your whole Screen class into a Game class (which I recommend to use in the future).
import sys
import pygame
class Game:
def __init__(self):
self.done = False
self.screen_width = 800
self.screen_height = 600
self.image = pygame.Surface((800, 600))
self.image.fill((30, 40, 50))
self.screen = pygame.display.set_mode(
(self.screen_width, self.screen_height))
# all_sprites is used to update and draw all sprites together.
self.all_sprites = pygame.sprite.Group()
# You'll probably need a separate bullet_group
# later for collision detection with enemies.
self.bullet_group = pygame.sprite.Group()
self.ship = Ship()
self.all_sprites.add(self.ship)
bullet = Bullet(self.ship)
self.bullet_group.add(bullet)
self.all_sprites.add(bullet)
def handle_events(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.ship.rect.centerx -= 5
if keys[pygame.K_RIGHT]:
self.ship.rect.centerx += 5
if keys[pygame.K_UP]:
self.ship.rect.centery -= 5
if keys[pygame.K_DOWN]:
self.ship.rect.centery += 5
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
self.done = True
if event.key == pygame.K_SPACE:
bullet = Bullet(self.ship)
self.bullet_group.add(bullet)
self.all_sprites.add(bullet)
def update(self):
# Calls `update` methods of all contained sprites.
self.all_sprites.update()
def draw(self):
self.screen.blit(self.image, (0, 0))
self.all_sprites.draw(self.screen) # Draw the contained sprites.
pygame.display.update()
class Ship(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((20, 30))
self.image.fill((50, 170, 230))
# A nicer way to set the start pos with `get_rect`.
self.rect = self.image.get_rect(center=(400, 500))
class Bullet(pygame.sprite.Sprite):
def __init__(self, ship):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((7, 7))
self.image.fill((230, 140, 30))
self.rect = self.image.get_rect()
self.rect.centerx = ship.rect.centerx
self.rect.centery = ship.rect.centery - 25
def update(self):
self.rect.y -= 5 # Move up 5 pixels per frame.
def main():
pygame.init()
pygame.display.set_caption('Space Game')
clock = pygame.time.Clock()
game = Game()
while not game.done:
game.handle_events()
game.update()
game.draw()
clock.tick(30)
if __name__ == '__main__':
main()
pygame.quit()
sys.exit()

Related

NameError: Name 'bullets' is not defined

I'm creating the alien invasion game in the python crash course book and i'm getting a name error that says bullets is not defined. So I changed 'bullets' to 'Bullet', which gives me an attribute error saying Bullet has no attribute 'sprites'.
here is my code so far.
alien_invasion.py
import pygame
from pygame.sprite import Group
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
# Initialize pygame, settings, and screen object.
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode(
(ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("Alien Invasion")
# Make a ship.
ship = Ship(ai_settings, screen)
# Make a group to store bullets in.
bullets = Group()
# Start the main loop for the game.
while True:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
bullets.update()
gf.update_screen(ai_settings, screen, ship, bullets)
run_game()
settings.py
class Settings():
"""A class to store all settings for Alien Invasion."""
def __init__(self):
"""Initialize the game's settings."""
# Screen settings
self.screen_width = 1200
self.screen_height = 600
self.bg_color = (230, 230, 230)
# Ship settings
self.ship_speed_factor = 1.5
# Bullet settings
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 60, 60, 60
ship.py
import pygame
class Ship():
def __init__(self, ai_settings, screen):
"""Initialize the ship and set its starting position."""
self.screen = screen
self.ai_settings = ai_settings
# Load the ship image and get its rect.
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
# Start each new ship at the bottom center of the screen.
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# Store a decimal value for the ship's center.
self.center = float(self.rect.centerx)
# Movement flags
self.moving_right = False
self.moving_left = False
def update(self):
"""Update the ship's position based on movement flags."""
# Update the ship's center value, not the rect.
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
self.center -= self.ai_settings.ship_speed_factor
# Update rect object from self.center.
self.rect.centerx = self.center
def blitme(self):
"""Draw the ship at its current location."""
self.screen.blit(self.image, self.rect)
game_functions.py
import sys
import pygame
from bullet import Bullet
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""Respond to keypresses."""
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.key == pygame.K_SPACE:
# Create a new bullet and add it to the bullets group.
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def check_keyup_events(event, ship):
"""Respond to key releases."""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def check_events(ai_settings, screen, ship, bullets):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ai_settings, ship)
# Redraw all bullets behind ship and aliens.
for bullet in Bullet.sprites():
bullet.draw_bullet()
def update_screen(ai_settings, screen, ship, bullets):
"""Update images on the screenand flip to the new screen."""
# Redraw the screen during each pass through the loop.
screen.fill(ai_settings.bg_color)
# Redraw all bullets behind ship and aliens.
for bullet in Bullet.sprites():
bullet.draw_bullet()
ship.blitme()
# Make the most recently drawn screen visible.
pygame.display.flip()
bullet.py
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""A class to manage bullets fired from the ship"""
def __init__(self, ai_settings, screen, ship):
"""Create a bullet object at the ship's current position."""
super().__init__()
self.screen = screen
# Create a bullet rect at (0, 0) and then set the correct position.
self.rect = pygame.Rect(0, 0, ai_settings.bullet_width,
ai_settings.bullet_height)
self.rect.centerx = ship.rect.centerx
self.rect.top = ship.rect.top
# Store the bullet's position as a decimal value.
self.y = float(self.rect.y)
self.color = ai_settings.bullet_color
self.speed_factor = ai_settings.bullet_speed_factor
def update(self):
"""Move the bullet up the screen."""
# Update the decimal position of the bullet.
self.y -= self.speed_factor
# Update the rect position.
self.rect.y = self.y
def draw_bullet(self):
"""Draw the bullet to the screen."""
pygame.draw.rect(self.screen, self.color, self.rect)
This is the origional error:
game_functions.py, line 36, in <module>
for bullet in bullets.sprites():
NameError: name 'bullets' is not defined
This is the error after I change 'bullets' to 'Bullet' in order to match the "class Bullet(Sprite)" in bullet.py:
game_functions.py, line 36, in <module>
for bullet in Bullet.sprites():
AttributeError: type object 'Bullet' has no attribute 'sprites'
I would greatly appreciate it if the problem were to be resolved. Also this is my first time asking a question as i am relatively new to coding. Let me know if I did something wrong when asking this question.
The error is coming from a two-line for-loop that is not inside any function. These lines will be executed only when the game_functions module is imported. This does not seem useful. You can probably just delete these lines without breaking anything.
The same two lines appear in the immediately following update_screen function. Between the names bullets and Bullet, only one has a sprites() method. That is the one you need for your for-loop.
for bullet in Bullet.sprites():
bullet.draw_bullet()
I think it should be:
for bullet in self.bullets.sprites():
bullet.draw_bullet()
you're not calling self which would cause it not to run.

Python crash coursebook alien invasion game

I encountered an error while trying to fire bullets in the alien invasion game I copied most of the code from the book but I must have left somethings out. This is my whole code and since I started to add the bullets i haven't been able to move the ship does anyone have a solution?
pythoncrashcoursebook.py
import pygame
import refactors as rf
from game_settings import Settings
from ship import Ship
from pygame.sprite import Group
def run_game():
# Initialize game and make a screen object and caption
pygame.init()
# Make a settings instance
settings = Settings()
# Make a screen
screen = pygame.display.set_mode((settings.screen_height, settings.screen_width))
# Make a ship
ship = Ship(screen, settings)
# Make a group to store bullets
bullets = Group()
# Display caption
pygame.display.set_caption("Alien Invasion")
# Main game loop
while True:
rf.check_events(settings, screen, ship, bullets)
ship.update()
bullets.update()
rf.update_screen(settings, screen, ship, bullets)
run_game()
ship.py
import pygame
class Ship:
def __init__(self, screen, settings):
# Initialize the ship and start position
self.screen = screen
self.settings = settings
# Load the image and get the rect of the ship
self.image = pygame.image.load("spaceship.png")
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
# Set the ship at the bottom of the screen
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# Store a decimal value for the ship's center
self.center = float(self.rect.centerx)
# Moving flag
self.moving_right = False
self.moving_left = False
def update(self):
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
self.center -= self.settings.ship_speed_factor
# Update rect object from self.center
self.rect.centerx = self.center
def blitme(self):
# Draw the ship
self.screen.blit(self.image, self.rect)
bullet.py
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""Class to control bullet"""
def __init__(self, settings, screen, ship):
"""Create bullet at ship position"""
super(Bullet, self).__init__()
self.screen = screen
# Create bullet rect at (0, 0) and then correct position
self.rect = pygame.Rect(0, 0, settings.bullet_width, settings.bullet_height)
self.rect.centerx = ship.rect.centerx
self.rect.top = ship.rect.top
# Store the bullet's position as a decimal
self.y = float(self.rect.y)
# Store bullet colour and speed
self.colour = settings.bullet_colour
self.bullet_speed = settings.bullet_speed_factor
def update_bullet(self):
"""Move the bullet up the screen"""
self.y -= self.bullet_speed
# Update rect position
self.rect.y = self.y
def draw_bullet(self):
"""Draw bullet on the screen"""
pygame.draw.rect(self.screen, self.colour, self.rect)
game_settings.py
class Settings:
# A class to store all the game settings
def __init__(self):
# Ship settings
self.ship_speed_factor = 1.5
# Screen settings
self.bg_colour = (0, 0, 255)
self.screen_height = 800
self.screen_width = 600
# Bullet settings
self.bullet_colour = 60, 60, 60
self.bullet_height = 15
self.bullet_width = 3
self.bullet_speed_factor = 1
refactors.py
import sys
import pygame
from bullet import Bullet
def check_events(ship, settings, screen, bullets):
# Check for events and assign functions to them
for event in pygame.event.get():
# To quit
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_key_down_event(event, ship, settings, screen, bullets)
elif event.type == pygame.KEYUP:
check_key_up_event(event, ship)
def check_key_down_event(event, ship, settings, screen, bullets):
"""Respond to keypress"""
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.key == pygame.K_SPACE:
# Create a mew bullet and add it to the bullet group
new_bullet = Bullet(settings, screen, ship)
bullets.add(new_bullet)
def check_key_up_event(event, ship):
"""Respond to key releases"""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def update_screen(settings, screen, ship, bullets):
screen.fill(settings.bg_colour)
# Redraw all bullets behind aliens and ship
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
pygame.display.flip()
And this is the error
line 14, in __init__
self.rect = pygame.Rect(0, 0, settings.bullet_width, settings.bullet_height)
AttributeError: 'pygame.Surface' object has no attribute 'bullet_width'
You should try changing:
rf.check_events(settings, screen, ship, bullets)
to:
rf.check_events(ship, settings, screen, bullets)
in your main code.

Don't know how to spawn multiple enemies

My code should spawn multiple enemies that chase around my player. Instead, the enemy just stays in one place. This only happens when I try to input a value into the enemy-spawning function for the spawn coordinates. Heres the code:
class spawn(object):
def __init__(self,place1,place2):
self.place1=place1
self.place2=place2
def AIPrototype(self):#The important parts to this error star here
global x,y,x1,y1
pygame.draw.rect(screen,THECOLORS['blue'],(self.place1,self.place2,50,50))
x1=self.place1
y1=self.place2#end here
if x1<x:
xspeed1=1
slopex1=x-x1
if x1>x:
xspeed1=-1
slopex1=x1-x
if y1<y:
yspeed1=1
slopey1=y-y1
if y1>y:
yspeed1=-1
slopey1=y1-y
#
hypo1=((slopex1**2)+(slopey1**2))**0.5
speedmark1=hypo1/3
speedy1=slopey1/speedmark1
speedx1=slopex1/speedmark1
movex1=speedx1
movey1=speedy1
if x1<=640 and x1>=0:
if x1>x:
x1+=xspeed1*movex1
if x1<x:
xspeed1=0
if y1<=480 and x1>=0:
if y1>y:
y1+=yspeed1*movey1
if y1<y:
yspeed1=0
if x1<=640 and x1>=0:
if x1<x:
x1+=xspeed1*movex1
if x1>x:
xspeed1=0
if y1<=480 and x1>=0:
if y1<y:
y1+=yspeed1*movey1
if y1>y:
yspeed1=0
#
if x1>640:
x1=640
if x1<0:
x1=0
if y1>480:
y1=480
if y1<0:
y1=0
self.place1=x1#start
self.place2=y1#end
That's the fucntion for spawning the enemies. The fucntion is called here:
clock = pygame.time.Clock()
keepGoing = True
try:
while keepGoing:
clock.tick(60)
screen.fill(THECOLORS['red'])
pygame.draw.rect(screen,THECOLORS['green'],(x,y,50,50))
char()
spawn1=spawn(200,200)#start
spawn1.AIPrototype()#end
pygame.display.flip()
I don't know where my error in the coding is, so if someone could help me out, that would be great.
Here's an example with vectors. To get the velocity of the enemies, I calculate the vector to the player, then normalize it and scale by 4 (the final speed).
vel = (player.center - self.pos).normalize() * 4
Then you can just add the velocity to the pos and set the rect.center to the updated pos vector to move the object.
To spawn new enemies, first create a list that should hold the enemy instances and then just append new instances when an enemy spawns (press the f-key to spawn them). You can also use pygame sprites and sprite groups instead of the Enemy class and the list.
import sys
import pygame as pg
class Enemy:
def __init__(self, pos, color):
self.rect = pg.Rect(pos, (26, 45))
self.color = color
self.pos = pg.math.Vector2(pos)
def update(self, player):
vel = (player.center - self.pos).normalize() * 4
self.pos += vel
self.rect.center = self.pos
def draw(self, screen):
pg.draw.rect(screen, self.color, self.rect)
def main():
screen = pg.display.set_mode((640, 480))
bg_color = pg.Color('gray12')
player_color = pg.Color('dodgerblue1')
enemy_color = pg.Color('sienna1')
clock = pg.time.Clock()
player = pg.Rect((100, 300), (26, 50))
enemy_list = [Enemy((100, 300), enemy_color)]
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.KEYDOWN:
if event.key == pg.K_f:
enemy_list.append(Enemy((400, 0), enemy_color))
keys = pg.key.get_pressed()
if keys[pg.K_a]:
player.x -= 5
elif keys[pg.K_d]:
player.x += 5
if keys[pg.K_w]:
player.y -= 5
elif keys[pg.K_s]:
player.y += 5
for enemy in enemy_list:
enemy.update(player)
screen.fill(bg_color)
pg.draw.rect(screen, player_color, player)
for enemy in enemy_list:
enemy.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()

Positioning an Entity in a Class (Pygame)

I'm trying to blit an image of grass onto my game in a specific area, but I'm having trouble doing so because it's in a class. I want to do something like this..
grass_platform.draw(screen, (200, 200))
NOTE: I know this actually doesn't work
Here's my code...
import pygame
pygame.init()
#Screen Size
screen_size = [1024,576]
#Display Window
screen = pygame.display.set_mode(screen_size)
pygame.display.set_caption('The Adventures of Fresco the Explorer')
#Clock
clock = pygame.time.Clock()
#Colors
black = (0,0,0)
white = (255,255,255)
#Game Start
gameStart = False
#Backgrounds
forest = pygame.image.load('forest.png')
#Gravity
gravity =-10
fall = True
#Player
fresco = pygame.image.load('fresco v2.png').convert()
fresco = pygame.transform.scale(fresco,(32,136))
class Player(pygame.sprite.Sprite):
def __init__(self, x, y, filename):
pygame.sprite.Sprite.__init__(self)
self.x = x
self.y = y
self.image = fresco
self.rect = self.image.get_rect()
def update (self):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
self.rect.x -= 6
self.image = pygame.transform.flip(fresco, True, False)
elif event.key == pygame.K_d:
self.rect.x += 6
self.image = pygame.transform.flip(fresco, False, False)
elif event.key == pygame.K_w:
self.rect.y -= 20
def draw(self, screen):
screen.blit(self.image, self.rect)
player = Player(0, 0, 'fresco v2.png')
#Grass Platform
grass = pygame.image.load('grass.png')
grass = pygame.transform.scale(grass, (90, 90))
class Grass (pygame.sprite.Sprite):
def __init__(self, filename):
self.image = grass
self.rect = self.image.get_rect()
def draw(self, screen):
screen.blit(self.image,self.rect)
grass_platform = Grass('grass.png')
#Game Loop
while not gameStart:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameStart = True
#Background Blitted
screen.blit(forest,(0,0))
#Falling Event
if fall == True:
if gravity:
player.rect.y -= gravity
#Class Blitted
player.update()
player.draw(screen)
grass_platform.draw(screen)
#Updates Screen
pygame.display.update()
#FPS
clock.tick(30)
pygame.quit()
I dont know if i understood you correctly but if you mean just passing a coordinate parameter and blitting to that position that should be quite easy.
class Grass (pygame.sprite.Sprite):
def __init__(self, filename):
self.image = grass
self.rect = self.image.get_rect()
def draw(self, screen, pos):
screen.blit(self.image, pos)
and then when you draw() you just do this:
grass_platform.draw(screen, (300, 200))
or on the other hand if you want to keep the position you could add
this code to the init method:
def __init__(self, filename, x, y):
self.x = x
self.y = y
and then acces it later
def draw(self, screen):
window.blit(img, (self.x, self.y))
Some notes:
Player and Grass are subclasses from Sprite, so they have already a draw function which does exactly what you are doing. Just remove them.
the following part
#Falling Event
if fall == True:
if gravity:
player.rect.y -= gravity
is specific to the Player class and should be moved to the Player class' update function
When using the Sprite class, the rect attribute is used to store the position of the sprite, hence the x and y attribute in your Player class don't make much sense. Remove them, and use the x and y parameters to change the x and y attribute of the rect instead:
self.rect = self.image.get_rect(x=x, y=y)
Hence, if you want to draw grass_platform at a specific position, just alter it's rect, e.g.:
grass_platform.rect.topleft = (200, 200)
instead of calling draw and update on each of your sprites manually, just but them into a Group, and call draw and update on that Group.

How to detect collision/mouse-over between the mouse and a sprite?

Goal: to know when a sprite is clicked / active with the mouse
Using: Python 3.2 64bit, Pygame 1.92 64bit, windows 7 64bit
I spend 6 hours to no avail...I've tried :
s.rect.collidepoint(pygame.mouse.get_pos())
s.collidepoint(pygame.mouse.get_pos())
s.sprite.spritecollide(pygame.mouse.get_pos())
s.spritecollide(pygame.mouse.get_pos())
s.sprite.collide_rect(pygame.mouse.get_pos())
s.collide_rect(pygame.mouse.get_pos())
I've also tried turning the mouse location, which I really don't want to do, like someone else mentioned here on another post, into a sprite and collide like that with the same results ;(
I'm able to successfully mouse-collide with an image, but as soon as I turn the image into a sprite class, it becomes a nightmare...what's wrong with the sprite class? Or am I wasting time trying to use sprites for the nice collision features and just use images with rect collision instead?
Keep on getting the AttributeError: 'Sheldon' object has no attribute 'Rect' (s.Rect.collidepoint)
or AttributeError: 'tuple' object has no attribute 'collidepoint' (s.collidepoint)
or AttributeError: 'Rake' object has no attribute 'sprite' (s.sprite.collidepoint)
Since I'm new to python/pygame, should I be putting this detection in an Update/Render method in the sprite class itself, or am I using the wrong event polling???
I haven't bothered trying to recode the mousedown/up/dragging since I can't even get the mouse-over to work
Hopefully this time the post gets a working response...the others didn't ;(
Thanks for your help.
Code:
import pygame
from pygame import *
from pygame.locals import *
from pygame.sprite import *
class Sheldon(Sprite):
def __init__(self):
Sprite.__init__(self)
self.image = transform.scale(image.load('sheldon.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
class Rake(Sprite):
def __init__(self):
Sprite.__init__(self)
self.image = transform.scale(image.load('rake.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
class Sprite_Mouse_Location(Sprite):
def __init__(self,x,y):
Sprite.__init__(self)
self.rect = pygame.Rect(x,y,1,1)
print(self.rect)
pygame.init()
window = display.set_mode( (800,600) )
sheldon = Sheldon()
sheldon.rect = (10,10)
all_sprites = Group(sheldon)
rake = Rake()
rake.rect = (400,250)
all_sprites.add(rake)
x,y = pygame.mouse.get_pos()
mouse_sprite = Sprite_Mouse_Location(x,y)
running = True
while running == True:
for event in pygame.event.get():
if event.type == QUIT or event.type == KEYUP and event.key == K_ESCAPE :
pygame.quit()
elif event.type == MOUSEMOTION :
for s in all_sprites :
if pygame.sprite.collide_rect(s,mouse_sprite):
print("hit")
window.fill( (0,0,0) )
all_sprites.update()
all_sprites.draw(window)
display.update()
You don't need Sprite_Mouse_Location.
BTW: to set position you need
rake.rect.topleft = (400, 250)
# or
rake.rect.x = 400
rake.rect.y = 250
not
rake.rect = (400, 250)
because it replace pygame.Rect() with tuple
Example code:
I use Surface instead of image.load() so everyone can run it without images.
import pygame
# --- constants --- (UPPER_CASE names)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
# --- classes --- (CamelCase names)
class Sheldon(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((230, 310))
self.image.fill(RED)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def check_click(self, mouse):
if self.rect.collidepoint(mouse):
print("hit RED")
class Rake(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((230, 310))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def check_click(self, mouse):
if self.rect.collidepoint(mouse):
print("hit GREEN")
# --- main --- (lower_case names)
# - init -
pygame.init()
window = pygame.display.set_mode((800,600))
# - objects -
sheldon = Sheldon(10, 10)
#sheldon.rect.topleft = (10, 10)
rake = Rake(400, 250)
#rake.rect.topleft = (400, 250)
all_sprites = pygame.sprite.Group()
all_sprites.add(sheldon, rake)
# - mainloop -
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT or \
(event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
for s in all_sprites:
s.check_click(event.pos)
window.fill(BLACK)
all_sprites.update()
all_sprites.draw(window)
pygame.display.update()
# - end -
pygame.quit()
There are a lot of things that are going on with your code. Coding can be hard at first, but keep sticking to! You'll better as long as you keep trying and don't give up! Your error is occurring because above the loop, you are setting your various sprites' rect variables to tuples instead of rects. Rects are not tuples, they have a bunch of variables that tuples don't have and collide_rect needs some of those variables. I put a couple comments in the code below about your other questions.
import pygame
from pygame import *
from pygame.locals import *
from pygame.sprite import *
class Sheldon(Sprite):
def __init__(self):
Sprite.__init__(self)
self.image = transform.scale(image.load('sheldon.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
class Rake(Sprite):
def __init__(self):
Sprite.__init__(self)
self.image = transform.scale(image.load('rake.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
class Sprite_Mouse_Location(Sprite):
def __init__(self,x,y):
Sprite.__init__(self)
self.rect = pygame.Rect(x,y,1,1)
print(self.rect)
pygame.init()
window = display.set_mode( (800,600) )
sheldon = Sheldon()
sheldon.rect = (10,10) # (10,10) is not a rect!
# pygame.Rect(0,0,10,10) is a rect!
all_sprites = Group(sheldon)
rake = Rake()
rake.rect = (400,250) # Same as above comment.
all_sprites.add(rake)
x,y = pygame.mouse.get_pos() # If you are going to make the mouse
mouse_sprite = Sprite_Mouse_Location(x,y) # a sprite, then update your mouse position in the loop.
running = True
while running == True:
for event in pygame.event.get():
if event.type == QUIT or event.type == KEYUP and event.key == K_ESCAPE :
pygame.quit()
elif event.type == MOUSEMOTION :
# You could have this instead.
# mouse_sprite.rect.x, mouse_sprite.rect.y = pygame.mouse.get_pos()
# Then move the for loop below to be out of the event loop.
for s in all_sprites :
if pygame.sprite.collide_rect(s,mouse_sprite):
print("hit")
window.fill( (0,0,0) )
all_sprites.update()
all_sprites.draw(window)
display.update()
I'd also recommend going through a pygame tutorial as well as looking at other people's pygame code and trying to understand it line by line. Both those tips helped me out. Good luck.
thanks to alanxoc3 and a new day, code working
posting here because I'm sure another newbie is going to want to do this type of mouse click
# to create a group of sprites and use the mouse over them individually
import pygame
from pygame import *
from pygame.locals import *
from pygame.sprite import *
class Sheldon(Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self,self.groups) #have to have groups here, not documented well
self.image = transform.scale(image.load('sheldon.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
def clickCheck(self,smouse):
if pygame.sprite.collide_rect(smouse, self): #could not use sritecollison because it would flag whole group
print('hit sheldon') # this if the check mouse that its working
#------------------------------------------
class Rake(Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self,self.groups)
self.image = transform.scale(image.load('rake.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
def clickCheck(self,smouse):
if pygame.sprite.collide_rect( smouse, self ):
print('hit rake')
#-------------------------------------------
class Sprite_Mouse_Location(Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.rect = pygame.Rect( 0 , 0 , 1 , 1 ) # no updating needed here
#--------------------------------------------
pygame.init()
window = display.set_mode( (800,600) )
mouse_sprite = Sprite_Mouse_Location() # had to create a mouse as sprite to use the sprite collision feature
all_sprites = pygame.sprite.Group() # have to declare first, no well documented
Sheldon.groups = all_sprites # better than all_sprites = Group(sheldon), and can be assigned before instantiation of sheldon!!!
sheldon = Sheldon()
sheldon.rect = (10,10,230,310)
Rake.groups = all_sprites
rake = Rake()
rake.rect = (400,250,230,310)
running = True
while running == True:
for event in pygame.event.get():
if event.type == QUIT or event.type == KEYUP and event.key == K_ESCAPE :
pygame.quit()
elif event.type == MOUSEBUTTONDOWN:
if event.button == 1:
mouse_sprite.rect.x , mouse_sprite.rect.y = pygame.mouse.get_pos() # have to have this to update mouse here or get wrong location
for s in all_sprites: #can't have outside the event or it will continuously check
s.clickCheck(mouse_sprite)
window.fill( (0,0,0) )
all_sprites.update() # have to have this for group sprites
all_sprites.draw(window) # have to have this for sprites
display.flip()

Resources