anon_user123456
anon_user123456

Reputation: 69

How to control individual bullets in pygame

I am trying to create a 2D side shooting game. For bullets, I am using a list so everytime I shoot a bullet gets appended to that list and I "draw" the list on the screen. Whenever the bullet gets out of screen I delete that bullet off the list.

Currently, I have a feature where you can't change the direction of the bullet (an angle) whilst shooting (i.e when the bullet list is bigger than 0) because obviously I don't want people to control the direction of the bullet when they are out of the gun. However, that is not my intention because I want people to change the direction whilst shooting, just not the direction of the bullet that has already been shot out.

If that doesn't make sense, in order to forbid players magically changing the path or direction of the bullet that has already been shot out(Like using telekinesis to control the bullets and make them go in a circle), I forbid changing the direction whilst shooting at all. But that is not my intention, as I still want players to have control the direction of the bullet, just not the ones that has been shot out. Below is my code:`

import pygame
import math
pygame.init()

win = pygame.display.set_mode((500,480))

pygame.display.set_caption("First Game")

walkRight = [pygame.image.load('R1.png'), pygame.image.load('R2.png'), pygame.image.load('R3.png'), pygame.image.load('R4.png'), pygame.image.load('R5.png'), pygame.image.load('R6.png'), pygame.image.load('R7.png'), pygame.image.load('R8.png'), pygame.image.load('R9.png')]
walkLeft = [pygame.image.load('L1.png'), pygame.image.load('L2.png'), pygame.image.load('L3.png'), pygame.image.load('L4.png'), pygame.image.load('L5.png'), pygame.image.load('L6.png'), pygame.image.load('L7.png'), pygame.image.load('L8.png'), pygame.image.load('L9.png')]
bg = pygame.image.load('bg.jpg')
clock = pygame.time.Clock()
b_angle = 0

