0

So I have a functioning shooting mechanic in python pygame for a top down shooter, where I am using the mouse position to aim the bullets by working out the angles, however when I do this, the bullets are shooting slightly off where the mouse position is. for instance: the mouse would be where the red arrow is drawn and the bullets will be shooting by a small amount in the wrong direction Any help would be appreciated code below:

main.py:

#-------------Imports-------------#
import pygame,sys
#import globals
from background import*
from player import*

#-------------Constants-------------#
WIDTH,HEIGHT = 500,500
WINDOW = pygame.display.set_mode((WIDTH,HEIGHT))
CLOCK = pygame.time.Clock()
BLACK = (0, 0, 0)

#-------------Instances-------------#
bg = Background()
player = Player()

#-------------Functions-------------#
def draw():
    WINDOW.fill(BLACK)
    bg.update(WINDOW)
    player.update(WINDOW)
    pygame.display.update()

#-------------Main Game Loop-------------#
def main():
    #globals.intialise()
    while 1:
        CLOCK.tick(1)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

        draw()
        #globals.game_ticks += 1



if __name__ == "__main__":
    main()

player.py

#-------------Imports-------------#
import pygame,math
#import globals

#-------------Constants-------------#
WIDTH,HEIGHT = 500,500
PLAYER_COLOUR = (255, 212, 112)
BLACK = (0,0,0)
PI = 3.14159265359

#-------------Classes-------------#
class Bullet:
    def __init__(self,origin,angle):
        self.speed = 20

        self.x_speed,self.y_speed = self.speed*math.cos(math.radians(angle)),self.speed*math.sin(math.radians(angle))

        self.rect = pygame.Rect(origin[0],origin[1],5,5)

    def __del__(self):
        pass

    def update(self,window):
        # move bullet
        self.rect.x += self.x_speed
        self.rect.y += self.y_speed

        # draw bullet
        pygame.draw.rect(window,BLACK,self.rect)

        # check if bullet is out of the screen
        if self.rect.x > WIDTH or self.rect.x < 0:
            return -1
        elif self.rect.y > HEIGHT or self.rect.y < 0:
            return -1

class Player:
    def __init__(self):
        self.sprite = pygame.transform.scale(pygame.image.load("sprites/temp_player.png"),(50,50))

        self.rect = pygame.Rect(250,250,50,50)
        self.center = (self.rect.x,self.rect.y)

        self.bullets = []
        self.fire_rate = 12

    def shoot(self,angle,window):
        # update all bullets and delete if bullet is out of screen
        for bullet in self.bullets:
            if bullet.update(window) == -1:
                self.bullets.remove(bullet)
                del bullet

        # instantiate bullet if mouse button pressed
        #if pygame.mouse.get_pressed()[0] and globals.game_ticks % self.fire_rate == 0:
        if pygame.mouse.get_pressed()[0]:
            self.bullets.append(Bullet(self.rect.center,-angle))

    def update(self,window):
        mx,my = pygame.mouse.get_pos()
        # find distance between mouse position and player position
        diff_x,diff_y = mx-self.rect.x,my-self.rect.y
        # word out angle between mouse and player
        angle_rad = math.atan2(diff_y,diff_x)
        angle = -360*angle_rad/(2*PI)
        # adjust angle according to where we want to rotate the player
        # when angle is bottom left
        if abs(angle) > 90 and angle < 0:
            a = 270-abs(angle)
        # when angle is top left
        elif abs(angle) > 90:
            a = angle-90
        # when angle is to the right
        else:
            a = angle - 90

        # create new sprite that is rotated
        rotated_image = pygame.transform.rotate(self.sprite,a)
        # replace current rectangle with rotated sprite
        self.rect = rotated_image.get_rect(center = self.center)

        self.shoot(angle,window)
        # add image to the screen
        #window.blit(pygame.transform.rotate(self.sprite,a),self.rect)

background.py:

#-------------Imports-------------#
import pygame,random,ast,time,globals

#-------------Constants-------------#
WIDTH,HEIGHT = 500,500
TILE_DIMENSION = 9
TILE_SIZE = int(round(WIDTH/TILE_DIMENSION,0))
TO_EDGE = int((TILE_DIMENSION+1)/2)

