Pygame how to set a certain delay on automatic shooting - python

I'm trying to create a space shooting game the problem is I need to set a certain delay in the game loop so that the shooting can be controlled. If possible if the delay can be stored in a variable so that I can control it while in game thank you. Here's the code
When you use the spacebar to shoot, it already have the delay but I'm guessing that just is because of the keyboard delay because it's detected when it's pressed down
class Player(pygame.sprite.Sprite):
........
def shoot(self):
bullet = Bullet(self.rect.centerx, self.rect.top)
all_sprites.add(bullet)
bullets.add(bullet)
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((10, 20))
self.image.fill(RED)
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedy = -10
def update(self):
self.rect.y += self.speedy
# kill if it moves off the top of the screen
if self.rect.bottom < 0:
self.kill()
#Sprites
all_sprites = pygame.sprite.Group()
bullets = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
running = True
while running:
player.shoot()

create a variable for example shootTime and iterate it (add one) every loop, then only call the shoot function when the variable reaches twenty. once you call the shoot variable then set the variable back to zero
all_sprites = pygame.sprite.Group()
bullets = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
shootTime = 0
running = True
while running:
shootTime += 1
if shootTime == 20:
player.shoot()
shootTime = 0
you can change the number 20 in if shootTime == 20 to change the length between shots

Related

Python pygame movement question about classes

I am trying to implement movement from my enemy class to follow the player class. Currently, the enemy class only moves in a straight line to the left. Here is some code to better understand my question.
Here is the player class
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((50, 40))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.left = 25
self.rect.bottom = HEIGHT / 2
self.speedx = 0
self.speedy = 0
Here is the enemy class
class Enemy(pygame.sprite.Sprite):
def __init__(self2):
pygame.sprite.Sprite.__init__(self2)
self2.image = pygame.Surface((50, 40))
self2.image.fill(RED)
self2.rect = self2.image.get_rect()
self2.rect.x = random.randrange(680, 750)
self2.rect.y = random.randrange(HEIGHT - self2.rect.height)
self2.speedx = random.randrange(-5, -3)
self2.speedy = random.randrange(-5, 5)
self2.min_dist = 200
def update(self2):
self2.speedy = 0
self2.rect.x += self2.speedx
self2.rect.y += self2.speedy
if self2.rect.left < -25 or self2.rect.top < -25 or self2.rect.bottom > HEIGHT + 10:
self2.rect.x = random.randrange(680, 750)
self2.rect.y = random.randrange(HEIGHT - self2.rect.height)
self2.speedx = random.randrange(-5, -3)
def move_towards_Player(self2, Player):
delta_x = Player.rect.x - self2.rect.x
delta_y = Player.rect.y - self2.rect.y
if abs(delta_x) <= self2.min_dist and abs(delta_y) <= self2.min_dist:
enemy_move_x = abs(delta_x) > abs(delta_y)
if abs(delta_x) > self2.speedx and abs(delta_x) > self2.speedx:
enemy_move_x = random.random() < 0.5
if enemy_move_x:
self2.rect.x += min(delta_x, self2.speedx) if delta_x > 0 else max(delta_x, -self2.speedx)
else:
self2.rect.y += min(delta_y, self2.speedy) if delta_y > 0 else max(delta_y, -self2.speedy)
all_sprites = pygame.sprite.Group()
enemies = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
for i in range(3):
e = Enemy()
all_sprites.add(e)
enemies.add(e)
running = True
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
all_sprites.update()
#Draw render
screen.fill(BLACK)
all_sprites.draw(screen)
pygame.display.flip()
pygame.quit()
quit()
Sorry for the large post in code just would like to get the best answer for this.
Question 1) This is probably a very simple question but, how come when the all_sprites.Group get updated in the loop why doesn't the move_towards player get updated?
Question 2) I tried letting the update inherit Player, like this
def update(self2, Player):
why doesn't this work?
Question 3) How can I make the move_towards_Player get updated in the loop?
how come when the all_sprites.Group get updated in the loop why doesn't the move_towards player get updated?
You ask why the move_towards_Player function is not called? Because you never call it, and it isn't magically called by anything. The update function of Group will call the update function of all its sprites. Nothing more.
I tried letting the update inherit Player, like this ... why doesn't this work?
The update function of Group will pass all arguments to the update function of all its sprites. So you could call it like this:
all_sprites.update(player)
and ensure that all sprite classes have their update function take an extra argument beside self.
How can I make the move_towards_Player get updated in the loop?
Just call it from the Enemy's update function.
You could start with something like this:
import pygame
import random
GREEN=(0,255,0)
RED=(255,0,0)
BLACK=(0,0,0)
HEIGHT=600
WIDTH=800
FPS=120
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((50, 40))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.left = 25
self.rect.bottom = HEIGHT / 2
self.speedx = 0
self.speedy = 0
class Enemy(pygame.sprite.Sprite):
def __init__(self, target):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((50, 40))
self.image.fill(RED)
self.rect = self.image.get_rect()
self.rect.x = random.randrange(680, 750)
self.rect.y = random.randrange(HEIGHT - self.rect.height)
self.target = target
self.speed = random.randint(4, 6)
def update(self, dt):
self.move_towards_Player(dt)
def move_towards_Player(self, dt):
pos = pygame.Vector2(self.rect.center)
v = pygame.Vector2(self.target.rect.center) - pos
if (v.length() < 5):
self.kill()
else:
v.normalize_ip()
v *= self.speed * dt/10
pos += v
self.rect.center = round(pos.x), round(pos.y)
all_sprites = pygame.sprite.Group()
enemies = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
for i in range(3):
e = Enemy(player)
all_sprites.add(e)
enemies.add(e)
clock=pygame.time.Clock()
pygame.init()
screen=pygame.display.set_mode([WIDTH, HEIGHT])
running = True
while running:
dt=clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
all_sprites.update(dt)
#Draw render
screen.fill(BLACK)
all_sprites.draw(screen)
pygame.display.flip()
pygame.quit()
quit()
Note that:
I renamed self2 to self. By convention, the first argument of a method should be called self. Stick to it.
I pass the Player instance to the Enemy-class' __init__ function and store it in a class member. This way, we don't have to "pollute" the update function
I use pygame's Vector2 class to handle the math.
I pass the delta time to the update function so the movement speed is constant even when the framerate is not

