How Can I Make This Object Shoot Projectiles? - python

I have an ice object my question is how can I make it shoot projectiles or make projectiles come out of it 1 by 1
for example: how could I make it shoot projectiles from its tip and it keeps falling tell the end of the screen
this is my ice object class
smallice = pygame.image.load("fals.png")
class smallice:
def __init__(self,x,y,height,width,color):
self.x = x
self.y = y
self.height = height
self.width = width
self.color = color
self.smallice = pygame.image.load("fals.png")
self.smallice = pygame.transform.scale(self.smallice,(self.smallice.get_width()-2,self.smallice.get_height()-2))
self.rect = pygame.Rect(x,y,height,width)
# the hitbox our projectiles will be colliding with
self.hitbox = (self.x + 0, self.y + 1, 10, 72)
def draw(self):
self.rect.topleft = (self.x,self.y)
player_rect = self.smallice.get_rect(center = self.rect.center)
player_rect.centerx += 0 # 10 is just an example
player_rect.centery += 70 # 15 is just an example
window.blit(self.smallice, player_rect)
# define the small ices
black = (0,0,0)
smallice1 = smallice(550,215,20,20,black)
small = [smallice1]

You can start by making a Bullet class, for example, something like this:
class Bullet():
def __init__(self, x, y):
self.x = x
self.y = y
self.v = 5
def fire_bullet(self,window):
pg.draw.circle(window, (255,0,0), (self.x,self.y), 5)
ice.fire = False
def collision_box(self):
collide = pg.Rect(self.x-offset_x, self.y-offset_y, size_x, size_y)
return collide
Bullets will be simple circles, self.v stands for velocity. And collision_box will be use to detect the possible collision.
Now in your main_loop you need to detect when the ice object "wants to fire", simple example:
if keys[pg.K_SPACE]:
if len(bullets) < 2:
ice.fire = True
And:
if ice.fire:
bullet = Bullet(round(y.x+offset), round(player.y+offset))
bullets.append(bullet)
Now len(bullets) appeared, bullets is a list in which you will add a bullet object when the bullet is fired and remove it when the collision is detected or bullet goes outside of the chosen area. With this you can control the number of the bullets on the screen and also loop through it and call collision() method to see if one (or more) of them had collided, and keep track if it is still on the screen.
And if you want to shoot randomly then here is one basic idea:
if round(pg.time.get_ticks()/1000) % 3 == 0:
ice.fire = True
The last part, going through the list, and some simple logic:
if bullets != []: #Firing bullets
for bullet in bullets:
bullet.fire_bullet(window)
if bullet.y < screen_y: #Bullet movement
bullet.x += bullet.v
else:
bullets.pop(bullets.index(bullet)) #Poping bullets from list
if bullet.collision_box().colliderect(other_rect_object): #Bullet collision with other object
bullets.pop(bullets.index(bullet))
I am assuming (from the screenshot) that you want to shoot bullets down and only (vertically) hence the bullet.y += bullet.v and no direction flag.
This is simple code and idea, and ofcourse there is a lot room for improvement, but the approach works.
I hope my answer will help you.

Related

Why are my colliders so unruly and unaccurate? And how can I improve them?

