pygame how to actualize rect of sprite - python

I'm learning pygame and i am encountering a problem :
When i'm trying to change the image of the sprite by changing the content of the variable self.image and then self.rect, it doesn't show/acutalize this new image. This is the code, hoping to make myself understood.
all_sprites_list = pygame.sprite.Group()
luffy_sprites_ls = pygame.sprite.Group()
class luffy(pygame.sprite.Sprite):
"""docstring pour le personnage"""
def __init__(self):
self.lsLuffy = []
self.lsLuffySauter = []
super().__init__()
self.imageAll = SpriteSheet("images/attaquesLuffy.png")
#loading some img to put them in a lsLuffySauter
self.image = self.imageAll.get_image(35, 74, 20, 95)
self.lsLuffySauter.append(self.image)
self.image2 = self.imageAll.get_image(200, 300, 300,300)
self.lsLuffySauter.append(self.image2)
self.rect = self.image.get_rect()
self.rect.x = 500
self.rect.y = 500
all_sprites_list.add(self)
self.positionX = 500
self.positionY = 500
def sauter(self):
""" Called when user hits 'jump' button. """
self.current_image = self.lsLuffySauter[0]
self.positionY -= 10
self.rect = self.current_image.get_rect()
self.rect.x = self.positionX
self.rect.y = self.positionY
luffy_sprites_ls.empty()
luffy_sprites_ls.add(self)
#all_sprites_list.update()
#Code to draw in the screen
screen.fill(WHITE)
all_sprites_list.draw((screen))
luffy_sprites_ls.draw((screen))
pygame.display.flip()
clock.tick(100)

When pygame.sprite.Group.draw() is called, as in luffy_sprites_ls.draw((screen)), every sprite in the group has it's sprite.image rendered to the screen at sprite.rect.
Your sauter() function is changing the luffy.rect, but it is not changing the luffy.image (it is changing luffy.current_image).
Probably you want something like:
def sauter(self):
""" Called when user hits 'jump' button. """
self.image = self.lsLuffySauter[0]
self.positionY -= 10
self.rect = self.image.get_rect()
self.rect.x = self.positionX
self.rect.y = self.positionY

Related

The Enemy keeps shooting after its killed

class NPC(pg.sprite.Sprite):
def __init__(self,x,y,image):
self.copyimg = pg.image.load(image).convert_alpha()
pg.sprite.Sprite.__init__(self)
self.image = self.copyimg.copy()
#self.copyimg.fill(RED)
self.rect = self.image.get_rect()
self.radius = int(self.rect.width / 3)
#pg.draw.circle(self.copyimg,RED,self.rect.center,self.radius)
self.rect.center = (x,y)
self.pos = vec(x,y)
self.vel = vec(0,0)
self.acc = vec(0,0)
self.speed = 0.3
self.friction = -0.02
self.rot = 0
self.chasex = True
self.chasey = True
self.directing = 1
self.times = pg.time.get_ticks()
self.mainmenu = True
def rotate(self):
self.rot = self.rot % 360
newimage = pg.transform.rotate(self.copyimg,int(self.rot%360))
#pg.draw.rect(screen,WHITE,self.rect,5)
#old_center = self.rect.center
self.image = newimage
self.rect = self.image.get_rect()
#self.rect.center = old_center
def shoot(self):
bullet = BulletPlayer(self.pos.x,self.pos.y,self.rot,lasers['laserblue'][random.randint(0,len(lasers['laserblue'])-1)])
bulletgroupenemy.add(bullet)
pass
class Bullet(pg.sprite.Sprite):
def __init__(self,x,y,rotation,image):
pg.sprite.Sprite.__init__(self)
self.cop = pg.image.load(image).convert_alpha()
self.image = self.cop.copy()
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.shootspeed = 10
self.rotation = rotation
def update(self):
self.rotation = self.rotation % 360
newimage = pg.transform.rotate(self.cop,int(self.rotation))
oldcenter = self.rect.center
self.image = newimage
self.rect = self.image.get_rect()
self.rect.center = oldcenter
keys = pg.key.get_pressed()
if self.rect.x < 0 or self.rect.x >WIDTH or self.rect.y > HEIGHT or self.rect.y < 0:
self.kill()
I have a NPC class and bullet class like that and ofcourse there is mainplayer that we can control. and as you can see there is shoot method with in the NPC class. this is calling automatically
by npc it self how ever when i shoot npc with a bullet and call spritecollide function
hitsenemy = pg.sprite.spritecollide(playerlist[i],bulletgroup,True)
if hitsenemy:
playerlist[i].kill()
the npc get kills thats correct but somehow it is keeping to shoot. the shoot function still works how this can be !. i just killed by using this hitsenemy . and also i use this for loop to add spride group. How can i prevent that i dont want it to keep shooting
playerlist = [Player(300,200,'playerShip2_blue.png')]
for players in playerlist:
allsprites.add(players)
playergroup.add(players)
i have also this allsprites group allsprites = pg.sprite.Group()
this method belongs the player class which i didnt share because but this is how i shoot with player class.
def shoot(self):
bullet = BulletPlayer(self.rect.centerx,self.rect.centery,self.rot,lasers['lasergreen'])
bulletgroup.add(bullet)
allsprites.add(bullet)
playerlist is a list but not a pygame.sprite.Group. pygame.sprite.Sprite.kill
remove the Sprite from all Groups
Therefor when you call kill, the Sprite is removed from all Groups but it is still in the playerlist.
You have to remove the Sprite from the list. See How to remove items from a list while iterating?
hitsenemy = pg.sprite.spritecollide(playerlist[i],bulletgroup,True)
if hitsenemy:
playerlist[i].kill()
playerlist.pop(i)
Alternatively, consider using a group instead of a list. Note the Sprites in a Group can be iterated. And the list of Sprites in the Group can be obtained via pygame.sprite.Group.sprites().

Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?