class player(object):
    def __init__(self,x,y,width,height):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.vel = 1.5
        self.isJump = False
        self.left = False
        self.right = False
        self.walkCount = 0
        self.jumpCount = 13
        self.standing = True
        #Boolean value which forbids players from changing the angle of bullet whilst firing
        self.isFiring = False

    def draw(self, win):
        if self.walkCount + 1 >= 45:
            self.walkCount = 0

        if not(self.standing):
            if self.left:
                win.blit(walkLeft[self.walkCount//5], (self.x,self.y))
                self.walkCount += 1
            elif self.right:
                win.blit(walkRight[self.walkCount//5], (self.x,self.y))
                self.walkCount +=1
        else:
            if self.right:
                win.blit(walkRight[0], (self.x, self.y))
            else:
                win.blit(walkLeft[0], (self.x, self.y))


#Bullet Class
class projectile(object):
    def __init__(self,x,y,radius,color,facing):
        self.x = x
        self.y = y
        self.radius = radius
        self.color = color
        self.facing = facing
        self.vel = 8

    def draw(self,win):
        pygame.draw.circle(win, self.color, (self.x,self.y), self.radius)


#Drawing everything on screen function
def redrawGameWindow():
    win.blit(bg, (0,0))
    man.draw(win)
    for bullet in bullets:
        bullet.draw(win)
    clock.tick(60)
    pygame.display.update()


#mainloop
man = player(200, 410, 64,64)
#List of bullets
bullets = []
run = True
previous_time = pygame.time.get_ticks()
while run:
    print(b_angle)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
#Adding bullets into the list
    for bullet in bullets:
        if bullet.x < 500 and bullet.x > 0 and bullet.y < 480 and bullet.y > 0:
            bullet.x += int((bullet.vel*math.cos(math.radians(b_angle))))
            bullet.y -= int((bullet.vel*math.sin(math.radians(b_angle))))
        else:
            bullets.pop(bullets.index(bullet))
    #Only allows changing direction when there are no bullets on screen.
            if len(bullets) == 0:
                man.isFiring = False

    keys = pygame.key.get_pressed()

#SPACE button, which shoot out the bullets (also a time delay).
    if keys[pygame.K_SPACE]:
        man.isFiring = True
        current_time = pygame.time.get_ticks()
        if man.left:
            facing = -1
        else:
            facing = 1
        if current_time - previous_time > 200:
            previous_time = current_time
            if len(bullets) < 10:
  #See when there are bullets and set Firing = True.
                man.isFiring = True
                bullets.append(projectile(round(man.x + man.width //2), round(man.y + man.height//2), 3, (0,0,0), facing))
#A and D keys control direction of bullet.
    if keys[pygame.K_a]:
        if not man.isFiring:
            b_angle += 3
    if keys[pygame.K_d]:
        if not man.isFiring:
            b_angle -= 3
    if keys[pygame.K_LEFT] and man.x > man.vel:
        man.x -= man.vel
        man.left = True
        man.right = False
        man.standing = False
    elif keys[pygame.K_RIGHT] and man.x < 500 - man.width - man.vel:
        man.x += man.vel
        man.right = True
        man.left = False
        man.standing = False
    else:
        man.standing = True
        man.walkCount = 0

    if not(man.isJump):
        if keys[pygame.K_UP]:
            man.isJump = True
            man.standing = False
            man.walkCount = 0
    else:
        if man.jumpCount >= -13:
            neg = 1
            if man.jumpCount < 0:
                neg = -1
            man.y -= (man.jumpCount ** 2) ** 0.35 * neg
            man.jumpCount -= 1
        else:
            man.isJump = False
            man.jumpCount = 13

    redrawGameWindow()

pygame.quit()

Thanks in advance!

Upvotes: 3

Views: 1259

Answers (2)

sloth
sloth

Reputation: 101072

You should restructure your code so that all logic related to the player is in the Player class, and all logic that is related to the projectiles is in the Projectile class.

Also, make use of pygame's Sprite class, which will help you organizing your code and keeping it simple.

Using some vector math will also simplify your code: when firing a projectile, create a vector describing the direction the projectile should move. Then, when moving the projectile, simply add that vector to the position of the projectile.

Here's a running example:

import pygame

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((32, 32))
        self.image.fill((0, 0, 0))
        self.image.set_colorkey((0, 0, 0))
        pygame.draw.polygon(self.image, pygame.Color('dodgerblue'), ((0, 0), (32, 16), (0, 32)))
        self.org_image = self.image.copy()
        self.angle = 0
        self.direction = pygame.Vector2(1, 0)
        self.rect = self.image.get_rect(center=(200, 200))
        self.pos = pygame.Vector2(self.rect.center)

    def update(self, events, dt):
        for e in events:
            if e.type == pygame.KEYDOWN:
                if e.key == pygame.K_SPACE:
                    self.groups()[0].add(Projectile(self.rect.center, self.direction.normalize()))
        pressed = pygame.key.get_pressed()
        if pressed[pygame.K_a]:
            self.angle += 3
        if pressed[pygame.K_d]:
            self.angle -= 3

        self.direction = pygame.Vector2(1, 0).rotate(-self.angle)
        self.image = pygame.transform.rotate(self.org_image, self.angle)
        self.rect = self.image.get_rect(center=self.rect.center)

class Projectile(pygame.sprite.Sprite):
    def __init__(self, pos, direction):
        super().__init__()
        self.image = pygame.Surface((8, 8))
        self.image.fill((0, 0, 0))
        self.image.set_colorkey((0, 0, 0))
        pygame.draw.circle(self.image, pygame.Color('orange'), (4, 4), 4)
        self.rect = self.image.get_rect(center=pos)
        self.direction = direction
        self.pos = pygame.Vector2(self.rect.center)

    def update(self, events, dt):
        self.pos += self.direction * dt
        self.rect.center = self.pos
        if not pygame.display.get_surface().get_rect().contains(self.rect):
            self.kill()

def main():
    pygame.init()
    screen = pygame.display.set_mode((500, 500))
    sprites = pygame.sprite.Group(Player())
    clock = pygame.time.Clock()
    dt = 0

    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return
        sprites.update(events, dt)
        screen.fill((30, 30, 30))
        sprites.draw(screen)
        pygame.display.update()
        dt = clock.tick(60)

if __name__ == '__main__':
    main()

enter image description here

Upvotes: 2

Kingsley
Kingsley

Reputation: 14906

One way to solve this is to store the direction in the projectile.

#Bullet Class
class projectile(object):
    def __init__(self, x, y, radius, color, facing, angle):
        self.x      = x
        self.y      = y
        self.radius = radius
        self.color  = color
        self.facing = facing
        self.vel    = 8
        self.angle  = angle   # <<== HERE

    def draw(self, win):
        pygame.draw.circle(win, self.color, (self.x, self.y), self.radius)

    # Compute the next position of the projectile, returning False
    # if it outside the screen co-oridinates
    def update(self):
        if (self.x < 500 and self.x > 0 and self.y < 480 and self.y > 0):
            self.x += int((self.vel*math.cos(math.radians(self.angle))))
            self.y -= int((self.vel*math.sin(math.radians(self.angle))))
            return True
        else:
            return False

This leaves your update function:

while run:
    print(b_angle)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
    # Move the bullets 
    for bullet in bullets:
        if (bullet.update() == False): 
            bullets.pop(bullets.index(bullet))  # bullet left screen

    #Only allows changing direction when there are no bullets on screen.
            if len(bullets) == 0:
                man.isFiring = False

Of course, when you create a new bullet, it needs to also be given the b_angle.

bullets.append(projectile(round(man.x + man.width //2), round(man.y + man.height//2), 3, (0,0,0), facing, b_angle))

Upvotes: 0

Related Questions