pygame sprite not moving automatically with each sprite update

I'm trying to make a cut scene for a pygame that I'm building, and in this pygame I'm trying to get the main player sprite to spawn at the rightmost side of the screen, before slowly moving towards the center. For some reason, rather than moving, it just stays completely still, despite the fact that the same logics that I applied in the cutscene works completely fine in the main game when I'm using keypresses to move the player instead of an automatic update.
I basically just followed the same logic that I did in my main game except instead of moving as a result of a keypress, it moves as a result of a sprite update. This is my code for the sprite:
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((30, 60))
self.image = pygame.image.load("soldier.png").convert_alpha()
self.rect = self.image.get_rect()
self.rect.x = WIDTH - 30
self.rect.y = HEIGHT - 60
self.speedx = -5
self.speedy = 0
self.count = 0
tru = False
def update(self):
self.count += 1
if self.count > 10:
self.speedx -= 5
self.count = 0
else:
self.speedx = 0
if self.rect.x < WIDTH / 2:
self.speedx = 0
self.rect.x += self.speedx
And here is my main game loop
game_over = True
running = True
CurrentLevel = 1
while running:
all_sprites = pygame.sprite.Group()
curtime = pygame.time.get_ticks()
platforms = pygame.sprite.Group()
bullets = pygame.sprite.Group()
grounds = pygame.sprite.Group()
thentime = pygame.time.get_ticks()
for plat in PLATFORMS:
p = Platform(*plat)
all_sprites.add(p)
platforms.add(p)
for gro in GROUNDS:
g = Ground(*gro)
all_sprites.add(g)
grounds.add(g)
player = Player()
all_sprites.add(player)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_d:
running = False
all_sprites.update()
screen.fill(WHITE)
screen.blit(bg, [0, 0])
all_sprites.draw(screen)
pygame.display.flip()
This is what the outcome becomes:
Output
That player sprite at the far right side of the screen just remains there and doesn't move like it should.
Does anyone know whats going on?
Inside your while running loop, you have the line player = Player() and all_sprites.add(player). That means that each frame, your game is creating a new player object and instantiating it on the right side of the screen, so it never has a chance to move.