My collide_rect function isn't working properly. It always returns True, when it's not suppose to. I have tried looking on the internet but nothing is working for me. I think the collide rect somehow did not use the actual coordinates for the two sprites. Can anyone help with this?
import pygame
import pygame.sprite
import sys
gameDisplay = pygame.display.set_mode((800,600))
pygame.display.set_caption("test_collision")
clock = pygame.time.Clock()
crashed = False
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect()
self.x = 280
self.y = 475
self.col = False
def update(self):
gameDisplay.blit(self.image, (self.x,self.y))
self.rect = self.image.get_rect()
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.x = 1000
self.y = 483
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect()
def change_x(self):
self.time = pygame.time.get_ticks()
self.x = -(self.time/5) + 800
def update(self):
self.rect = self.image.get_rect()
gameDisplay.blit(self.image,(self.x,self.y))
obstacle = Obstacle()
ball = Ball()
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
gameDisplay.fill((255,255,255))
ball.update()
obstacle.change_x()
obstacle.update()
ball.test_collisions(obstacle)
if ball.col:
print("colided")
pygame.display.flip()
clock.tick(1000)
pygame.quit()
sys.exit()
P.S This is my first post :)
pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, but it returns a rectangle that always starts at (0, 0) since a Surface object has no position.
The Surface is placed at a position on the display with the blit function.
You've to set the location of the rectangle, either by a keyword argument, e.g:
self.rect = self.image.get_rect(topleft = (self.x, self.y))
or an assignment to a virtual attribute (see pygame.Rect), e.g:
self.rect = self.image.get_rect()
self.rect.topleft = (self.x, self.y)
It is absolutely unnecessary to add some extra attributes self.x and self.y. Use the location of the rectangle instead. e.g:
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect(topleft = (280, 475))
self.col = False
def update(self):
gameDisplay.blit(self.image, self.rect)
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect(topleft = (1000, 483))
def change_x(self):
self.time = pygame.time.get_ticks()
self.rect.x = -(self.time/5) + 800
def update(self):
gameDisplay.blit(self.image, self.rect)
Further note, that you can get rid of the methods Ball.update() respectively Obstacle.update() (you can delete them), if you use a pygame.sprite.Group and call .draw(), which uses the .image and .rect properties of the contained sprites, to draw them. e.g.:
obstacle = Obstacle()
ball = Ball()
all_sprites = pygame.sprite.Group([obstacle, ball])
while not crashed:
# [...]
gameDisplay.fill((255,255,255))
all_sprites.draw(gameDisplay)
pygame.display.flip()
clock.tick(1000)

Trouble creating multiple instances in python/pygame

