I have an image of a ufo and a missile. I'm trying to get it to where if the missile hits the ufo they both would explode and disappear and then a few moments later another ufo would respawn but the collision code isn't working. can someone explain to me how to make the code work?
pygame.display.init()
pygame.font.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
ufo = pygame.image.load("ufo.png")
rocket = pygame.image.load("rocket.png")
done = False
debug = False
fontObj = pygame.font.SysFont("Courier New", 20)
#Making my Empty Lists
missiles = [] #[x,y]
ufo_list = [] #[x,y,hspeed]
particle_list = []
#UFO Respawn Info
ufoRespawn = True
ufoHits = 0
ufoSpawnTimer = 0.0
ufoSpeed = 500.0
#MISSILE Info
launchX = 400
launchY = 550
missileSpeed = 100.0
missileDirection = 0
#creating the Starfield
myStars = [] # An (initially) empty list
for i in range(1000):
x = random.randint(0, 800)
y = random.randint(0, 600)
newStar = [x, y] # A 2-element list
myStars.append(newStar)
starSpeed = 100.0 # Rate of star movement (px / s)
starDirection = 0 # 0 = not moving, -1 = left, +1 = right
#input
while not done:
event = pygame.event.poll()
if event.type == pygame.QUIT:
done = True
keys = pygame.key.get_pressed()
if keys[pygame.K_ESCAPE]:
done = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_d:
debug = not debug
dt = clock.tick() /1000.0
#the missile range (making it disappear after it hits the top)
for missile in missiles:
missile[1] -= missileSpeed * dt
if missile[1] < 0: missiles.remove(missle)
#triangle following mouse position
mx, my = pygame.mouse.get_pos()
if mx > launchX:
launchX += .3
if mx < launchX:
launchX -= .3
#bullets firing when pressing with mouse
mbuttons = pygame.mouse.get_pressed()
if mbuttons [0]:
x = launchX
y = launchY
newMissiles = [x,y]
missiles.append(newMissiles)
#Creating the UFOs
ufoSpawnTimer -= dt
if ufoSpawnTimer <= 0:
if random.choice (("head", "tail")) == "head":
x = 0
hspeed = random.randint (10,50)
else:
x = 800
hspeed = random.randint (-50, -10)
y = random.randint (0,300)
new_ufo = [x,y,hspeed]
ufo_list.append(new_ufo)
ufoSpawnTimer = 5.0
#Moving the Starfield
for i in range(len(myStars)):
myStars[i][0] += starSpeed * dt * starDirection
if myStars[i][0] < 0: myStars[i][0] = 800
if myStars[i][0] > 800: myStars[i][0] = 0
screen.fill ((0,0,0))
#drawing the triangle a.k.a missle launcher :D
pygame.draw.polygon(screen, (255,255,255), [[launchX, launchY], [launchX + 10, launchY + 10], \
[launchX - 10, launchY + 10]], 3)
for missile in missiles:
x = int(missile[0])
y = int(missile[1])
screen.blit(rocket, (x,y))
#drawing the ufo
for v in ufo_list:
v[0] += v[2] * dt
screen.blit(ufo,(v[0],v[1]))
#Missle distance from UFO - NEED HELP ON THIS PORTION
#Hit Detection
missileDist = ((x - v[0]) ** 2 + (y - v[1]) ** 2) ** 0.5
if **????** :
ufoRespawn = True
ufoHits += 10
#drawing th starfield
for star in myStars:
x = int(star[0])
y = int(star[1])
pygame.draw.circle(screen, (255,255,255), (x,y), 2)
pygame.display.flip()
pygame.font.quit()
pygame.display.quit()
Well there are many different ways to detect collision, And it might be worth looking at libraries that would do so, but the simplest method by far is to use pygame.sprite.spritecollide().
But before I can show how to use the function, you need to know what a pygame.sprite.Group() is and what a sprite class is.
Basicly, what a pygame.sprite.Group() is, is a way to keep track of and hold multiple sprites. In your case, it seems making a missile group for your missiles would be the best choice.
So I would create a group to hold your missiles:
missiles_group = pygame.sprite.Group(). You can add missiles to the group by saying missiles_group.add(<sprite instance name>).
As for the sprite class, please see this answer I gave to a question. To be terse, a Sprite class is a modular way to create a sprite. Instead of using just a plain image, a sprite class would hold necessary methods and attributes of a sprite. I will be using a sprite class in my example below, so if more detail is needed, please read the answer I linked to above.
With that out of the way, and without going into too much detail, here is how you fill in each function parameter to the above function.
sprite: This is the sprite that will be tested against a group of sprites
group: This is the group that will be used to test with the sprite.
dokill: This is a boolean value. If set to true, each time the sprite parameter collides with something in the group parameter, and object from the group parameter will be deleted. And visa versa if the dokill argument is set to false.
The is one more parameter that the function takes, but for what you're trying to do, it is not needed.
Incorporating the above information, here is an example. The example creates a sprite and a list of sprites. Each time the sprite collides with a sprite from the group, HIT is printed to the screen:
import pygame #import the pygame module into the namespace <module>
WIDTH = 640 # define a constant width for our window
HEIGHT = 480 # define a constant height for our window
#create a pygame window, and
#initialize it with our WIDTH and HEIGHT constants
display = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock() # create a game clock
class Sprite(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((20, 20))
self.image.fill((255, 0, 0))
self.rect = self.image.get_rect()
self.rect.x = WIDTH / 2
self.rect.y = HEIGHT / 2
self.vx = 0
self.vy = 0
def update(self):
self.vx = 0
self.vy = 0
key = pygame.key.get_pressed()
if key[pygame.K_LEFT]:
self.vx = -1
elif key[pygame.K_RIGHT]:
self.vx = 1
if key[pygame.K_UP]:
self.vy = -1
elif key[pygame.K_DOWN]:
self.vy = 1
self.rect.x += self.vx
self.rect.y += self.vy
# cretae a player sprite
player = Sprite()
# create a group to hold all of our sprites
sprites = pygame.sprite.Group()
# create a group to hold sprites we want to
# test collions against. These sprites will
# still be added to the sprites list
# but we need a seperate group to test for
# collisions against
collision_sprites = pygame.sprite.Group()
# add a sprite to out collison sprite group
# We also add the sprite to our sprites group
# that holds all sprites
tmp = Sprite()
tmp.update = lambda: None
sprites.add(tmp)
collision_sprites.add(tmp)
# add a player sprites to the player group
player.rect.x = 10
sprites.add(player)
running = True # our variable for controlling our game loop
while running:
for e in pygame.event.get(): # iterate ofver all the events pygame is tracking
clock.tick(60) # make our clock keep pour game at 60 FPS
if e.type == pygame.QUIT: # is the user trying to close the window?
running = False # if so break the loop
pygame.quit() # quit the pygame module
quit() # quit is for IDLE friendliness
sprites.update()
# here is where we test for collision
if pygame.sprite.spritecollide(player, collision_sprites, False):
print("HIT!")
display.fill((180, 180, 180)) # fill the pygame screen with white
sprites.draw(display)
pygame.display.flip() # update the screen
My example if fairly big, so take your time and step through it carefully. I tried to add as many good comments as I could. Good luck!
Related
I want to design a snake game with smoother movement than others, using the pygame library. My issue does not concern the movement part - I have been able to move the snake, but this came at the cost of 2 FPS and changing the speed to TILE_SIZE, creating an effect of lag/choppiness which I don't like. Is there a solution, such that I can move the snake more often in smaller intervals to create smoother movement? I know about LERP / interpolation but I am not too sure how to use it / if this is the best solution. Thanks
import pygame as pg
import random
# Initialising
pg.init()
# Creating screen
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 800
screen = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
# Creating clock
clock = pg.time.Clock()
fps = 60
# Creating game variables
TILE_SIZE = 40
right = left = down = up = False
# Colours
black = (0, 0, 0)
class Snake(pg.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.body = [[x, y]]
self.length = len(self.body)
self.direction = pg.math.Vector2()
self.speed = TILE_SIZE // 5
def update(self):
# Movement
self.direction.x = 1 if right else -1 if left else 0
self.direction.y = 1 if down else -1 if up else 0
self.body[0][0] += self.speed * self.direction.x
self.body[0][1] += self.speed * self.direction.y
def draw(self):
pg.draw.rect(screen, "green", (self.body[0][0], self.body[0][1], TILE_SIZE, TILE_SIZE))
# Creating snake at a random tile starting point
snake = Snake(random.randrange(0, SCREEN_WIDTH, TILE_SIZE), (random.randrange(0, SCREEN_WIDTH, TILE_SIZE)))
running = True
while running:
# Set frame rate
clock.tick(fps)
# Fill screen
screen.fill("beige")
# Draw grid
for i in range(0, SCREEN_WIDTH, TILE_SIZE):
pg.draw.line(screen, black, (i, 0), (i, SCREEN_HEIGHT))
for i in range(0, SCREEN_HEIGHT, TILE_SIZE):
pg.draw.line(screen, black, (0, i), (SCREEN_WIDTH, i))
# Draw snake
snake.update()
snake.draw()
for e in pg.event.get():
if e.type == pg.QUIT:
running = False
keys = pg.key.get_pressed()
if (keys[pg.K_RIGHT] or keys[pg.K_d]) and not left:
right = True
left = down = up = False
if (keys[pg.K_LEFT] or keys[pg.K_a]) and not right:
left = True
right = down = up = False
if (keys[pg.K_DOWN] or keys[pg.K_s]) and not up:
down = True
right = left = up = False
if (keys[pg.K_UP] or keys[pg.K_w]) and not down:
up = True
right = left = down = False
pg.display.update()
Solved - I decided to only change the snake's direction when the snake is inside the grid pattern (using MOD operator %) -
if self.rect.topleft[0] % TILE_SIZE == 0 and self.rect.topleft[1] % TILE_SIZE == 0:
# change direction
I'm making a simple game in pygame, in which you're supposed to dodge or shoot the targets that descend through the screen. So far, I've created the ship and managed to set the movement of both the bullet and the ship, as for their key bindings. However, the bullet's x coordinate doesn't seem to change at all, even though I've defined in the init method in the bullet class that the bullet x coordinate is equal to the ship x coordinate, therefore, the bullet would always be leaving the ship's position. Instead, the bullet always leaves the middle of the screen. I can't find the issue. Any help would be very much appreciated
import pygame, sys
pygame.init()
clock = pygame.time.Clock()
screen_width = 600
screen_height = 800
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Space Race Game")
class ROCKET:
def __init__(self):
self.rocketImg = pygame.image.load("spaceship.png")
self.rocket_x = screen_width/2 - 32
self.rocket_y = screen_height/2 + 100
def draw_rocket(self):
screen.blit(self.rocketImg, (self.rocket_x, self.rocket_y))
def move_rocket(self):
key = pygame.key.get_pressed()
if key[pygame.K_LEFT] and self.rocket_x + 15 > 0:
self.rocket_x -= 5
if key[pygame.K_RIGHT] and self.rocket_x < screen_width - 40:
self.rocket_x += 5
class BULLET(ROCKET):
def __init__(self):
super().__init__()
self.bullet_width = 10
self.bullet_height = 20
self.bullet_x = self.rocket_x + 25
self.bullet_y = self.rocket_y
self.move = [0, 0]
self.bullet_speed = 5
self.bullet_rect = pygame.Rect(self.bullet_x, self.bullet_y, self.bullet_width, self.bullet_height)
def draw_bullet(self):
key = pygame.key.get_pressed()
if key[pygame.K_SPACE]:
self.bullet_x = self.rocket_x
self.move[1] = -1
self.bullet_y += self.move[1] * self.bullet_speed
self.bullet_rect.topleft = (self.bullet_x, self.bullet_y)
if self.bullet_y < self.rocket_y - 10:
pygame.draw.rect(screen, (0, 0, 0), self.bullet_rect)
if self.bullet_y < - 20:
self.bullet_y = self.rocket_y
self.move[1] = 0
rocket = ROCKET()
bullet = BULLET()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill((255, 255, 255))
rocket.draw_rocket()
rocket.move_rocket()
bullet.draw_bullet()
pygame.display.flip()
clock.tick(60)
You have to set the x-coordinate of the bullet by the current x-coordinate of the rocket.
Add an argument rocket to the method draw_bullet of the class BULLET. Make sure that you can only fire the bullet if the bullet has not yet been fired (self.move[1] == 0). Compute the center of the rocket and set the position of the bullet (rocket.rocket_x + self.rocketImg.get_width() // 2):
class BULLET(ROCKET):
# [...]
def draw_bullet(self, rocket):
key = pygame.key.get_pressed()
if key[pygame.K_SPACE] and self.move[1] == 0:
rocket_center_x = rocket.rocket_x + self.rocketImg.get_width() // 2
self.bullet_x = rocket_center_x - self.bullet_width // 2
self.move[1] = -1
# [...]
Passe the instance of ROCKET to the method draw_bullet:
while True:
# [...]
bullet.draw_bullet(rocket)
# [...]
If you want to fire multiple bullets, see the answers to the questions:
How can i shoot a bullet with space bar?
How do I stop more than 1 bullet firing at once?
I need help deleting an object, and I mean delete, not draw over or other things.
My code so far:
def detect_collision(player_pos, enemy_pos):
p_x = player_pos[0]
p_y = player_pos[1]
e_x = enemy_pos[0]
e_y = enemy_pos[1]
if (e_x >= p_x and e_x < (p_x + player_size)) or (p_x >= e_x and p_x < (e_x+enemy_size)):
if (e_y >= p_y and e_y < (p_y + player_size)) or (p_y >= e_y and p_y < (e_y+enemy_size)):
return True
return False
def bullets():
b_x = player_pos[0]
b_y = player_pos[1]
keep_going = True
pygame.draw.rect(screen, TEAL, (b_x, b_y, 15, 50))
while keep_going:
b_y += 75
if detect_collision(player_pos, enemy_pos):
# deleting part here
Here is what makes my player and enemy:
enemy_size = 50
enemy_pos = [random.randint(0,WIDTH-enemy_size), 0]
enemy_list = [enemy_pos]
def drop_enemies(enemy_list):
delay = random.random()
if len(enemy_list) < 10 and delay < 0.1:
x_pos = random.randint(0,WIDTH-enemy_size)
y_pos = 0
enemy_list.append([x_pos, y_pos])
def draw_enemies(enemy_list):
for enemy_pos in enemy_list:
pygame.draw.rect(screen, RED, (enemy_pos[0], enemy_pos[1],
enemy_size, enemy_size))
def update_enemy_positions(enemy_list, score):
for idx, enemy_pos in enumerate(enemy_list):
if enemy_pos[1] >= 0 and enemy_pos[1] < HEIGHT:
enemy_pos[1] += SPEED
else:
enemy_list.pop(idx)
score += 1
return score
Player part:
player_size = 50
player_pos = [WIDTH/2, HEIGHT-2*player_size]
pygame.draw.rect(screen, TEAL, (player_pos[0], player_pos[1], player_size,
player_size))
The best way to solve your issue is to learn how to use Sprite objects in pygame. Together with Group objects, they can already do what you want, right out of the box.
In brief, your "enemy" should be an instance of some Sprite subclass, and you'd add it to an instance of Group (rather than building your own enemy_list). When you want the enemy to die, you can call the kill() method on it, which will remove it from the Group. This serves to delete it from the game, since you should be using methods on the Group object to update and draw all the sprites it contains (but not ones that have been killed).
It would be beneficial to look into Sprites and SpriteGroups to track entities in your game. They have a bunch of functionality built in that will make things easier.
Here's a demo I have that groups sprites, removing those that collide with the mouse pointer:
import random
import pygame
screen_width, screen_height = 640, 480
def get_random_position():
"""return a random (x,y) position in the screen"""
return (
random.randint(0, screen_width - 1), # randint includes both endpoints.
random.randint(0, screen_height - 1),
)
color_list = ["red", "orange", "yellow", "green", "cyan", "blue", "blueviolet"]
colors = [pygame.color.Color(c) for c in color_list]
class PowerUp(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
width, height = 64, 32
self.color = random.choice(colors)
self.image = pygame.Surface([width, height])
self.image.fill(self.color)
# Fetch the rectangle object that has the dimensions of the image
self.rect = self.image.get_rect()
# then move to a random position
self.update()
def update(self):
# move to a random position
self.rect.center = get_random_position()
if __name__ == "__main__":
pygame.init()
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Sprite Group Collision Demo")
clock = pygame.time.Clock() # for limiting FPS
FPS = 60
exit_demo = False
# create a sprite group to track the power ups.
power_ups = pygame.sprite.Group()
for _ in range(10):
power_ups.add(PowerUp()) # create a new power up and add it to the group.
# main loop
while not exit_demo:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit_demo = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
exit_demo = True
elif event.key == pygame.K_SPACE:
power_ups.update()
elif event.type == pygame.MOUSEBUTTONUP:
for _ in range(10):
power_ups.add(PowerUp())
# Update State: check for collisions
for p in power_ups:
if p.rect.collidepoint(pygame.mouse.get_pos()):
power_ups.remove(p)
# draw background
screen.fill(pygame.Color("black")) # use black background
# draw sprites
power_ups.draw(screen)
# update screen
pygame.display.update()
clock.tick(FPS)
pygame.quit()
quit()
Clicking a mouse button creates more sprites and pressing the space bar randomises their position.
The update() method of a bullet sprite would adjust the sprite position by its speed, e.g. self.rect.x += SPEED and you would need to call the bullet sprite group .update() method every game loop.
Right, so you have a list of enemy positions in enemy_list. That's a good start. I don't see a bullet_list, so I will assume only a single bullet at a time, positioned at b_x,b_y.
So a main loop for this program might look something like:
### Main Loop
while not game_over:
# handle user input
# move the player
# move the bullet (if any)
# move the enemies
# if there's a bullet, did it hit an enemy?
# remove enemy hit from enemy_list
# Did an enemy in enemy_list, hit the player?
# do game-over
# clear the screen
# paint the player
# paint the bullet (if any)
# paint every enemy in enemy_list
Where the enemy collision, and list removal might look something like the below. (I've tried to match the look of your code as much as possible.)
# if a bullet is on-screen (non 0,0), see if it hits an enemy
if ( b_x != 0 and b_y != 0 ):
hit_enemy_idx = None
for idx, enemy_pos in enumerate( enemy_list ):
# check for collision
if detect_collision( ( b_x, b_y ), enemy_pos ):
hit_enemy = idx # note the enemy index
b_x, b_y = 0, 0 # erase the bullet
break # exit the loop when hit found
# If the bullet hit an enemy, remove from list
if hit_enemy_idx != None:
del( enemy_list[ hit_enemy_idx ] ) # delete hit enemy from list
We do an iteration through the enemy list, checking for a collision. Once a hit is found, it save the index of which enemy was hit, and stops the loop.
The next step is to delete the enemy from the list. I have written this in a separate block, rather than placing it before the break in the loop. This is because unexpected results can happen when you change a list as you're iterating over it. In this particular case it would have been OK, but it's something to be wary of as a beginner.
I'm assuming there's only one bullet in screen at a time since you didn't mention a bullet list. You probably create bullet by hitting space or something which I'll leave out here. One thing to consider in you bullets() function is your doing moving, drawing and checking for collision in the same function. Note, it is always a good idea to make a function do only one thing.
def draw_bullet():
move_bullet()
pygame.draw.rect(screen, TEAL, (b_x, b_y, 15, 50))
def move_bullet():
if b_y < HEIGHT:
b_y += 75
else:
create_bullet = False # explained below
So to create a bullet, you should have a Boolean create_bullet variable. So:
# mainloop
if user hits spacebar (or the key used to create bullet) :
create_bullet = true
b_x = player_pos[0] # initialise bullet
b_y = player_pos[1]
if create_bullet:
draw_bullet()
if detect_collision((b_x, b_y), enemy_pos):
# assuming b_x, b_y are global variable or something, so that they are actually defined and equal to bullet's x and y pos
# if bullet and enemy collide, remove them both
enemy_list.remove(enemy)
create_bullet = False
if detect_collision(player_pos, enemy_pos):
# reset player pos, or whatever you want to do
you say you want to delete it and not just draw over it. However the way pygame generates a 'moving/ video-like' screen is by continuously drawing over. E.g: player is drawn at (10, 10) and then is drawn at (20, 10) so it appears like player moved. However, this is done very fast so you don't see him 'disappear' and 'reappear'. So here's what the above code does.
When spacebar is hit, it 'creates' the bullet by setting it's x and y value to the current player position and sets create_bullet = true. Then, on each iteration of the main loop, if create_bullet is true, it moves then draws the bullet. If the bullet moves outside the screen or collides with enemy, create_bullet = False, so it will stop drawing it and on the next iteration of main loop, bullet will be drawn over by the background and would 'disappear'.
import pygame
import os
import random
from pygame.locals import * # Constants
import math
import sys
import random
pygame.init()
screen=pygame.display.set_mode((1280,700)) #(length,height)
screen_rect=screen.get_rect()
background = pygame.Surface(screen.get_size())
background.fill((255,255,255)) # fill the background white
background = pygame.image.load('stage.png').convert()
Black=(0,0,0)
class Player(pygame.sprite.Sprite):
x = 20
y = 615
def __init__(self):
super().__init__() # calls the parent class allowing sprite to initalize
self.image = pygame.Surface((50,25)) # this is used to create a blank image with the size inputted
self.image.fill((0,0,128)) # fills the blank image with colour
self.rect = self.image.get_rect(topleft =(20,615)) # This place the player at the given position
self.dist = 10
def update(self): # code to make the character move when the arrow keys are pressed
if self.rect.right > 100: # These are to make the player so move constantly
self.rect.y += 1
self.rect.x += 2
if self.rect.bottom == 700:
pygame.quit()
keys = pygame.key.get_pressed()
if keys[K_LEFT]:
self.rect.move_ip(-1,0)
elif keys[K_RIGHT]:
self.rect.move_ip(0.5,0)
elif keys[K_UP]:
self.rect.move_ip(0,-0.5)
elif keys[K_DOWN]:
self.rect.move_ip(0,1)
self.rect.clamp_ip(screen_rect)
#while self.rect == (20,615):
if keys [K_SPACE]:
self.rect = self.image.get_rect(topleft =(100,100))
class Enemy(pygame.sprite.Sprite): # the enemy class which works fine
def __init__(self):
super().__init__()
x = random.randint(50,450)
self.image = pygame.Surface((50,25))
self.image.fill((128,0,0))
self.rect = self.image.get_rect(topleft (300, 50))
self.direction = 0
def update(self):
self.rect.y += 2 if self.direction == 0 else -2
if self.rect.bottom >= 600:
self.direction = 1
if self.rect.top <= 50:
self.direction = 0
clock = pygame.time.Clock() # A clock to limit the frame rate.
player = Player()
enemy = Enemy()
enemy_list = pygame.sprite.Group() # a group where the enemys will be put
sprites = pygame.sprite.Group(player) # The group where evry spirte will be put into
for i in range (5): # creates 5 enemy spirtes
enemy = Enemy() # calls the enemy class
enemy.rect.x = random.randrange(200, 1100) # makes the enemny spawn random
enemy.rect.y = random.randrange(50, 600)
enemy_list.add(enemy) # adds the enemy to the group
sprites.add(enemy)
I got the code so that the enemies will randomly and then they will move up and down however since the spawning is random they will sometimes overlap i was wondering how i would get it so that they don't overlap when they move up and down
i was wondering if i could do it so they have a gap when they spawns e.g. 50 in x axis but still spawn five enemies
def main(): #my main loop
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
sprites.update()
screen.blit(background, (0, 0))
sprites.draw(screen)
clock.tick(100) # Limit the frame rate to 60 FPS.
pygame.display.flip() #updates the whole screen
#Collison check
player_hit_list = pygame.sprite.spritecollide(player, enemy_list, True)
for enemy in player_hit_list:
pygame.quit()
if __name__ == '__main__':
main()
Try this approach (I've purposely made the code a little verbose to try to explain what's going on):
...
number_of_enemies = 5
min_enemy_x = 200
max_enemy_x = 1100
enemy_x_range = max_enemy_x - min_enemy_x # zone in which enemies can spawn
enemy_zone_width = enemy_x_range / number_of_enemies # zone width for each enemy
pixel_buffer = 50 # gap between enemies
for i in range (number_of_enemies): # creates 5 enemy spirtes
enemy = Enemy() # calls the enemy class
min_x = min_enemy_x + enemy_zone_width * i + pixel_buffer / 2 # minimum x at which current enemy can spawn (including buffer)
max_x = min_enemy_x + enemy_zone_width * (i + 1) - pixel_buffer / 2 # maximum x at which current enemy can spawn (including buffer)
enemy.rect.x = random.randrange(min_x, max_x) # makes the enemy spawn random
enemy.rect.y = random.randrange(50, 600)
enemy_list.add(enemy) # adds the enemy to the group
...
Note that this will produce a 25 pixel buffer at the start and end (so enemies will actually spawn between 225 and 1075), but you can either adjust min_enemy_x and max_enemy_x to compensate, or remove the buffering for the first and last loop iterations.
I'm trying to detect collisions between the player and the floor. This is part of my school project so any insight would be helpful. Also suggestions to improve the code will be appreciated. Here is my code:
import pygame
#initialise pygame
pygame.init()
#variables
level = 0
velx = 0
vely = 0
health = 1
floor_group = set([])
clock = pygame.time.Clock()
#collisions
def detectCollisions(x1, y1, w1,h1, x2, y2, w2, h2):
if (x2 + w2 >= x1 >= x2 and y2 + h2 >= y1 >= y2):
return True
elif (x2 + w2 >= x1 + w1 >= x2 and y2 + h2 >= y1 >= y2):
return True
elif (x2 + w2 >= x1 >= x2 and y2 + h2 >= y1 + h1 >= y2):
return True
elif (x2 + w2 >= x1 + w1 >= x2 and y2 + h2 >= y1+ h1 >= y2):
return True
else:
return False
#screen size the same size as my background
window = pygame.display.set_mode((900,563))
#Load Images: Backgrounds
background0 = pygame.image.load("background0.png").convert()
#Load Sprites
halfspike = pygame.image.load("halfspike.png").convert_alpha()
spike = pygame.image.load("spike.png").convert_alpha()
platform = pygame.image.load("platform.png").convert_alpha()
spider = pygame.image.load("spider.png").convert_alpha()
char1 = pygame.image.load("char1.png").convert_alpha()
char2 = pygame.image.load("char2.png").convert_alpha()
#Window title
pygame.display.set_caption("Super Boshy Brothers")
# character class
class Sprite:
def __init__(self,x,y):
self.x = x
self.y = y
self.width = 42
self.height = 44
self.velx = 0
self.vely = 0
self.image0 = pygame.image.load("char1.png")
self.image1 = pygame.image.load("char2.png")
self.timeTarget = 10
self.timeNumber = 0
self.currentImage = 0
def update(self):
self.timeNumber += 1
if (self.timeNumber == self.timeTarget):
if (self.currentImage == 0):
self.currentImage = 1
else:
self.currentImage = 0
self.timeNumber = 0
self.render()
def render(self):
if (self.currentImage == 0):
window.blit(self.image0, (self.x, self.y))
else:
window.blit(self.image1, (self.x, self.y))
# Floor class
class Floor:
def __init__(self,x,y):
self.x = x
self.y = y
self.width = 43
self.height = 44
self.image0 = pygame.image.load("floor.png")
def update(self):
self.render()
def render(self):
window.blit(self.image0, (self.x, self.y))
def floor_spawner(row):
global floor_group
for i in range(0,946,43):
floor_group.add(Floor(i,row))
#Create first level floor
floor_spawner(519)
floor_spawner(475)
#player
player = Sprite(0,431)
#create our main loop
gameloop = True
while gameloop:
for event in pygame.event.get(): #get function handles events
if (event.type == pygame.QUIT): #if Quit (red x) pressed exit loop
gameloop = False
if (event.type == pygame.KEYDOWN): #If a key is pressed down
if (event.key ==pygame.K_LEFT): #If Left Arrow
velx = -7
if (event.key ==pygame.K_RIGHT): #If Right Arrow
velx = 7
if (event.key ==pygame.K_UP): #If Up Arrow
vely = -7
if (event.type == pygame.KEYUP): #If a key is pressed down
if (event.key ==pygame.K_LEFT): #If Left Arrow
velx = 0
if (event.key ==pygame.K_RIGHT): #If Right Arrow
velx = 0
if (event.key ==pygame.K_UP): #If Up Arrow
vely = 0
#Level 0
if level == 0:
window.blit(background0, (0,0)) #Bottom bricks
for f in list(floor_group):
f.render()
if player.x <= 0: #Left side collision
player.x = 0
if player.x >= 900: #Level change
level = 1
player.x = 0
#Level 1
if level == 1:
window.blit(background0, (0,0)) #Bottom bricks
for f in list(floor_group):
f.render()
player.x += velx
player.y += vely
player.update()
clock.tick(50) #Tick Tock Tick Tock
pygame.display.flip() #Updates the window
pygame.quit()
It's better if you use pygame sprite to do your assignment.
For managing the floor collision problem, just detect the collision as I do in the code below, if a collision between player and floor occurs then simply move the player back to the previous position.
Here's my code:
import pygame
import random
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 255, 0)
WHITE = (255, 255, 255)
#Sprite are basically game images in pygame
#Through sprites collision detection and rendering becomes much easier
class Block(pygame.sprite.Sprite):
#Here I've a block class which is a subclass of the pygame's sprite class
def __init__(self, image):
pygame.sprite.Sprite.__init__(self)
# Here I've initialised he superclass Sprite
self.image = image
#Image of sprite = image .. that's it
self.rect = self.image.get_rect()
#It get's all dimesion's of image which will help it in detecting collision
pygame.init()
infoObject = pygame.display.Info()
# pygame.display.Info() provides us with the information about cureent resolution and a bunch of other stuff
screen = pygame.display.set_mode((infoObject.current_w, infoObject.current_h))
char1 = pygame.image.load("char1.png").convert_alpha()
char2 = pygame.image.load("char2.png").convert_alpha()
char2_list = pygame.sprite.Group()
# Sprite groups are sets for sprites
# i.e. We have different types of object stored in different groups
# In our game the char1 and char2 are of different
#internal interaction between all char2 does'nt matter
#It's the interaction between the char1 and char2 that we've to deal with
all_list = pygame.sprite.Group()
#I've made a group which contains all the blocks which helps me in rendering them all together
for i in range(50):
block = Block(char1)
block.rect.x = random.randrange(infoObject.current_w)
block.rect.y = random.randrange(infoObject.current_h)
charimport pygame
import random
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 255, 0)
WHITE = (255, 255, 255)
#Sprite are basically game images in pygame
#Through sprites collision detection and rendering becomes much easier
class Block(pygame.sprite.Sprite):
#Here I've a block class which is a subclass of the pygame's sprite class
def __init__(self, image):
pygame.sprite.Sprite.__init__(self)
# Here I've initialised he superclass Sprite
self.image = image
#Image of sprite = image .. that's it
self.rect = self.image.get_rect()
#It get's all dimesion's of image which will help it in detecting collision
pygame.init()
infoObject = pygame.display.Info()
# pygame.display.Info() provides us with the information about cureent resolution and a bunch of other stuff
screen = pygame.display.set_mode((infoObject.current_w, infoObject.current_h))
char1 = pygame.image.load("char1.png").convert_alpha()
char2 = pygame.image.load("char2.png").convert_alpha()
char2_list = pygame.sprite.Group()
# Sprite groups are sets for sprites
# i.e. We have different types of object stored in different groups
# In our game the char1 and char2 are of different
#internal interaction between all char2 does'nt matter
#It's the interaction between the char1 and char2 that we've to deal with
all_list = pygame.sprite.Group()
#I've made a group which contains all the blocks which helps me in rendering them all together
for i in range(50):
block = Block(char1)
block.rect.x = random.randrange(infoObject.current_w)
block.rect.y = random.randrange(infoObject.current_h)
char2_list.add(block)
all_list.add(block)
player = Block(char2)
running = True
clock = pygame.time.Clock()
score = 1
all_list.add(player)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(BLACK)
pos = pygame.mouse.get_pos()
#Gets position of the mouse
player.rect.x = pos[0]
player.rect.y = pos[1]
char_hit_list = pygame.sprite.spritecollide(player, char2_list, True)#Set it to false and see the result
#Checks collision
for block in char_hit_list:
score += 1
print score
all_list.draw(screen)
#renders(draw) all the sprites onto screen
pygame.display.update()
#update's display
clock.tick(60)
# Sets Frame Rate to 60
pygame.quit()2_list.add(block)
all_list.add(block)
player = Block(char2)
running = True
clock = pygame.time.Clock()
score = 1
all_list.add(player)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(BLACK)
pos = pygame.mouse.get_pos()
#Gets position of the mouse
player.rect.x = pos[0]
player.rect.y = pos[1]
char_hit_list = pygame.sprite.spritecollide(player, char2_list, True)#Set it to false and see the result
#Checks collision
for block in char_hit_list:
score += 1
print score
all_list.draw(screen)
#renders(draw) all the sprites onto screen
pygame.display.update()
#update's display
clock.tick(60)
# Sets Frame Rate to 60
pygame.quit()
And one more thing try not to hard-code stuff like you did for screen dimension.
For learning more about pygame Sprites have a look at this.
If you have any problem, let me know by commenting in the comments section below, I'll try my best to help you out with your problem.
Keep pygaming :)