I'm currently making a 2d top down game in pygame where the player (ship/cursor) moves with keyboard input and will always look towards the mouse point. I've also made it that when the player presses or holds the SPACE key the ship will shoot upwards from the middle of its rect.
The problem I'm having is that I need the bullets to face and go towards the mouse position. I've been trying to work on it for the past week to figure it out but I don't know how to do this.
What I want is for the bullets (a rectangle image) to go towards the point of the mouse at the time it was fired, and for the bullet to rotate towards the mouse as well like the ship already does. What this means is that even if the mouse moves, I want the bullet to continue in its same path until it hits the edge of the screen and to keep its same angle as before, if that makes sense.
Sorry about the spacing in the code, it just makes it easier for me to recognize what's to do with what.
Any help would be very appriciated :)
import math, pygame
pygame.init()
# === CONSTANTS ===
#window dimensions set into the name 'win'
win = pygame.display.set_mode((1280,800))
#cursor / player ship image
cursor = pygame.image.load("images/cursor.png").convert_alpha()
# === CLASSES ===
#bullet class, holds image and other points
class projectile(object):
def __init__(self,x,y,radius,color):
self.x = x
self.y = y
self.image = pygame.transform.smoothscale(pygame.image.load('images/bullet.png'), (30,60))
self.vel = 3
#gets called in the update_win() to draw the bullet on the screen
def draw(self,win):
win.blit(self.image, (self.x,self.y))
# === MAIN FUNCTIONS ===
#keeps the display.update() and other blit code for easier layout
def update_win():
win.fill((31,27,24))
for bullet in bullets:
bullet.draw(win)
win.blit(rot_image, rot_image_rect.topleft)
pygame.display.update()
# 0 - image is looking to the right
# 90 - image is looking up
# 180 - image is looking to the left
# 270 - image is looking down
correction_angle = 90
cursor_pos = list(win.get_rect().center)
#this is where the bullets go into a list
bullets = []
#for control of how many bullets are fired and at what interval
shoot_loop = 0
# === MAIN LOOP ===
run = True
while run:
pygame.time.delay(2)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#cursor postion and rectangle
cursor_rect = cursor.get_rect(center = (cursor_pos))
#simple movement / key presses
keys = pygame.key.get_pressed()
if keys[pygame.K_a] and cursor_rect.x > -10:
cursor_pos[0] -= 1
if keys[pygame.K_d] and cursor_rect.x < 1210:
cursor_pos[0] += 1
if keys[pygame.K_w] and cursor_rect.y > -10:
cursor_pos[1] -= 1
if keys[pygame.K_s] and cursor_rect.y < 730:
cursor_pos[1] += 1
if keys[pygame.K_SPACE]:
x,y = pygame.mouse.get_pos()
print(x,y)
#controls how many bullets are shot (interval)
if shoot_loop > 0:
shoot_loop += 0.06
if shoot_loop > 3:
shoot_loop = 0
#will move the bullet image in list and .pop it if it goes above screen
for bullet in bullets:
if bullet.y < 800 and bullet.y > -10:
bullet.y -= bullet.vel # Moves the bullet by its vel (3)
else:
bullets.pop(bullets.index(bullet)) # This will remove the bullet if it is off the screen
#checks the bullet loop and will add another bullet to the loop if conditions are met
if keys[pygame.K_SPACE] and shoot_loop == 0:
if len(bullets) < 100:
bullets.append(projectile(round(cursor_rect.x + 25), round(cursor_rect.y ), 6, (255,255,255)))
shoot_loop = 1
#calculates mouse position, angle and rotation for image
mx, my = pygame.mouse.get_pos()
dx, dy = mx - cursor_rect.centerx, my - cursor_rect.centery
angle = math.degrees(math.atan2(-dy, dx)) - correction_angle
#rotated image surface
rot_image = pygame.transform.rotate(cursor, angle)
rot_image_rect = rot_image.get_rect(center = cursor_rect.center)
update_win()
pygame.quit()
exit()
heres a gif to better understand
Compute the unit direction vector of the bullet in the constructor of the class projectile
(a Unit Vector has a lenght of 1):
mx, my = pygame.mouse.get_pos()
dx, dy = mx - self.x, my - self.y
len = math.hypot(dx, dy)
self.dx = dx / len
self.dy = dy / len
and rotate the projectile image:
angle = math.degrees(math.atan2(-dy, dx)) - 90
self.image = pygame.transform.rotate(self.image, angle)
Add a method move
to the class projectile
:
def move(self):
self.x += self.dx * self.vel
self.y += self.dy * self.vel
Call move
instead of bullet.y -= bullet.vel
:
for bullet in bullets:
if -10 < bullet.x < 1200 and -10 < bullet.y < 800:
bullet.move()
else:
bullets.pop(bullets.index(bullet))
Changes:
class projectile(object):
def __init__(self,x,y,radius,color):
self.x = x
self.y = y
self.image = pygame.transform.smoothscale(pygame.image.load('images/bullet.png'), (30,60))
self.vel = 3
mx, my = pygame.mouse.get_pos()
dx, dy = mx - self.x, my - self.y
len = math.hypot(dx, dy)
self.dx = dx / len
self.dy = dy / len
angle = math.degrees(math.atan2(-dy, dx)) - 90
self.image = pygame.transform.rotate(self.image, angle)
def move(self):
self.x += self.dx * self.vel
self.y += self.dy * self.vel
def draw(self,win):
win.blit( self.image, (round(self.x), round(self.y)))
run = True
while run:
# [...]
#will move the bullet image in list and .pop it if it goes above screen
for bullet in bullets:
if -10 < bullet.x < 1200 and -10 < bullet.y < 800:
bullet.move()
else:
bullets.pop(bullets.index(bullet)) # This will remove the bullet if it is off the screen
# [...]