I am having the issue that, my colliders aren't accurate. No matter how I tweak the numbers, they wont work. The eagle, the birds collider is accurate, but the collider of the box/obstacle isn't. The rectangle I use as a collider is the same size as the image of the box, and should be centered on it, but for some reason, it isn't.
import pygame as pg # imports life juice
eagleImage = pg.image.load("playerflappybirdscaled.png") # loads fucking eagle image
eagleCollider = pg.Surface.get_bounding_rect(eagleImage) # better collider
boxImage = pg.image.load("boxcollider.png") # box image go load
boxCollider = pg.Surface.get_bounding_rect(boxImage) # makes box rect
class Box(object): # makes box
def __init__(self, x, y):
self.image = boxImage # image of a box
self.collider = boxCollider # collider for said box
self.x = x # box xpos
self.y = y # box ypos
self.collider.center = self.x, self.y
def checkCollision(self, rect): # checks for collision
self.collider.center = self.x, self.y # sets collider
colliderToCheckFor = rect # gets other collider
collision = boxCollider.colliderect(colliderToCheckFor) # checks for collision
if collision: # returns car crash
return True
else:
return False
def draw(self, surface):
surface.blit(self.image, (self.x, self.y))
class Bird(object): # bird go flyyyyyyyyyyy
def __init__(self):
self.image = eagleImage # bird go display
self.collider = eagleCollider # bird gets collider
self.x = 320 # bird posx
self.y = 200 # bird posy
def key_handler(self, moveDist, direction): # manhandles keys
key = pg.key.get_pressed() # gets manhandled keys
dist = 2 # distance in FREEDOM UNITS jk
if key[pg.K_UP]: # key go up
self.y -= dist
elif key[pg.K_SPACE]: # key go up
self.y -= dist
elif key[pg.K_w]: # key go up
self.y -= dist
elif key[pg.K_a]: # key go left
self.x -= dist
if moveDist == 0: # checks if distance moved is wanted
return # if not returns
else:
if direction == "y": # checks direction wanted
self.y += moveDist # does shit
elif direction == "x":
self.x += moveDist
def draw(self, surface): # toddler draws image
surface.blit(self.image, (self.x, self.y))
self.collider.center = self.x, self.y
pg.init() # starts pygame
s = pg.display.set_mode((640, 400)) # sets screen
box = Box(320, 50)
bird = Bird() # biiiiiird
clock = pg.time.Clock() # clock for fps and shit
running = True # if runs
while running: # when runs
event = pg.event.poll() # get event
if event.type == pg.QUIT: # if not run go quit
pg.quit()
running = False
break
bird.key_handler(1, "y") # bird mauls keys
s.fill((255, 0, 0)) # give screen color
box.draw(s)
bird.draw(s) # take drawing from toddler
pg.display.update() # put drawing on display
if box.checkCollision(eagleCollider) == True:
running = False
pg.quit()
clock.tick(60) # tick tack
So as I have said, I have tried tweaking every number, and I see no reason why the colliders wouldn't be accurate.
Edit to highlight the difference between questions this one has been associated with. I have actually followed and implemented answers from one of these questions to the best of my ability, and as far as I can tell, I have followed what was answered there.
See Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?.
The instruction surface.blit(self.image, (self.x, self.y)) draws self.image at the topleft position (self.x, self.y):
class Box(object): # makes box
def __init__(self, x, y):
self.image = boxImage # image of a box
self.rect = self.image.get_rect(topleft = (x, y))
self.collider = self.image.get_bounding_rect()
self.collider.x += x
self.collider.y += y
def checkCollision(self, rect): # checks for collision
collision = self.collider.colliderect(rect)
return collision
def draw(self, surface):
surface.blit(self.image, self.rect)

Send obstacles from right side of screen in Pygame

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.

Play animation upon collision with enemy

