Search code examples
pythonpygame

How to shoot a projectile in the way the player is looking in a TDS


I wanna know how to shoot a projectile towards the direction the player is looking. I would also like to know how to shoot that projectile from the middle of the player. I have got my bullet class sort of ready but don't know my next step.

here is my code and replit...

https://replit.com/@TahaSSS/game-1#main.py

import pygame
from sys import exit
from random import randint
import math

from pygame.constants import K_LSHIFT, K_SPACE, MOUSEBUTTONDOWN
pygame.init()
pygame.mouse.set_visible(True)



class Player(pygame.sprite.Sprite):
    def __init__(self, x , y):
        super().__init__()
        self.x = x
        self.y = y
        self.image = pygame.image.load('graphics/Robot 1/robot1_gun.png').convert_alpha()
        self.rect = self.image.get_rect()
        self.orig_image = pygame.image.load('graphics/Robot 1/robot1_gun.png').convert_alpha()
        self.rotate_vel = 1
        self.cross_image = pygame.image.load('graphics/crosshair049.png')
        

    def draw(self, surface):
        """ Draw on surface """
        # blit yourself at your current position
        surface.blit(self.image, self.rect)
        dir_vec = pygame.math.Vector2()
        dir_vec.from_polar((180, -self.rotate_vel))
        cross_pos = dir_vec + self.rect.center
        cross_x, cross_y = round(cross_pos.x), round(cross_pos.y)
        surface.blit(self.cross_image,  self.cross_image.get_rect(center = (cross_x, cross_y)))
        

    def movement(self):
        key = pygame.key.get_pressed()
        self.rect = self.image.get_rect(center = (self.x, self.y))
        dist = 3 # distance moved in 1 frame, try changing it to 5
        if key[pygame.K_DOWN] or key[pygame.K_s]: # down key
            self.y += dist # move down
        elif key[pygame.K_UP] or key[pygame.K_w]: # up key
            self.y -= dist # move up
        if key[pygame.K_RIGHT] or key[pygame.K_d]: # right key
            self.x += dist # move right
        elif key[pygame.K_LEFT] or key[pygame.K_a]: # left key
            self.x -= dist # move left
  
    
    def rotate(self, surface):

        keys = pygame.key.get_pressed()
        if keys[K_LSHIFT]:
            self.rotate_vel += 5
            self.image = pygame.transform.rotate(self.orig_image, self.rotate_vel)
            self.rect = self.image.get_rect(center=self.rect.center)
            surface.blit(self.image, self.rect)
        if keys[K_SPACE]:
            self.rotate_vel += -5
            self.image = pygame.transform.rotate(self.orig_image, self.rotate_vel)
            self.rect = self.image.get_rect(center=self.rect.center)
            surface.blit(self.image, self.rect)


    def update(self):
        self.movement()
        self.draw(screen)
        self.rotate(screen)

    

class Bullet(pygame.sprite.Sprite):
    def __init__(self, x , y):
        super().__init__()
        self.bullet_image = pygame.image.load('graphics/weapons/bullets/default_bullet.png')
        self.bullet_rect = self.bullet_image.get_rect(center = (x,y))
        self.bullet_speed = 15

    


  

#screen
clock = pygame.time.Clock()
FPS = 60
screen = pygame.display.set_mode((800, 400))

#player
player_sprite = Player(600, 300)
player = pygame.sprite.GroupSingle()
player.add(player_sprite)

#bullet
bullet_group = pygame.sprite.Group()



running = True
while running:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            exit()

    #screen
    screen.fill('grey')
    #player sprite funtions
    player.update()    

    clock.tick(FPS)

    pygame.display.update()

Solution

  • pygame.sprite.Group.draw() and pygame.sprite.Group.update() are methods which are provided by pygame.sprite.Group.

    The former delegates the to the update method of the contained pygame.sprite.Sprites - you have to implement the method. See pygame.sprite.Group.update():

    Calls the update() method on all Sprites in the Group [...]

    The later uses the image and rect attributes of the contained pygame.sprite.Sprites to draw the objects - you have to ensure that the pygame.sprite.Sprites have the required attributes. See pygame.sprite.Group.draw():

    Draws the contained Sprites to the Surface argument. This uses the Sprite.image attribute for the source surface, and Sprite.rect. [...]

    A Sprite kan be removed from all Groups and thus delted by calling kill.

    Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data.

    The coordinates for Rect objects are all integers. [...]

    The fraction part of the coordinates gets lost when the new position of the object is assigned to the Rect object. If this is done every frame, the position error will accumulate over time.

    If you want to store object positions with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect object. round the coordinates and assign it to the location (e.g. .center) of the rectangle.

    Write a Sprite class with an update method that moves the bullet in a certain direction. Destroy the bullet when it goes out of the display:

    class Bullet(pygame.sprite.Sprite):
        def __init__(self, pos, angle):
            super().__init__()
            self.image = pygame.image.load('graphics/weapons/bullets/default_bullet.png')
            self.image = pygame.transform.rotate(self.image, angle)
            self.rect = self.image.get_rect(center = pos)
            self.speed = 15
            self.pos = pos
            self.dir_vec = pygame.math.Vector2()
            self.dir_vec.from_polar((self.speed, -angle))
    
        def update(self, screen):   
            self.pos += self.dir_vec
            self.rect.center = round(self.pos.x), round(self.pos.y)
            if not screen.get_rect().colliderect(self.rect):
                self.kill()
    

    pygame.key.get_pressed() returns a list with the state of each key. If a key is held down, the state for the key is True, otherwise False. Use pygame.key.get_pressed() to evaluate the current state of a button and get continuous movement.
    The keyboard events (see pygame.event module) occur only once when the state of a key changes. The KEYDOWN event occurs once every time a key is pressed. KEYUP occurs once every time a key is released. Use the keyboard events for a single action.

    Use an keyboard event to generate a new Bullet object (e.g. TAB). Call bullet_group.update(screen) and bullet_group.draw(screen) in the application loop:

    running = True
    while running:
    
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_TAB:
                    pos = player_sprite.rect.center
                    dir_vec = pygame.math.Vector2()
                    new_bullet = Bullet(pos, player_sprite.rotate_vel)
                    bullet_group.add(new_bullet)
    
        bullet_group.update(screen)
    
        screen.fill('grey')
        player.update()    
        bullet_group.draw(screen)
        pygame.display.update()
    
        clock.tick(FPS)