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'.
Related
I'm quite new to pygame and am currently doing a school project. My problem is that i have been attempting to create a border for my game so my charectar cant go out from the screen.
Heres my code so far:
# This imports the Pygame Module / loads pygame keywords
import pygame
# This lets python use my file system
import sys
# This helps python identify my OS, which is Windows 10
import os
"""
Variables
"""
img = 'PlayerSprite1'
# This is the basic set screen width and height / 320x320 pixels
worldx = 1271
worldy = 957
world = pygame.display.set_mode([worldx, worldy])
# This is for the frame rate and animation cycles, in order
fps = 30
ani = 4
# These are the color definitions - Red, Green, Blue (RGB)
RED = (0, 0, 0)
GREEN = (128, 128, 128)
BLUE = (128, 128, 128)
# This is used in order to make the sprites color block be 'invisible', in order for it to blend into the background
ALPHA = (00, 00, 00)
ALPHA1 = (255, 255, 255)
"""
Objects
"""
'Individual sprite classes: '
class Player(pygame.sprite.Sprite):
# Spawn the player's sprite/ character/ main player's sprite
def __init__(self):
pygame.sprite.Sprite.__init__(self)
# These variables will be to track frames and movements
self.movex = 0 # This is to monitor movement along X
self.movey = 0 # This is to monitor movement along Y
self.frame = 0 # This counts frames
self.health = 10 # This counts health
self.images = []
for i in range(1, 5):
# This loads the image from the selected directory in the pycharm folder
img = pygame.image.load(os.path.join('images', 'PlayerSprite1' + str(i) + '.png')).convert()
img.convert_alpha() # This is used to optimize alpha
img.set_colorkey(ALPHA) # This is used to set alpha
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
# This controls the players sprites movement
def control(self, x, y):
self.movex += x
self.movey += y
# This updates the sprites position, by redefining its position, designated by self.rect.x and self.rect.y,
# to its current position plus whatever amount movex or movey is
def update(self):
self.rect.x = self.rect.x + self.movex
self.rect.y = self.rect.y + self.movey
# This is for moving left
if self.movex < 0:
self.frame += 1
if self.frame > 3 * ani:
self.frame = 0
self.image = self.images[self.frame // ani]
hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
for enemy in hit_list:
self.health -= 1
print(self.health)
# This is for moving right
if self.movex > 0:
self.frame += 1
if self.frame > 3 * ani:
self.frame = 0
self.image = self.images[self.frame // ani]
# This class is for the main antagonistic sprite in opposition to players sprite
class Enemy(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
# These variables will be to track frames and movements
self.movex = 0 # This is to monitor movement along X
self.movey = 0 # This is to monitor movement along Y
self.frame = 0 # This counts frames
self.images = []
self.counter = 0
for i in range(1, 4):
# This loads the image from the selected directory in the pycharm folder
img = pygame.image.load(os.path.join('images', 'antagonist' + str(i) + '.png')).convert()
img.convert_alpha() # This is used to optimize alpha
img.set_colorkey(ALPHA1) # This is used to set alpha
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
# This is to alter the scale of this sprite in order for it to fit into the overall pixel scale of the game
# THIS IS NOT YET DONE, DO ASAP
pygame.transform.scale(pygame.image.load(os.path.join('images', 'antagonist' + str(i) + '.png')), (13, 29))
def move(self):
distance = 80
speed = 8
if self.counter >= 0 and self.counter <= distance:
self.rect.x += speed
elif self.counter >= distance and self.counter <= distance * 2:
self.rect.x -= speed
else:
self.counter = 0
self.counter += 1
'These classes are for levels: '
# This class is for level one of the game ( NOT YET DONE )
class Level():
def bad(lvl, eloc):
if lvl == 1:
enemy = Enemy()
eloc = 0
eloc = 1
img = 'antagonist1.png'
enemy_list = pygame.sprite.Group()
enemy_list.add(enemy)
if lvl == 2:
print("Level " + str(lvl))
return enemy_list
'These classes are for Platforms'
"""
Setup
"""
# This creates the boundaries for the sprite so it dosnt go outside the borders
# This is used for various loops, mainly for keeping the main loop, in loop
main = True
# This is for the internal clock and for pygame to initialize
clock = pygame.time.Clock()
pygame.init()
# This is to set up the Main background
backdrop = pygame.image.load(os.path.join('images', 'Main Background.jpg'))
backdropbox = world.get_rect()
# This is to bring the actual '123' sprite into the game, or the 'game world'
player = Player() # This spawns player's sprite
player.rect.x = 10 # go to x
player.rect.y = 12 # go to y
player_list = pygame.sprite.Group()
player_list.add(player)
steps = 10 # This is how many pixels the sprite will move
# This is to add the Main antagonistic enemy sprite into the 'actual' game and further create a 'group' for a sort of
# wave, which will be a core mechanic of the game
enemy = Enemy() # This spawns the antagonist
enemy.rect.x = 10
enemy.rect.y = 12
enemy_list = pygame.sprite.Group() # This creates the antagonist group
enemy_list.add(enemy) # This adds the antagonist to the group
eloc = []
eloc = [300, 0]
enemy_list = Level.bad(1, eloc)
"""
Main Loop
"""
# This is to make sure the background does not just appear for a singular millisecond
world.blit(backdrop, backdropbox)
player.update() # This updates the players position to make the character move from pixel to pixel, making it appear
# as it is moving
# This, similar to the above is to make sure the player's sprite does not just appear for a singular millisecond
player_list.draw(world) # draw player
enemy_list.draw(world) # refresh enemies
pygame.display.flip()
clock.tick(fps)
# This is the main loop, which keeps the game's execution in a loop, in order to keep it on the screen and operating
# for longer then a couple milliseconds
while main:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
try:
sys.exit()
finally:
main = False
# These next two sections add keybindings, this time being changed by replacing the print statements with the
# player's sprite name, including the .control function, and how many steps to move with each loop
if event.type == pygame.KEYDOWN:
if event.key == ord('q'):
pygame.quit()
try:
sys.exit()
finally:
main = False
if event.key == pygame.K_LEFT or event.key == ord('a'):
player.control(-steps, 0)
if event.key == pygame.K_RIGHT or event.key == ord('d'):
player.control(steps, 0)
if event.key == pygame.K_DOWN or event.key == ord('s'):
player.control(-steps, 0)
if event.key == pygame.K_UP or event.key == ord('w'):
player.control(-steps, 0)
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == ord('a'):
player.control(steps, 0)
if event.key == pygame.K_RIGHT or event.key == ord('d'):
player.control(-steps, 0)
if event.key == pygame.K_DOWN or event.key == ord('s'):
player.control(-steps, 0)
if event.key == pygame.K_UP or event.key == ord('w'):
player.control(-steps, 0)
world.blit(backdrop, backdropbox)
img_rect = img.get_rect(topleft=[100, 200])
world.blit(img, img_rect)
player.update()
player_list.draw(world)
enemy_list.draw(world)
for e in enemy_list:
e.move()
pygame.display.flip()
clock.tick(fps)
I know it has somthing to do with classes and localisation of code and ive tried to fix that many times but it never seems to work, either not working once executed or never even executing. If anyone has any suggestions, that would be great! Also apoligies for the very messy and unorganised code, if anyone has any further tips on how i should improve my code, though not neccesary, I'll be in your debt!
To get rid of the error message just out-comment the lines causing it as follows:
world.blit(backdrop, backdropbox)
# img_rect = img.get_rect(topleft=[100, 200])
# world.blit(img, img_rect)
player.update()
This will do no harm to the game as these lines do nothing else ( if img is an actual pygame image ) as drawing an image to the screen which appears to have no function in the game.
Then see what the code does and how to achieve with it what you want.
With a running code you can investigate its behavior while changing and updating the code and so make further progress.
Another option is to actually display an image if this image was intended to be a border, but for this you need to assign a pygame image to img as follows:
world.blit(backdrop, backdropbox)
img = pygame.image.load('border.png').convert()
img_rect = img.get_rect(topleft=[100, 200])
world.blit(img, img_rect)
player.update()
Notice that the player and enemy images will overlay the border.png image as they will be drawn later in the game while main loop. Move the code displaying this image after the code updating the player and enemy if you want the border not to be covered by the player or enemy sprites.
This above is my guess what you are after and hope it helps you to make further progress.
I'm making a basic dodger game where you play as a fish and need to avoid getting hit by obstacles. I want the obstacles to come from the right side of the screen at a random velocity (from a certain range, let's just say 4 to 6). I already have the fish/underwater gravity mechanics working. But I have no idea where to start with the obstacles.
This is my main python script:
import pygame
from assets.player import Bob
# Screen properties
pygame.init()
# Important Variables
run = True
game_speed = 0
SCREEN_WIDTH, SCREEN_HEIGHT = 900, 600
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
clock = pygame.time.Clock()
FPS = 60
# Properties
bg_game_surface = pygame.image.load('images/game-background.png')
player = Bob(game_speed)
#############
# Functions #
#############
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
screen.blit(bg_game_surface, (0, 0))
player.update(screen)
pygame.display.update()
clock.tick(FPS)
pygame.quit()
And this is the class for my player:
import pygame
class Bob:
def __init__(self, game_speed):
self.img = pygame.image.load('images/player/bob.png').convert_alpha()
self.img_up = pygame.image.load('images/player/bob_up.png').convert_alpha()
self.rect = self.img.get_rect()
self.game_speed = game_speed
def update(self, screen):
key = pygame.key.get_pressed()
### Collision
if self.rect.top <= 0:
self.rect.centery = (screen.get_height() - self.img.get_height() * 2) + 10
elif self.rect.top >= screen.get_height() - self.img.get_height() * 2:
self.rect.centery = 20
### Movement
if key[pygame.K_w] or key[pygame.K_UP]:
self.rect.centery -= 6
self.img = pygame.image.load('images/player/bob_up.png').convert_alpha()
### Gravity
else:
self.img = pygame.image.load('images/player/bob.png').convert_alpha()
self.rect.centery += 4
screen.blit(self.img, self.rect)
My folders are sorted like this:
Right now, I'd like that the trash.py is also a class with a function called "update". When that function is executed, obstacles (preferably in the form of an image) come out of the right side of the display, doing what I said above.
Any help is appreciated. Thanks in advance :)
You should make a sprite class group, but first you need to import the random module:
import random
Now you can create the Obstacle Sprite class, this is just an example:
class Obstacle(pygame.sprite.Sprite):
def __init__(self, pos): #pos is the starting position
super().__init__()
self.image = pygame.Surface((10,10)) # You can change this with pygame.image.load('...')
self.rect = self.image.get_rect(topleft = pos)
self.vel = random.randint(4,6) #random speed of the obstacle
def collisions(self, player): #collisions function
if self.rect.colliderect(player.rect):
print('collision!') #write what you want to happen
def movement(self): #the blocks are moving left
if self.rect.x > -100: #just putting some limits
self.rect.x -= self.vel
def update(self, player): #try to put all the other functions in the update one
self.collisions(player)
self.movement()
Once you created the class, you only need to create some objects of that class (every obstacle).
First we create a sprite group before of the while loop:
obstacles = pygame.sprite.Group()
Then, inside your main loop, you need a condition that generates the obstacles.
I've done something like this, but you really should change it:
if x < 10: #I created x before the while (x=0)
posx = SCREEN_WIDTH + 50 #the starting x will be 50 pixels right of the screen
posy = random.randint(0, SCREEN_HEIGHT) #Im setting this to random
obs = Obstacle((posx, posy))
obstacles.add(obs)
x +=1
This basically creates 9 obstacles when we start the game, with different random x speed and y location, and draw them on the screen. If you collide with one of the blocks, the code just prints "collision!" (see the Obstacle class). Hope it was helpful and I didn't miss something.
I'm making a game with Python. I have a spaceship in this game. The spaceship can move to the left and to the right and it is supposed to shoot bullets from its x-position and it is supposed to fire the bullets up. I am able to move the spaceship and I am able to put it on the screen. The problem is that I am not able to fire the bullet from the spaceship's x-position and I am not able to make the bullet move up. Can somebody help me with this? Please post simple code too if possible.
Here's the code:
import pygame
import sys
pygame.init()
screen_length = 512
screen_width = 288
clock = pygame.time.Clock()
screen = pygame.display.set_mode((screen_width, screen_length))
bg = pygame.image.load(r'C:\Users\Anonymous\Downloads\space game folder\space background3.png').convert()
bg = pygame.transform.scale2x(bg)
spaceship = pygame.image.load(r'C:\Users\Soorya\Anonymous\space game folder\spaceship.png').convert()
missile = pygame.image.load(r'C:\Users\Soorya\Anonymous\space game folder\bullet2.png').convert()
x = 130
y = 480
z = 460
velocity = 30
spaceship_rect = spaceship.get_rect(center=(x, y))
spaceship_x_pos = spaceship_rect.left + 170
bullet_speed = 10
missile_rect = missile.get_rect(center=(round(spaceship_x_pos // 2), z))
spaceship_direction = 10
while True:
for event in pygame.event.get():
if event == pygame.QUIT:
pygame.display.quit()
sys.exit(0)
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT] and x < 288:
x += velocity
if keys[pygame.K_LEFT] and x > 0:
x -= velocity
if keys[pygame.K_SPACE]:
bg.blit(missile, missile_rect)
screen.fill(0)
screen.blit(bg, (0, 0))
screen.blit(spaceship, (x, y))
pygame.display.update()
clock.tick(120)
The problem with your code is that your indentation is not right.
while True:
for event in pygame.event.get():
if event == pygame.QUIT:
pygame.display.quit()
sys.exit(0)
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT] and x < 288:
x += velocity
if keys[pygame.K_LEFT] and x > 0:
x -= velocity
if keys[pygame.K_SPACE]:
bg.blit(missile, missile_rect)
screen.fill(0) # THIS
screen.blit(bg, (0, 0)) #THIS
screen.blit(spaceship, (x, y)) #THIS
pygame.display.update() #THIS
clock.tick(120) #THIS
The lines where i commented this have to be indented like this:
while True:
for event in pygame.event.get():
if event == pygame.QUIT:
pygame.display.quit()
sys.exit(0)
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT] and x < 288:
x += velocity
if keys[pygame.K_LEFT] and x > 0:
x -= velocity
if keys[pygame.K_SPACE]:
bg.blit(missile, missile_rect)
screen.fill(0)
screen.blit(bg, (0, 0))
screen.blit(spaceship, (x, y))
pygame.display.update()
clock.tick(120)
That's because you want to do those things every single frame while the game is running. One more thing, you are filling the screen with black in line screen.fill(0), which is not necessary since you are already rendering a background image, so you basically cannot see it. With that being said, i recommend taking an object oriented approach as well because you wont get very far without it. Also, set velocity to like 0.5 or something. Its currently 30, which means 30 pixels every frame and that's not what you want.
Now to fire bullets. What you are doing right now wont work because you are only "blitting" a missile if space is pressed. Below i wrote a separate code for you that only fires missiles, so hopefully its clear and you can implement in your own code.
import pygame
import sys
pygame.init()
d = pygame.display.set_mode((1200, 600))
# WE WILL USE FIRING BOOLEAN TO KEEP TRACK OF IF WE ARE FIRING MISSILES
firing = False
missile = pygame.Surface((10, 50))
while True:
d.fill((255, 255, 255))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
# Assign initial positions only if it isnt firing
if not firing:
missilex = 600 # This is the initial position of the missile. This will be
missiley = 500 # your player position in your game
if pygame.key.get_pressed()[pygame.K_SPACE]: # if space pressed set firing to true
firing = True
# if firing is true, we want to display the missile and move it.
if firing:
d.blit(missile, (missilex, missiley))
missiley -= 1
# if missile is off the screen, set firing to false so it resets to initial position (600, 500).
if missiley < -20:
firing = False
pygame.display.update()
The way you are approaching it doesn't seem right to me, unless ofc that isn't your whole code. But you are using the same variable to represent the x and y coordinate of your bullet and the spaceship. You want to bullet to move independently from the space ship. So if you change the variable y in your code, that will move space ship up too, and same with the x variable.
What you need to do is to create 2 separate classes. One for you bullet, and the other for the space ship. Here is a simple example of its implementation, which you can improve upon by adding whatever features you want later.
class Spaceship:
image = pygame.image.load(r'C:\Users\Soorya\Anonymous\space game folder\spaceship.png').convert()
def __init__(self, x, y, vel):
self.x = x
self.y = y
self.vel = vel
def draw(self, screen):
screen.blit(Spaceship.image, (self.x, self.y))
class Bullet:
image = pygame.image.load(r'C:\Users\Soorya\Anonymous\space game folder\bullet2.png').convert()
bullet_list = [] # holds the list of bullets in the screen
def __init__(self, x, y, vel):
self.x = x
self.y = y
self.vel = vel
Bullet.bullet_list.append(self)
def move(self):
self.y -= self.vel
if self.y < 0: # remove bullet if it goes beyond screen
Bullet.bullet_list.remove(self)
def draw(self, screen):
screen.blit(Bullet.image, (self.x, self.y))
spaceship = Spaceship(130, 480, 30)
# set up screen and bg as before
while True:
for event in pygame.event.get():
if event == pygame.QUIT:
pygame.display.quit()
sys.exit(0)
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT] and x < 288:
spaceship.x += spaceship.vel
if keys[pygame.K_LEFT] and x > 0:
spaceship.x -= spaceship.vel
if keys[pygame.K_SPACE]: # create a bullet if space is pressed
Bullet(spaceship.x, spaceship.y, 10)
screen.blit(bg, (0, 0))
spaceship.draw()
for bullet in Bullet.bullet_list():
bullet.move() # actually moves the bullet on each iteration of the loop
bullet.draw()
pygame.display.update()
clock.tick(120)
Important note for future if you want to create games, make everything object oriented lol. Trust me, it will make your life a whole lot easier. Let me know if you have any problems.
so, Im trying to get a reaction when the sprite player pass around (not over) another sprite.
So far my idea is something like:
if (sprite.x +100) > player.x > (sprite.x -100) and (sprite.y +100) > player.y > (sprite.y -100):
print("hit")
# Sprite.x and y are the position of the rect, not the whole image
This works but there is some way to simplify it? Also Im thinking how to get rid of the 100 pixels
If you want to use a scaled rect for the collision detection, you can inflate your original rect (or create a new one) and assign it as a separate attribute to your sprite (I call it hitbox here). The original rect will only be used to store the position.
Create a custom collision detection function which has to be passed as the collided argument to pygame.sprite.spritecollide or groupcollide. In this function you can use the colliderect method of the hitbox rect to check if it collides with the rect of the other sprite.
def hitbox_collision(sprite1, sprite2):
"""Check if the hitbox of the first sprite collides with the
rect of the second sprite.
`spritecollide` will pass the player object as `sprite1`
and the sprites in the enemies group as `sprite2`.
"""
return sprite1.hitbox.colliderect(sprite2.rect)
Then call spritecollide in this way:
collided_sprites = pg.sprite.spritecollide(
player, enemies, False, collided=hitbox_collision)
Here's a complete example (the red rect is the hitbox and the green rect the self.rect which is used as the blit position):
import pygame as pg
class Player(pg.sprite.Sprite):
def __init__(self, pos, *groups):
super().__init__(*groups)
self.image = pg.Surface((30, 50))
self.image.fill(pg.Color('dodgerblue1'))
self.rect = self.image.get_rect(center=pos)
# Give the sprite another rect for the collision detection.
# Scale it to the desired size.
self.hitbox = self.rect.inflate(100, 100)
def update(self):
# Update the position of the hitbox.
self.hitbox.center = self.rect.center
class Enemy(pg.sprite.Sprite):
def __init__(self, pos, *groups):
super().__init__(*groups)
self.image = pg.Surface((30, 50))
self.image.fill(pg.Color('sienna1'))
self.rect = self.image.get_rect(center=pos)
def hitbox_collision(sprite1, sprite2):
"""Check if the hitbox of the first sprite collides with the
rect of the second sprite.
`spritecollide` will pass the player object as `sprite1`
and the sprites in the enemies group as `sprite2`.
"""
return sprite1.hitbox.colliderect(sprite2.rect)
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
enemies = pg.sprite.Group()
player = Player((100, 300), all_sprites)
enemy = Enemy((320, 240), all_sprites, enemies)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEMOTION:
player.rect.center = event.pos
all_sprites.update()
collided_sprites = pg.sprite.spritecollide(
player, enemies, False, collided=hitbox_collision)
for enemy_sprite in collided_sprites:
print(enemy_sprite)
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.draw.rect(screen, (0, 255, 0), player.rect, 1)
pg.draw.rect(screen, (255, 0, 0), player.hitbox, 1)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
If you want a circular collision area, you can just pass the pygame.sprite.collide_circle function as the collided argument.
Give your sprites a self.radius attribute,
class Player(pg.sprite.Sprite):
def __init__(self, pos, *groups):
super().__init__(*groups)
self.radius = 100
and in the main loop pass collide_circle to spritecollide.
collided_sprites = pg.sprite.spritecollide(
player, enemies, False, collided=pg.sprite.collide_circle)
So, there's a couple things here. Starting with your second question (as I understand it from how you wrote it), you're correct to want to get rid of the 100 pixels. Having "100" randomly in your code is what's known as a Magic Number/Constant and is bad programming style for a few reasons:
There is no indication anywhere what that 100 is supposed to represent
In many cases (and yours specifically) Magic Numbers get replicated in multiple places. When that number changes (9-out-of-10 times, it does), you'll have to dig through all the code as well as related scripts to update the number and hope you didn't miss it anywhere
Related to 2, you may find out that you want that number (in this case, the hitbox/aura radius) to change often. Hardcoding it makes that difficult or impossible.
Chances are, this section of code is better off as its own function anyway, so you can simply have "hitradius" as a keyword parameter and then substitute "hitradius" for 100.
Either alongside or in place of this, you can track hitradius as an attribute of another object (i.e., the player or other sprite) and then substitute it in the same manner (e.g.- sprite.x - player.hitradius > player.x [etc]). If you're sure that it's never going to change, then have it as an accessible variable somewhere and use the same technique.
def check_aura_collision(player,sprite,hitradius = 100):
""" Checks if the player sprite is within hitradius (default 100)
pixels of another sprite.
"""
if (sprite.x + hitradius > player.x > sprite.x - hitradius )\
and (sprite.y + hitradius > player.y > sprite.y - hitradius ):
print("hit")
return True
return False
As for simplifying the logic, it's simple enough already; I might have used absolute value instead, but that's not really a meaningful change. Conversely, however, you may be interested in making it slightly more complex by using some basic trig to check a circular aura instead of the square aura you have right now.
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!