so I have loaded the images into pygame:
Explosion = [pygame.image.load('Explosion1.png'), pygame.image.load('Explosion2.png'), pygame.image.load('Explosion3.png'), pygame.image.load('Explosion4.png'), pygame.image.load('Explosion5.png'), pygame.image.load('Explosion6.png'), pygame.image.load('Explosion7.png'), pygame.image.load('Explosion8.png'), pygame.image.load('Explosion9.png'), pygame.image.load('Explosion10.png')]
and I would like, when the bullet, which is a separate class, makes a collision with the missile, it plays this animation at the position where both the bullet and the enemy collide, I'm not sure how I go around doing this?
Collision Script (In the main loop):
hits = pygame.sprite.groupcollide(enemies, bullets, True, True)
Bullet Class:
class Bullet (pygame.sprite.Sprite):
def __init__ (self, x, y):
super (Bullet, self).__init__()
self.surf = pygame.image.load("Bullet.png").convert()
self.surf.set_colorkey((255,255,255), RLEACCEL)
self.rect = self.surf.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedx = bullet_speed
def update(self):
self.rect.x += self.speedx
if self.rect.left > SCREEN_WIDTH:
self.kill()
Enemy Class:
class Enemy(pygame.sprite.Sprite):
def __init__(self):
super(Enemy, self).__init__()
self.surf = pygame.image.load("Missiles.png").convert()
self.surf.set_colorkey((255,255,255), RLEACCEL)
self.rect = self.surf.get_rect(
center=(
random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
random.randint(0, SCREEN_HEIGHT),
)
)
self.speed = random.randint(Enemy_SPEED_Min, Enemy_SPEED_Max)
def update(self):
self.rect.move_ip(-self.speed, 0)
if self.rect.right < 0:
self.kill()
all of the code is here https://pastebin.com/CG2C6Bkc if you need it!
thank you in advance!
Do not destroy the enemies, when it collides with an bullet. Iterate through the enemies and which are returned in hits and start the explosion animation instead of killing the enemies:
hits = pygame.sprite.groupcollide(enemies, bullets, True, True)
hits = pygame.sprite.groupcollide(enemies, bullets, False, True)
for enemy in hits:
enemy.start_animation()
Add the attributes animation_count, animation_frames and animation to th class Enemy. When the explosion animation is started then animation is set True. animation_frames controls the speed of the animation. If the animation is started, then the enemy stops to move and the Explosion image is shown instead of the enemy. After the last image of the animation is shown, the enemy is killed:
class Enemy(pygame.sprite.Sprite):
def __init__(self):
super(Enemy, self).__init__()
self.surf = pygame.image.load("Missiles.png").convert()
self.surf.set_colorkey((255,255,255), RLEACCEL)
self.rect = self.surf.get_rect(
center=(
random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
random.randint(0, SCREEN_HEIGHT),
)
)
self.speed = random.randint(Enemy_SPEED_Min, Enemy_SPEED_Max)
self.animation_count = 0
self.animation_frames = 10
self.animation = False
def start_animation(self):
self.animation = True
def update(self):
if self.animation:
image_index = self.animation_count // self.animation_frames
self.animation_count += 1
if image_index < len(Explosion):
self.surf = Explosion[image_index]
self.rect = self.surf.get_rect(center = self.rect.center)
else:
self.kill()
self.rect.move_ip(-self.speed, 0)
if self.rect.right < 0:
self.kill()
Well i dont want to do it for you, But i will say one way you could do it, then you can go and try to do it, and if you run into a problem/error, you can update the question and we can help you.
I would create another class called Explosion and and give it the images, when when you draw it, draw the first image, then change to the second, then draw that one, then change...
Then after it finishes, destroy the object.
So, create the class, create a spritegroup, when the bullet collides, create a new instance of the class, giving it the x and y co ordinates of the collision, update it every frame, and it will draw all the images then destroy itself
also, an easier way to get all of your images instead of typing them all out is
Explosion_imgs = [pygame.image.load("explosion" + str(x) + ".png") for x in range(1,11,1)]

collision between moving sprites in pygame