#-------------Classes-------------#
class Background:
    def __init__(self):
        self.tiles = self.generate_screen()
        self.centre = [2,2]
        self.right = 0
        self.up = 0

        self.speed = 5

    def generate_screen(self):
        # generate original chunk of tiles
        tiles = [[random.randint(100,200) for i in range(TILE_DIMENSION)] for j in range(TILE_DIMENSION)]
        # eventually use image instead of random RGB value
        return tiles

    def movement(self,tile_rects):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_a] or keys[pygame.K_LEFT]:
            # if player is on tile to the left of centre
            if (self.right - self.speed) < -TILE_SIZE:
                # reset movement and adjust centre
                self.right = 0
                self.centre[0] -= 1
            else:
                # add to movement if not on next tile
                self.right -= self.speed

                # move all rectangles in background to simulate player moving
                for i in range(len(tile_rects)):
                    for j in range(len(tile_rects[0])):
                        tile_rects[i][j].x += self.speed

        if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
            # if player is on tile to the right of centre
            if (self.right + self.speed) > TILE_SIZE:
                # reset movement and adjust centre
                self.right = 0
                self.centre[0] += 1
            else:
                # add to movement if not on next tile
                self.right += self.speed

                # move all rectangles in background to simulate player moving
                for i in range(len(tile_rects)):
                    for j in range(len(tile_rects[0])):
                        tile_rects[i][j].x -= self.speed

        if keys[pygame.K_w] or keys[pygame.K_UP]:
            # if player is on tile above the centre
            if (self.up + self.speed) > TILE_SIZE:
                # reset movement and adjust centre
                self.up = 0
                self.centre[1] -= 1
            else:
                # add to movement if not on next tile
                self.up += self.speed

                # move all rectangles in background to simulate player moving
                for i in range(len(tile_rects)):
                    for j in range(len(tile_rects[0])):
                        tile_rects[i][j].y += self.speed

        if keys[pygame.K_s] or keys[pygame.K_DOWN]:
            # if player is on tile below the centre
            if (self.up - self.speed) < -TILE_SIZE:
                # reset movement and adjust centre
                self.up = 0
                self.centre[1] += 1
            else:
                # add to movement if not on next tile
                self.up -= self.speed

                # move all rectangles in background to simulate player moving
                for i in range(len(tile_rects)):
                    for j in range(len(tile_rects[0])):
                        tile_rects[i][j].y -= self.speed

        return tile_rects

    def update(self,window):
        # rendering in brand new map chunks

        # if part of the chunk trying to be rendered in is non-existant in the 2D map array to the left
        if self.centre[0]-TO_EDGE < 0:
            # check how many tiles it is offset by
            for i in range(0-(self.centre[0]-TO_EDGE)):
                # add new column of values at the beginning of the 2D array 
                for i in range(len(self.tiles)):
                    self.tiles[i].insert(0,random.randint(120,230))
                # due to whole array being shifted to the right, adjust the centre accordingly
                self.centre[0] += 1

        # if part of the chunk trying to be rendered is non-existant in the 2D map array to the right
        if self.centre[0]+TO_EDGE >= len(self.tiles[0]):
            # check how many tiles it is offset by
            for i in range((self.centre[0]+TO_EDGE)-(len(self.tiles[0])-1)):
                # add a new column of values at the end of the 2D array
                for i in range(len(self.tiles)):
                    self.tiles[i].append(random.randint(120,230))

        # if part of the chunk trying to be rendered in is non-existant in the 2D array at the top
        if self.centre[1]-TO_EDGE < 0:
            # check how many tiles it is offset by
            for i in range(0-(self.centre[1]-TO_EDGE)):
                # add a new row at the top of the 2D array
                self.tiles.insert(0,[random.randint(120,230) for i in range(len(self.tiles[0]))])
                # due to whole array shifting downwards, adjust the centre accordingly
                self.centre[1] += 1

        # if part of the chunk trying to be rendered in is non-existant in the 2D array at the bottom
        if self.centre[1]+TO_EDGE >= len(self.tiles):
            #  check how many tiles it is offset by
            for i in range((self.centre[1]+TO_EDGE)-(len(self.tiles)-1)):
                # add a new row at the bottom of the 2D array
                self.tiles.append([random.randint(120,230) for i in range(len(self.tiles[0]))])
                

        # determining which tiles should be rendered in according to the centre(where player would be)
        t = []
        for i in range(TILE_DIMENSION+2):
            t.append([])
            for j in range(TILE_DIMENSION+2):
                try:
                    t[i].append(self.tiles[i+(self.centre[1]-TO_EDGE)][j+(self.centre[0]-TO_EDGE)])
                except:
                    pass

        # create a rectangle for each tile that is rendered in
        tile_rects = [[pygame.Rect((i-1)*TILE_SIZE-self.right,(j-1)*TILE_SIZE+self.up,TILE_SIZE,TILE_SIZE) for i in range(TILE_DIMENSION+2)] for j in range(TILE_DIMENSION+2)]

        tile_rects = self.movement(tile_rects)
        # draw all rectangles
        for i in range(TILE_DIMENSION+2):
            for j in range(TILE_DIMENSION+2):
                try:
                    pygame.draw.rect(window,(0,int(t[i][j]),0),tile_rects[i][j])
                except:
                    pass

the background script doesnt affect anything, its just there as a background to make it easier to see, and you may have to make your own temp_player.png image to make it compatible

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
B1ake
  • 11
  • 5
  • 1
    Learn how to formt the code. See [Markdown help](https://stackoverflow.com/editing-help). Codesnippets are only for javascript/html/css. – Rabbid76 Jul 08 '22 at 17:13
  • i think it's how you create the bullet. You're giving the bullet the center of the player, but used that center as the top left corner of the bullets rect – smcrowley Jul 08 '22 at 17:18
  • Since [`pygame.Rect`](https://www.pygame.org/docs/ref/rect.html) is supposed to represent an area on the screen, a `pygame.Rect` object can only store integral data. The fraction part of the movement gets lost when the movement is add to the position of the rectangle. – Rabbid76 Jul 08 '22 at 17:20

0 Answers0