Can't do projectile stuff in pygame correctly - python

Trying to enable shooting projectiles with pygame. After I hit space button it shoots only 1 projectile, fps drops from 100 to ~30, player character can't move and does not react to button presses. After projectile stops being animated fps returns back to 100 and I can move&shoot up to 3 projectiles (as planned) but fps drops again and I can't move until the last projectile reaches the border.
I had a bullet image being loaded instead of drawing bullet before and thought that the case was in it. But the result didn't change. I also tried re-ordering blit() functions but nothing changed as well.
class Game():
# Some game stuff and functions
def __init__(self):
self.x_default = 800
self.y_default = 600
self.screen = pygame.display.set_mode((self.x_default, self.y_default))
#self.background = pygame.image.load('IMGs/BCK2.bmp').convert()
self.title = 'Title'
self.fps = 100
class Projectile():
# This class is supposed to make projectiles
def __init__(self, x, y, speed_x=5, direction=1, width=10, height=10):
# x&y - starting positions of projectiles
# speed_x & speed_y - starting moving speeds of projectiles in # oX & oY
# a - speed increase over time (cancelled for now)
# direction - stays for its' name
# Directions: [2, 3, 1, 4, 5, 6, -1, 7] starting from 0:00 and moving towards the right side of the clock
self.x = x
self.y = y
self.speed_x = speed_x
self.direction = direction
self.width = width
self.height = height
self.img = pygame.Rect(self.x, self.y, self.width, self.height)
def move_projectile(self, x, speed_x, direction):
# Moves stuff
# For now direction works only in right-left (1 for right, -1 for left)
x += speed_x * direction
return x
while 1:
if pygame.event.poll().type == pygame.QUIT:
sys.exit()
for pr in projectiles:
if (pr.x >= -pr.width) and (pr.x <= Game().x_default):
pr.x = pr.move_projectile(x=pr.x, speed_x=pr.speed_x, direction=pr.direction)
else:
projectiles.remove(pr)
keys = pygame.key.get_pressed()
# ------------ oX moving --------------
if keys[pygame.K_LEFT]:
P.x -= P.speed
P.direction = -1
if keys[pygame.K_RIGHT]:
P.x += P.speed
P.direction = 1
# ------------ Shooting ----------------
if keys[pygame.K_SPACE]:
if len(projectiles) < 3:
if P.direction > 0:
projectiles.append(Projectile(x=P.x + P.width, y=P.y + P.height//2, direction=1))
else:
projectiles.append(Projectile(x=P.x, y=P.y + P.height//2, direction=-1))
# ---------- The blitting process --------------
G.screen.blit(G.background, (0, 0))
G.screen.blit(Block1.img, (Block1.x, Block1.y))
G.screen.blit(Block2.img, (Block2.x, Block2.y))
for pr in projectiles:
pygame.draw.rect(G.screen, (0, 0, 0), pr.img)
G.screen.blit(P.img, (P.x, P.y))
pygame.display.update()
pygame.time.delay(1000//G.fps)

See How can i shoot a bullet with space bar? and How do I stop more than 1 bullet firing at once?
keys[pygame.K_SPACE] is True as long SPACE is hold. This means multiple bullets are continuously generated as long the key is hold.
If you want to create a single bullet, when SPACE is pressed, then use the KEYDOWN event (see pygame.event).
The event only occurs once, for each time when the key is pressed. e.g.:
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
if len(projectiles) < 3:
if P.direction > 0:
projectiles.append(Projectile(x=P.x + P.width, y=P.y + P.height//2, direction=1))
else:
projectiles.append(Projectile(x=P.x, y=P.y + P.height//2, direction=-1))
keys = pygame.key.get_pressed()
# [...]

Related

Platfromer on pygame: string has no attribute 'get_rect' while trying to make border for game

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.

Moving bullet in Pygame

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?

how to make object keep moving up

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.

How to make enemy follow player? Pygame

I know this has been asked before but I can't get it to work with my code.
I think this is where to have the enemy follow the player should be. I'm not sure how exactly to implement it.
I want the enemies to follow the player and when they run into the player the game ends. I also want this to be able to work with multiple enemies as well.
# player class
class player:
def __init__(self, x, y, w, h, xChange, yChange, vel):
self.x = x # plater x value
self.y = y # player y value
self.w = w # player w (width)
self.h = h # player h (height)
self.xChange = xChange # player xChange (to add to x value to move player horizontally)
self.yChange = yChange # player yChange (to aad to y value to move player vertically)
self.vel = vel # velocity of player (needed for collision)
# enemy class
class enemy:
def __init__(self, x, y, w, h):
self.x = x # enemy x value
self.y = y # enemy y value
self.w = w # enemy w (width) value
self.h = h # enemy h (height) value
# ----------------------------------------------------------------
"""enemy's x value (random value) (we pick 750 because the enemy width is 50 and
the screen width is 800. If we set the random value to 800, then the enemy has a
chance of spawning outside of the screen.)"""
enemyX = random.randint(0, 700)
"""enemy's y value (random value) (we pick 540 because the enemy height is 60
and the screen height is 600. If we set the random value to 600, the enemy has a
chance of spawning outside of the screen.)"""
enemyY = random.randint(0, 540) # enemy's y value
score = 0 # score set to 0. Will update in while loop.
rec = player(50, 50, 24, 32, 0, 0, 5) # the player's values (x, y, w, h, xChange, yChange, vel)
redRec = enemy(enemyX, enemyY, 24, 32) # the enemy's values (x, y, w, h)
# mainloop #
def mainloop():
global running, score, intro, sprite, next_zombie_time
while running:
"""keeps filling window with the background image"""
window.blit(background, (0, 0))
pygame.time.delay(25) # delay
for event in pygame.event.get(): # for every event in game
if event.type == pygame.QUIT: # if I exit the game
quitGame()
if event.type == pygame.KEYUP: # if any keys are let go
if event.key == pygame.K_a: # if key a
rec.xChange = 0 # set xChange to 0 (stop moving rec)
if event.key == pygame.K_d: # if key d
rec.xChange = 0 # set xChange to 0 (stop moving rec)
if event.key == pygame.K_w: # if key w
rec.yChange = 0 # set xChange to 0 (stop moving rec)
if event.key == pygame.K_s: # if key s
rec.yChange = 0 # set xChange to 0 (stop moving rec)
if event.type == pygame.KEYDOWN: # if any keys are pressed
if event.key == pygame.K_F4: # if key F4
pygame.quit() # set running to false
if event.key == pygame.K_a: # if key a
rec.xChange += -5 # add -5 to xChange (move rec left)
sprite = spriteLeft
if event.key == pygame.K_d: # if key a
rec.xChange += 5 # adds 5 to xChange (move rec right)
sprite = spriteRight
if event.key == pygame.K_w: # if key a
#adds -5 to yChange (moves rec up). Yes, this is supposed to say up.
rec.yChange += -5
sprite = spriteUp
if event.key == pygame.K_s: # if key a
# adds 5 to yChange (moves rec down). Yes, this is supposed to say down.
rec.yChange += 5
sprite = spriteDown
# pause key to pause game
if event.key == pygame.K_o: # if key o
running = False # set running to false
intro = False # intro set to False
pauseMenu() # pauseMenu is called
rec.x += rec.xChange # add rec's xChange to x (to do the moving)
rec.y += rec.yChange # adds rec's yChange to y (to do the moving)
# ----------------BOUNDARIES------------------------------
if rec.x <= 0: # if rec's x is less than or equal to 0 (if tries to escape screen)
rec.x = 0 # rec's x is set to 0 so it won't go off screen.
"""(we pick 750 because the player width is 50 and the screen width is 800.
If we set it to 800, then the player can go outside of screen."""
if rec.x >= 750: # if rec's x is greater than or equal to 750 (if tries to escape screen)
rec.x = 750 # set rec's x to 750 so it won't go off screen
if rec.y <= 0: # if rec's y is less than or equal to 0 (if tries to escape screen)
rec.y = 0 # set rec's y to 0 so it won't go off screen
"""we pick 540 because the player height is 60 and the screen height is 600.
If we set it to 600, then the player can go outside of screen"""
if rec.y >= 540: # if rec'y is greater than or equal to 540 (if tries to escape screen)
rec.y = 540 # set rec's y to 540 so it won't go off screen
#enemy.update(delta_time, player)
collisions = detCollision(rec.x, rec.y, rec.w, rec.h, redRec.x, redRec.y, redRec.w, redRec.h)
# activate the redrawWin function
redrawWin(collisions)
You need a few peices of information to do this. Firstly, you will need the distance between the player and the enemy. You can work out the distance using math.hypot function like this distance = (math.hypot(enemy.x - player.x, enemy.y - player.y) ). Then you would want to work out the angle between them in radians by using math.atan2 funciton. Note that this function takes y positional argument first. You can use this as follows
angle_radians = (math.atan2(enemy.y - player.y , enemy.x - player.x))
Now to get the enemy to move in the direction of the player, you can do this
enemy.y += math.sin(angle_radians)
enemy.x += math.cos(angle_radians)
See why this works http://setosa.io/ev/sine-and-cosine/.
You can even add a range on how close you want the player to be to the enemy when it starts following by setting a condition on distance between them like this.
if distance < range:
You can also control the speed by multiplying both the sin and cosine by the same number and this works because they are ratios.It can looks something like this
enemy.y += math.sin(angle_radians) * speed # Note both speeds must be the same nnumber
enemy.x += math.cos(angle_radians) * speed
For the last part of your question, if you want this to work for all the enemies, i would recommend adding your game objects to a list. For exmple, you can add enemies to a list
and make them follow the player. Lets say you make a list of all the enemies by doing this
all_enemies = []
for i in range(number of enemies you want):
all_enemies.append(enemy())
You could have an end result that looks something like this:
def Follow_player(self):
for e in all_enemies:
distance = (math.hypot(e.x - player.x, e.y - player.y) )
angle_radians = (math.atan2(e.y - player.y , e.x - player.x))
e.y += math.sin(angle_radians)
e.x += math.cos(angle_radians)
if distance < 1:
# they have collided
EDIT
Here's a link to a very good youtube video that describes all of this
https://www.youtube.com/watch?v=DVYDkHdsTIM

Collision Between two sprites - Python 3.5.2

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!

Categories

Resources