Make multiple enemy class the same but in different positions

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__()
#y = random.randint(300,1200)
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))
def update(self):
if self.rect.bottom <= 400:
self.rect.y += 1
if self.rect.bottom >= 400:
self.rect.y -= 300
On this part i got so that the enemy class moves downwards and when it reaches 400 it teleport 300 upwards but i wanted it so that it constantly moves upwards and then downwards again.
I thought that i either cancel the movement downwards when it reaches the position but i don't think you can do that.
clock = pygame.time.Clock() # A clock to limit the frame rate.
player = Player()
enemy = Enemy()
enemy_list = pygame.sprite.Group(enemy)
#enemy_list.add(enemy)
sprites = pygame.sprite.Group(player, enemy)
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()
The Enemy class needs an additional attribute that keeps track of the direction it's moving: up or down.
So a simple solution could look like this:
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 = 'DOWN'
def update(self):
self.rect.y += 1 if self.direction == 'DOWN' else -1
if self.rect.bottom >= 400:
self.direction = 'UP'
if self.rect.top <= 50:
self.direction = 'DOWN'

FIRE. Checking if a list of rectangles collides with a player rectangle

I know i need a loop to check if a list of bullets hits a player.
I've tried researching online for 2 hours for reference code but all use sprites and classes.
#Bullet Collision with players
for i in range(len(bullets_2)):
#player is a rectangle style object
if bullets_2[i].colliderect(player):
player_health -= 10
Sadly enough my computer science teacher hasn't taught the class about sprites or classes, so let's avoid that.
I tried having the code above check if the list collides with the rectangle player.
The point of the above code is for the game to to take health away from the health bar if the enemy player's bullets hit the player.
EDIT:
I only have about 8 days to finish this game
TLDR:
How do I check to see if a list collides with a rectangle.
As in the comment, here's the code.
Here's the images I am using
import pygame, time, sys
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((800,600))
clock = pygame.time.Clock()
class playerClass(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("player.png").convert()
self.image.set_alpha(196)
self.rect = self.image.get_rect()
self.rect.y = 260
self.rect.x = 500
def update(self):
pass
class bulletClass(pygame.sprite.Sprite):
def __init__(self, speed, starty):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("bullet.png").convert()
self.rect = self.image.get_rect()
self.rect.y = starty
self.rect.x = 0
self.speed = speed
def update(self):
self.rect.x += self.speed
gameExit = False
player = playerClass()
bullet1 = bulletClass(7,265)
bullet2 = bulletClass(10,295)
bullet3 = bulletClass(7,325)
bulletSprites = pygame.sprite.RenderPlain((bullet1, bullet2, bullet3))
playerSprites = pygame.sprite.RenderPlain((player))
bulletRectList = [bullet1.rect, bullet2.rect, bullet3.rect]
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
screen.fill((255,255,255))
bulletSprites.update()
bulletSprites.draw(screen)
playerSprites.update()
playerSprites.draw(screen)
collidedBulletList = player.rect.collidelistall(bulletRectList)
if len(collidedBulletList) > 0:
for i in collidedBulletList:
print(i)
pygame.display.update()
clock.tick(10)
In this code, if you want to add a bullet just declare a new object
bullet4 = bulletClass(speed, yCoordinateToBeginWith)
and append it to bulletSprites and the bulletRectList and you are done. Using sprites simplifies this. Making your code sprite friendly would be difficult at the beginning but appending it is definitely easy afterwards.

Pygame - sprite collision with sprite group

I have created two simple sprites in PyGame and one of them is an Umbrella, the other one is a rain drop.
The Raindrops are added into a sprite group called all_sprites. The Umbrella sprite has its own group called Umbrella_sprite
The raindrops are "falling" from top of the screen and if one of them touches the umbrella / collides with it.. the raindrop is supposed to be deleted. BUT instead of that specific raindrops all other are affected by this.
main file (rain.py)
#!/usr/bin/python
VERSION = "0.1"
import os, sys, raindrop
from os import path
try:
import pygame
from pygame.locals import *
except ImportError, err:
print 'Could not load module %s' % (err)
sys.exit(2)
# main variables
WIDTH, HEIGHT, FPS = 300, 300, 30
# initialize game
pygame.init()
screen = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption("Rain and Rain")
# background
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((40,44,52))
# blitting
screen.blit(background,(0,0))
pygame.display.flip()
# clock for FPS settings
clock = pygame.time.Clock()
def main():
all_sprites = pygame.sprite.Group()
umbrella_sprite = pygame.sprite.Group()
# a function to create new drops
def newDrop():
nd = raindrop.Raindrop()
all_sprites.add(nd)
# creating 10 rain drops
for x in range(0,9): newDrop()
# variable for main loop
running = True
# init umbrella
umb = raindrop.Umbrella()
# all_sprites.add(umb)
umbrella_sprite.add(umb)
# event loop
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
for enemy in all_sprites:
gets_hit = pygame.sprite.spritecollideany(umb, all_sprites)
if gets_hit:
all_sprites.remove(enemy)
screen.blit(background,(100,100))
# clear
all_sprites.clear(screen,background)
umbrella_sprite.clear(screen,background)
# update
all_sprites.update()
umbrella_sprite.update()
# draw
all_sprites.draw(screen)
umbrella_sprite.draw(screen)
# flip the table
pygame.display.flip()
pygame.quit()
if __name__ == '__main__':
main()
raindrop.py ( Raindrop() & Umbrella() )
import pygame
from pygame.locals import *
from os import path
from random import randint
from rain import HEIGHT, WIDTH
img_dir = path.join(path.dirname(__file__), 'img')
class Raindrop(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.width = randint(32, 64)
self.height = self.width + 33
self.image = pygame.image.load(path.join(img_dir, "raindrop.png")).convert_alpha()
self.image = pygame.transform.scale(self.image, (self.width, self.height))
self.speedy = randint(1, 15)
self.rect = self.image.get_rect()
self.rect.x = randint(0, 290)
self.rect.y = -self.height
def reset(self):
self.rect.y = -self.height
def update(self):
self.rect.y += self.speedy
if self.rect.y >= HEIGHT:
self.rect.y = -self.height
self.rect.x = randint(0, 290)
class Umbrella(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.width = 50
self.height = 50
self.image = pygame.image.load(path.join(img_dir,"umbrella.png")).convert_alpha()
self.image = pygame.transform.scale(self.image, (self.width, self.height))
self.speedx = 10
self.rect = self.image.get_rect()
self.rect.x = (WIDTH/2) - self.width
self.rect.y = (0.7 * HEIGHT)
def update(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and self.rect.x > 0:
self.rect.x -= self.speedx
elif keys[pygame.K_RIGHT] and self.rect.x < (WIDTH - self.width):
self.rect.x += self.speedx
This is your problem:
for enemy in all_sprites:
gets_hit = pygame.sprite.spritecollideany(umb, all_sprites)
if gets_hit:
all_sprites.remove(enemy)
You're looping through the group, and if any sprite collides, deleting all of them.
You don't need to loop through the group - the collision functions take care of that. You just need to use the spritecollide function, which compares a sprite versus a group. That function will return a list of collisions, as well as using the DOKILL flag to delete them automatically:
gets_hit = pygame.sprite.spritecollide(umb, all_sprites, True)
spritecollideany checks if the sprite collides with any sprite in the group and returns this sprite, so gets_hit is a trueish value as long as the collided sprite in the group is not removed and the if gets_hit: block gets executed. That means the code in the for loop simply keeps deleting every sprite in the group that appears before the collided sprite is reached and removed. A simple fix would be to check if the hit sprite is the enemy: if enemy == gets_hit:, but the code would still be inefficient, because spritecollideany has to loop over the all_sprites group again and again inside of the for loop.
I recommend to use spritecollide instead of spritecollideany as well, since it's more efficient and just one line of code.

Categories

Resources