I have two moving sprites, one is called wall and it just moves up and down; the other is called Player and it bounces around the screen and changes direction whenever it hit an obstacles.
I got this to work to a degree: The player ball does bounce around but sometimes there is a bug where the ball seems to be confused about the movement and just wobbles back and forth when colliding with the wall. The faster I set the speed of the player ball or the movement of the wall, the more frequent the bug becomes.
Here is the code for the project:
import pygame, sys
# The class that creates the player ball; it does not take user input but just bounces around
class Player(pygame.sprite.Sprite):
def __init__(self, color, width, height,speed_x,speed_y):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(color)
self.rect = self.image.get_rect()
self.speed_x = speed_x
self.speed_y = speed_y
def update(self,collision_group):
self.rect.x += self.speed_x
self.rect.y += self.speed_y
# code to get a bounce when the ball hits the edge of the screen
if self.rect.right >= screen_width or self.rect.left <= 0:
self.speed_x *= -1
if self.rect.bottom >= screen_height or self.rect.top <= 0:
self.speed_y *= -1
# Code to get a bounce when the player collides with the wall
collide_object = pygame.sprite.spritecollide(self,collision_group,False)
if collide_object:
if collide_object[0].rect.collidepoint(self.rect.midbottom) or collide_object[0].rect.collidepoint(self.rect.midtop):
self.speed_y *= -1
if collide_object[0].rect.collidepoint(self.rect.midright) or collide_object[0].rect.collidepoint(self.rect.midleft):
self.speed_x *= -1
# Code for the wall, it just moves up and down
class Wall(pygame.sprite.Sprite):
def __init__(self,width,height,pos_x,pos_y,color):
super().__init__()
self.image = pygame.Surface([width,height])
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.x = pos_x
self.rect.y = pos_y
self.speed = 5
def update(self):
self.rect.y += self.speed
if self.rect.bottom >= screen_height:
self.speed *= -1
if self.rect.top <= 0:
self.speed *= -1
# General setup
pygame.init()
clock = pygame.time.Clock()
# Game Screen
screen_width = 800
screen_height = 800
screen = pygame.display.set_mode((screen_width,screen_height))
pygame.display.set_caption("Side by side collision")
# Creating the sprites and groups
moving_sprites = pygame.sprite.Group()
player = Player((255,0,0),20,20,8,8)
moving_sprites.add(player)
wall_sprites = pygame.sprite.Group()
center_wall = Wall(500,200,300,300,(255,255,0))
wall_sprites.add(center_wall)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Drawing
screen.fill((0,0,0))
wall_sprites.draw(screen)
wall_sprites.update()
moving_sprites.draw(screen)
moving_sprites.update(wall_sprites)
pygame.display.flip()
clock.tick(60)
many thanks in advance :)
You can realize that your code works perfectly if the the wall is moving away from the player when they collide. The problem occurs when they are moving towards each other. This is because your player is actually goes inside the wall, you detect a collision, reverse the speed of the player and at the next iteration your player is still inside the wall (because wall is moving towards your player, the player couldn't find enough time to escape it) and you detect a collision again, reverse the speed of the player...
You can modify your code like this to solve the problem:
if collide_object:
rect = collide_object[0].rect
if rect.collidepoint(self.rect.midbottom): # if we hit from bottom
if self.speed_y > 0: # and the player was moving down
self.speed_y *= -1 # go up
elif rect.collidepoint(self.rect.midtop): # if we hit from top
if self.speed_y < 0: # and the player was moving up
self.speed_y *= -1 # go down

Image doesn't move in Pygame

Please, My Pygame image doesn't move but when I change the code to print and I press the arrow keys, it does work. Any help would be greatly appreciated. I have imported OS, random and pygame in my original code as well as set width and height to 800, 600
class Alien(pygame.sprite.Sprite):
def __init__(self,speed, size):
self.x = random.randrange(0,width)
self.y = random.randrange(-200, -20)
self.size = random.randint(3, 20)
self.speed = speed
def move_down(self):
self.y += self.speed
def draw(self, screen, colour):
pygame.draw.circle(screen, colour, (self.x, self.y), self.size)
def check_landed(self):
if self.y > height:
self.x = random.randrange(0,width)
self.y = random.randrange(-400, -20)
self.speed = random.randrange(1,4)
class Actor(Alien):
def __init__(self, filename):
self.score = 0
self.image = pygame.image.load(os.path.join("../images", filename)).convert_alpha()
self.rect = self.image.get_rect()
def move(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] or keys[pygame.K_a]:
self.rect.centerx -= 10
print("let")
elif keys[pygame.K_RIGHT] or keys[pygame.K_d]:
self.rect.centerx += 10
def draw(self, screen):
screen.blit(self.image, (width/2, height - 40))
In the main game implementation, i have
enemies = []
actor = Actor("ship.jpg")
for i in range (20):
aliens = Alien(2, 4)
enemies.append(aliens)
done = False
while done == False:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(white)
actor.x = 500
actor.y = 500
actor.draw(screen)
actor.move()
for i in range(len(enemies)):
enemies[i].draw(screen, red)
enemies[i].move_down()
enemies[i].check_landed()
pygame.display.flip()
clock.tick(30)
You've overriden the Sprite.draw method.
The normal method, as the docs say, "uses the Sprite.image attribute for the source surface, and Sprite.rect for the position".
In your Alien subclass, you aren't using that, but you're instead using the x and y attributes, which might also work, because that's what you're modifying everywhere:
def draw(self, screen, colour):
pygame.draw.circle(screen, colour, (self.x, self.y), self.size)
But your Actor subclass doesn't use either, it just blits to screen at a fixed location of (width/2, height - 40):
def draw(self, screen):
screen.blit(self.image, (width/2, height - 40))
So, no matter what attributes you change, you're always going to draw at the same position.
I'm not sure why you're overriding draw for either of these classes. I'm also not sure why Alien inherits from Sprite but then uses x and y attributes while ignoring rect, while Actor inherits from Alien but then ignores x and y while using rect again. This is almost certainly going to confuse you. But if you want the smallest possible change:
screen.blit(self.image, self.rect)
Of course you'll also need to set up self.rect to the initial starting position at startup, and prevent the user from going all the way off the edge of the screen, and so on. And, again, it would be better to clean this up to not be confusing in the first place. But this should get you started.

Categories

Resources