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().
Related
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
I am trying to build a simple version of Flappy Bird. To detect collisions between my circle(Flappy Bird) and my rectangles(Pipes) I was using pygame.sprite.collide_rect() but I wanted a better way of handling collisions.
But using mask collision causes no detection of the collision. The circle passes directly through the rectangle as if it isn't there.
Here is my code:
bird_group = pygame.sprite.Group()
pipe_group = pygame.sprite.Group()
class Bird(pygame.sprite.Sprite):
def __init__(self, x_loc, y_loc, velocity):
super(Bird, self).__init__()
self.velocity = velocity
self.x_loc = x_loc
self.y_loc = y_loc
self.image = pygame.image.load(os.path.join(game_folder,"index2.png")).convert()
self.image.set_colorkey(WHITE)
self.image = pygame.transform.scale(self.image,(60,65))
self.rect = self.image.get_rect()
self.rect.center = (x_loc,y_loc)
def update(self):
self.rect.y += self.velocity
self.velocity = self.velocity+1
self.mask = pygame.mask.from_surface(self.image)
def jump(self):
self.velocity = -10
def boundary_collison(self):
if self.rect.bottom+100>=display_height or self.rect.top<=0:
return True
class UpperPipe(pygame.sprite.Sprite):
"""docstring for UpperPipe"""
def __init__(self, pipe_x, pipe_height, pipe_speed):
super(UpperPipe, self).__init__()
self.pipe_speed = pipe_speed
self.image = pygame.Surface((pipe_width, pipe_height))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.x = (pipe_x)
self.rect.y = (0)
def update(self):
self.rect.x -= self.pipe_speed
self.mask = pygame.mask.from_surface(self.image)
def x_cord(self):
return self.rect.x
class LowerPipe(pygame.sprite.Sprite):
"""docstring for UpperPipe"""
def __init__(self, pipe_x, pipe_height, pipe_speed):
super(LowerPipe, self).__init__()
self.pipe_speed = pipe_speed
self.image = pygame.Surface((pipe_width, display_height-(pipe_gap+pipe_height)))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.x = (pipe_x)
self.rect.y = (pipe_height+pipe_gap)
def update(self):
self.rect.x -= self.pipe_speed
self.mask = pygame.mask.from_surface(self.image)
def x_cord(self):
return self.rect.x
The following code I use to make the sprites:
bird = Bird(x_loc,y_loc,velocity)
bird_group.add(bird)
pipe_list = []
init_pipe_x = 500
for make in range(pipe_count):
pipe_x = init_pipe_x+((between_pipe+pipe_width)*make)
pipe_height = (round(random.uniform(0.2,0.8), 2))*(display_height-pipe_gap)
upper = UpperPipe(pipe_x,pipe_height,pipe_speed)
lower = LowerPipe(pipe_x,pipe_height,pipe_speed)
add_pipe = [upper,lower]
pipe_list.append(add_pipe)
pipe_group.add(upper)
pipe_group.add(lower)
For detection inside my game loop I use the following code:
bird_hits = pygame.sprite.spritecollide(bird,pipe_group,False,pygame.sprite.collide_mask)
if bird_hits:
gameExit = True
The pygame.Surfaces that you pass to mask.from_surface must have an alpha channel. That means you either have to call the convert_alpha or the set_colorkey method of the surfaces.
class UpperPipe(pygame.sprite.Sprite):
def __init__(self, pipe_x, pipe_height, pipe_speed):
super(LowerPipe, self).__init__()
self.pipe_speed = pipe_speed
# Either call `convert_alpha` ...
self.image = pygame.Surface((pipe_width, display_height-(pipe_gap+pipe_height))).convert_alpha()
self.image.fill(GREEN)
# ... or call `set_colorkey`.
# I think surfaces converted with convert_alpha are blitted faster.
# self.image.set_colorkey((0, 0, 0))
# You also don't have to create the mask repeatedly in the
# update method. Just call it once in the __init__ method.
self.mask = pygame.mask.from_surface(self.image)
You haven't defined any collide_mask in your class : something like
self.mask = pygame.mask.from_surface(image)
So if the rect of your Upper and lower pipe corresponds to the hitbix of these: simply use
bird_hits = pygame.sprite.spritecollide(bird,pipe_group,False)
or create a self.mask in your class to use
bird_hits = pygame.sprite.spritecollide(bird,pipe_group,False,pygame.sprite.collide_mask)
I am new to pygame and I am trying to make a game where the player has to bypass some enemy's to get to a point where you can go to the next level. I need the enemy's to walk back and forward on a predetermined path but I can't figure out how to do it. So I was wondering if there is an easy way to do this?
This is my code.
import pygame
import random
import os
import time
from random import choices
from random import randint
pygame.init()
a = 0
b = 0
width = 1280
height = 720
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Game")
done = False
n = 0
x = 0
y = 0
x_wall = 0
y_wall = 0
clock = pygame.time.Clock()
WHITE = (255,255,255)
RED = (255,0,0)
change_x = 0
change_y = 0
HW = width / 2
HH = height / 2
background = pygame.image.load('mountains.png')
#player class
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("character.png")
self.rect = self.image.get_rect()
self.rect.x = width / 2
self.rect.y = height / 2
#enemy class
class Enemy(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("enemy.png")
self.image = pygame.transform.scale(self.image, (int(50), int(50)))
self.rect = self.image.get_rect()
self.rect.x = width / 3
self.rect.y = height / 3
#wall class
class Wall(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("wall.png")
self.image = pygame.transform.scale(self.image, (int(50), int(50)))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
#wall movement
def update(self):
self.vx = 0
self.vy = 0
key = pygame.key.get_pressed()
if key[pygame.K_LEFT]:
self.vx = 5
self.vy = 0
elif key[pygame.K_RIGHT]:
self.vx = -5
self.vy = 0
if key[pygame.K_UP]:
self.vy = 5
self.vx = 0
elif key[pygame.K_DOWN]:
self.vy = -5
self.vx = 0
self.rect.x = self.rect.x + self.vx
self.rect.y = self.rect.y + self.vy
#player sprite group
sprites = pygame.sprite.Group()
player = Player()
sprites.add(player)
#enemy sprite group
enemys = pygame.sprite.Group()
enemy = Enemy()
enemy2 = Enemy()
enemys.add(enemy, enemy2)
#all the wall sprites
wall_list = pygame.sprite.Group()
wall = Wall(x_wall, y_wall)
wall2 = Wall((x_wall + 50), y_wall)
wall3 = Wall((x_wall + 100), y_wall)
wall4 = Wall((x_wall + 150), y_wall)
wall5 = Wall((x_wall + 200), y_wall)
wall6 = Wall((x_wall + 250), y_wall)
#add all the walls to the list to draw them later
wall_list.add(wall, wall2, wall3, wall4, wall5, wall6)
#add all the walls here to fix the collision
all_walls = (wall, wall2, wall3, wall4, wall5, wall6)
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
sprites.update()
wall_list.update()
enemys.update()
#collision between player and and walls
if player.rect.collidelist(all_walls) >= 0:
print("Collision !!")
player.rect.x = player.rect.x - player.vx
player.rect.y = player.rect.y - player.vx
#fill the screen
screen.fill((0, 0, 0))
#screen.blit(background,(x,y))
#draw the sprites
sprites.draw(screen)
wall_list.draw(screen)
enemys.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
Here is the download link with the images if you want to run it:
https://geordyd.stackstorage.com/s/hZZ1RWcjal6ecZM
I'd give the sprite a list of points (self.waypoints) and assign the first one to a self.target attribute.
In the update method I subtract the self.pos from the self.target position to get a vector (heading) that points to the target and has a length equal to the distance. Scale this vector to the desired speed and use it as the velocity (which gets added to the self.pos vector each frame) and the entity will move towards the target.
When the target is reached, I just increment the waypoint index and assign the next waypoint in the list to self.target. It's a good idea to slow down when you're getting near the target, otherwise the sprite could get stuck and moves back and forth if it can't reach the target point exactly. Therefore I also check if the sprite is closer than the self.target_radius and decrease the velocity to a fraction of the maximum speed.
import pygame as pg
from pygame.math import Vector2
class Entity(pg.sprite.Sprite):
def __init__(self, pos, waypoints):
super().__init__()
self.image = pg.Surface((30, 50))
self.image.fill(pg.Color('dodgerblue1'))
self.rect = self.image.get_rect(center=pos)
self.vel = Vector2(0, 0)
self.max_speed = 3
self.pos = Vector2(pos)
self.waypoints = waypoints
self.waypoint_index = 0
self.target = self.waypoints[self.waypoint_index]
self.target_radius = 50
def update(self):
# A vector pointing from self to the target.
heading = self.target - self.pos
distance = heading.length() # Distance to the target.
heading.normalize_ip()
if distance <= 2: # We're closer than 2 pixels.
# Increment the waypoint index to swtich the target.
# The modulo sets the index back to 0 if it's equal to the length.
self.waypoint_index = (self.waypoint_index + 1) % len(self.waypoints)
self.target = self.waypoints[self.waypoint_index]
if distance <= self.target_radius:
# If we're approaching the target, we slow down.
self.vel = heading * (distance / self.target_radius * self.max_speed)
else: # Otherwise move with max_speed.
self.vel = heading * self.max_speed
self.pos += self.vel
self.rect.center = self.pos
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
waypoints = [(200, 100), (500, 400), (100, 300)]
all_sprites = pg.sprite.Group(Entity((100, 300), waypoints))
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
for point in waypoints:
pg.draw.rect(screen, (90, 200, 40), (point, (4, 4)))
pg.display.flip()
clock.tick(60)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
Instead of the waypoints list and index I'd actually prefer to use itertools.cycle and just call next to switch to the next point:
# In the `__init__` method.
self.waypoints = itertools.cycle(waypoints)
self.target = next(self.waypoints)
# In the `update` method.
if distance <= 2:
self.target = next(self.waypoints)
Use a list to have him walk back and forth.
class Enemy(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("enemy.png")
self.image = pygame.transform.scale(self.image, (int(50), int(50)))
self.rect = self.image.get_rect()
self.rect.x = width / 3
self.rect.y = height / 3
#x or y coordinates
self.list=[1,2,3,4,5]
self.index=0
def update(self):
# patrol up and down or left and right depending on x or y
if self.index==4:
#reverse order of list
self.list.reverse()
self.index=0
#set the x position of the enemy according to the list
self.rect.x=self.list[self.index]
self.index+=1
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
How to make the red block appear between grey and green block.
In the above picture i changed my rectangle x position of donkey to 160 then only i can able to see the red block if not means i'll hide under grey block.
this is my code
class Road(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((200, 500))
self.image.fill(grey)
self.rect = self.image.get_rect()
self.rect.centerx = width/2
#self.rect.left = width-200
#print self.rect.left
self.speedy = 5
def update(self):
self.rect.y += self.speedy
if self.rect.y > 0:
self.rect.y = 0
class Donkey(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((100, 100))
self.image.fill(red)
self.rect = self.image.get_rect()
self.rect.x = 200
print 'x:',self.rect.x
self.rect.y = -54
print 'y:',self.rect.y
self.speedy = random.randint(8,10)
def update(self):
self.choice = (200, 300)
self.rect.y += self.speedy
if self.rect.top > height:
self.rect.x = random.choice(self.choice)
print 'x:', self.rect.x
self.rect.y = random.randrange(-100, -40)
print 'y', self.rect.y
self.speedy = random.randint(8,10)
class Race_car(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((100, 100))
self.image.fill(green)
self.rect = self.image.get_rect()
self.rect.x = 200
self.rect.bottom = height
self.speedy = -0.1
def update(self):
#self.speedx = 0
self.rect.y += self.speedy
keypressed = pygame.key.get_pressed()
if keypressed[pygame.K_RIGHT]:
self.rect.x = 300
elif keypressed[pygame.K_LEFT]:
self.rect.x = 200
#Group, object for game_hearth
game_hearth = pygame.sprite.Group()
road = Road()
game_hearth.add(road)
#Group, object for donkey
donkey = Donkey()
donkeys = pygame.sprite.Group()
game_hearth.add(donkey)
donkeys.add(donkey)
#Group, object for racecar
racecar = Race_car()
racecars = pygame.sprite.Group()
game_hearth.add(racecar)
racecars.add(racecar)
when i run my code after commenting the Race_car class the red blocks working fine, see
so the thing is i want to display the both red and green block above the grey road. so how to do it without commenting any class in my program. i know its some co-ordinate mistake but i don't know where to change it.
From the Pygame docs:
The Group does not keep sprites in any order, so the draw order is
arbitrary.
You should probably switch to pygame.sprite.OrderedUpdates:
This class derives from pygame.sprite.RenderUpdates(). It maintains
the order in which the Sprites were added to the Group for rendering.
This makes adding and removing Sprites from the Group a little slower
than regular Groups.
#Group, object for game_hearth
game_hearth = pygame.sprite.OrderedUpdates()
road = Road()
game_hearth.add(road)
#Group, object for donkey
donkey = Donkey()
donkeys = pygame.sprite.OrderedUpdates()
game_hearth.add(donkey)
donkeys.add(donkey)
#Group, object for racecar
racecar = Race_car()
racecars = pygame.sprite.OrderedUpdates()
game_hearth.add(racecar)
racecars.add(racecar)