I've been creating a smup game. The problem is that I have multiple instances of enemies within the game which are supposed to fall from the top of the screen.All of my instances except for one hang at the top of the screen. For some bizarre reason it appears that only one instance of my enemy objects seem to move. I've spent hours trying to fix it to absolutely no avail. I've also browsed a plethora of tutorials on how to create classes, and I can't find anything really wrong with my code. Please help.
import pygame,random,os
from pygame.locals import *
'initialize pygame'
pygame.init()
'set variables'
red = (255,0,0)
green = (0,255,0)
blue = (0,0,255)
black = (0,0,0)
white = (255,255,255)
width = 1280
height = 720
'create window'
screen = pygame.display.set_mode((1280,720))
clock = pygame.time.Clock()
'sprite groups'
all_sprites = pygame.sprite.Group()
enemies = pygame.sprite.Group()
'classes'
class Player(pygame.sprite.Sprite):
def __init__(self):
self.x, self.y = pygame.mouse.get_pos()
pygame.sprite.Sprite.__init__(self)
#self.image = pygame.Surface((32,32))++--3
#$self.image.fill((green))
self.image = pygame.image.load("vehicle.png")
self.image.set_colorkey(white)
self.rect = self.image.get_rect()
self.rect.center = (width/2,700)
self.speed = 0
def move(self):
self.keypress = pygame.key.get_pressed()
if self.keypress[pygame.K_a]:
self.speed = 3
self.rect.x -= self.speed
if self.keypress[pygame.K_d]:
self.speed = 3
self.rect.x += self.speed
#self.rect.x += 1
if self.rect.left > width:
self.rect.right = 0
if self.rect.right < 0:
self.rect.left = 1280
class Enemy(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("missile.png")
self.rect = self.image.get_rect()
self.rect.x = random.randrange(50,width)
self.rect.y = random.randrange(-100,-40)
self.speedy = random.randrange(1,5)
def enmove(self):
self.rect.y = self.rect.y + self.speedy
if self.rect.top > height:
self.rect.x = random.randrange(50,width)
self.rect.y = random.randrange(-100,-40)
self.speedy = random.randrange(1,5)
class bullet(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((32,32))
self.rect = self.image.get_rect()
def bmove(self):
pass
player = Player()
for r in range(9):
enemy = Enemy()
enemies.add(enemy)
while True:
pygame.event.pump()
'main loop'
player.move()
enemy.enmove()
all_sprites.add(player)
screen.fill(black)
all_sprites.draw(screen)
enemies.draw(screen)
hits = pygame.sprite.spritecollide(player,enemies,False)
if hits == True:
player.all_sprites.remove(player)
print('true')
all_sprites.update()
pygame.display.update()
print(hits)
Your problem seem to be that you move only last enemy:
enemy.enmove()
You should try iterate your enemies group and move every enemy seperately

PyGame development bug?

I am developing a Space Invaders clone using Python 3.5.1 and have stumbled upon an error which I am not sure how to fix. I'm trying to keep a sprite inside the window and my code only works to keep the sprite from leaving the top and left sides of it. Here is my code. Thanks.
import pygame
import sys
width = 500
height = 700
white = (255, 255, 255)
black = (0, 0, 0)
score = 0
screen = pygame.display.set_mode([width, height])
screen_rect = screen.get_rect()
class Ship(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("player.png").convert()
self.rect = self.image.get_rect()
def update(self):
pos_x, pos_y = pygame.mouse.get_pos()
player_rect = self.image.get_rect()
self.rect.x = pos_x
self.rect.y = pos_y
player_rect.clamp_ip(screen_rect)
class Enemy(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("enemy.png").convert()
self.rect = self.image.get_rect()
class Bullet(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("laser.png").convert()
self.rect = self.image.get_rect()
player = Ship()
allSprites = pygame.sprite.Group()
allSprites.add(player)
running = True
while running == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
screen.fill(black)
player.update()
allSprites.draw(screen)
pygame.display.flip()
pygame.quit()
The object already has a Rect object in self.rect; you aren't actually calling the clamp_ip method on the right Rect.
It may be enough to call self.rect.clamp_ip(screen_rect) instead of even bothering with getting the image Rect at all. The builtin draw method of the Sprite Groupshould just draw the image in the sprite's Rect if I recall correctly, so just make sure you're only updating the sprite's Rect correctly.
def update(self):
self.rect.topleft = pygame.mouse.get_pos()
if not screen_rect.contains(self.rect):
self.rect.clamp_ip(screen_rect)
Change you def update(self): with the following:
def update(self):
pos_tup = pygame.mouse.get_pos() # edit fix
pos_x = pos_tup[0] # edit fix
pos_y = pos_tup[1] # edit fix
player_rect = self.image.get_rect()
if pos_x < 0:
self.rect.x = 0
elif pos_x > width:
self.rect.x = width
else:
self.rect.x = pos_x
if pos_y < 0:
self.rect.y = 0
elif pos_y > height:
self.rect.y = height
else:
self.rect.y = pos_y
player_rect.clamp_ip(screen_rect)

adding sprites to a group

I am trying to display a group of Sprite to the screen, however, my code below displays a blank screen. I've been at this for a while and i dont know why this doesnt work at the logic seems correct. the main problem occurs in the sprite method where i am trying to add the sprites at a random position. The sprites are then drawn in the gameLoop method.
Any suggestions?
import pygame, sys, random
pygame.init()
troll = pygame.image.load("image.png")
black = (0, 0, 0)
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 400
sprite_width = 5
sprite_height = 5
spriteCount = 5
class Sprite(pygame.sprite.Sprite):
pygame.init()
sprite = pygame.sprite.Group()
clock = pygame.time.Clock()
def __init__(self):
pygame.sprite.Sprite.__init__(self)
def __init__(self, Image, pos):
pygame.sprite.Sprite.__init__(self)
self.image =pygame.Surface([0, 0])
self.rect = self.image.get_rect()
self.rect.topleft = pos
#classmethod
def sprite(self):
for i in range(spriteCount):
tmp_x = random.randrange(0, SCREEN_WIDTH)
tmp_y = random.randrange(0, SCREEN_HEIGHT)
# all you have to do is add new sprites to the sprite group
self.sprite.add(Sprite(troll, [tmp_x, tmp_y]))
def update(self):
self.rect.x += 1
self.rect.y += 2
if self.rect.y > SCREEN_HEIGHT:
self.rect.y = -1 * sprite_height
if self.rect.x > SCREEN_WIDTH:
self.rect.x = -1 * sprite_width
#classmethod
def setImage(self,Image):
self.image=pygame.image.load(Image)
#classmethod
def gameLoop(self):
screen = pygame.display.set_mode([640, 400])
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(black)
# to update or blitting just call the groups update or draw
# notice there is no for loop
# this will automatically call the individual sprites update method
self.actor.update()
self.actor.draw(screen)
pygame.display.update()
self.clock.tick(20)
Sprite.sprite()
Sprite.setImage("image.png")
Sprite.gameLoop()
#classmethod
def setImage(self,Image):
self.image=pygame.image.load(Image)
Here, you should name the first parameter cls, not self, since it's a class method, not an instance method. Also, parameter-names should be lowercase (image instead of Image).
#classmethod
def gameLoop(self):
screen = pygame.display.set_mode([640, 400])
while True:
...
Same here. Also, I don't know why your gameloop is part of your Sprite class. Also, you should not name your class Sprite in the first place, as it will just create confusion with pygame's Sprite class.
class Sprite(pygame.sprite.Sprite):
...
sprite = pygame.sprite.Group()
...
#classmethod
def sprite(self):
...
Here you have a field called sprite and a class level function sprite. This will not work, as the method will overwrite the field. So whenever you want to access the field sprite, you will access the function sprite instead.
#classmethod
def gameLoop(self):
...
self.actor.update()
Sprite.actor does not exit. You never created such a field.
def __init__(self, Image, pos):
pygame.sprite.Sprite.__init__(self)
self.image =pygame.Surface([0, 0])
self.rect = self.image.get_rect()
self.rect.topleft = pos
Here, you pass an argument called Image. However, you don't use it but an empty Surface .
Here's a running version of your code:
import pygame, sys, random
pygame.init()
troll = pygame.image.load("image.png")
black = pygame.color.Color('black')
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 400
spriteCount = 5
class Sprite(pygame.sprite.Sprite):
actors = pygame.sprite.Group()
clock = pygame.time.Clock()
def __init__(self):
pygame.sprite.Sprite.__init__(self)
def __init__(self, image, pos):
pygame.sprite.Sprite.__init__(self)
self.image = image
self.rect = self.image.get_rect()
self.rect.topleft = pos
#classmethod
def init(self):
for i in range(spriteCount):
tmp_x = random.randrange(0, SCREEN_WIDTH)
tmp_y = random.randrange(0, SCREEN_HEIGHT)
self.actors.add(Sprite(troll, [tmp_x, tmp_y]))
def update(self):
self.rect.x += 1
self.rect.y += 2
if self.rect.y > SCREEN_HEIGHT:
self.rect.y = -1 * self.rect.height
if self.rect.x > SCREEN_WIDTH:
self.rect.x = -1 * self.rect.width
#classmethod
def gameLoop(self):
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(black)
self.actors.update()
self.actors.draw(screen)
pygame.display.update()
self.clock.tick(20)
Sprite.init()
Sprite.gameLoop()

Categories