How can you rotate the sprite and shoot the bullets towards the mouse position?
There are 2 issues. The current position of the bullets is multiplied by its the normalized direction vector. That doesn't make sense:
for bullet in list_of_bullets:
bullet[0] = bullet[0] * bullet[2][0]
bullet[1] = bullet[1] * bullet[2][1]
Add the direction vector to the position of the bullet:
for bullet in list_of_bullets:
bullet[0] += bullet[2][0]
bullet[1] += bullet[2][1]
If you want to increase the speed of the bullets, then you've to scale the vector by a certain speed
. e.g.:
def spawn_bullet(x,y):
global list_of_bullets
initial_x = x
initial_y = y
mouse_x, mouse_y = pygame.mouse.get_pos()
vector_x, vector_y = mouse_x - x, mouse_y - y
distance = math.sqrt(vector_x ** 2 + vector_y **2)
speed = 5
move_vec = (speed*vector_x/distance, speed*vector_y/distance)
list_of_bullets.append([initial_x, initial_y, move_vec])
The 2nd issue is, that the display is cleared, after the bullets are draw, so you'll never "see" the bullets.
Draw the bullets in update_game
after the display is cleared:
def update_game(x,y,width,height,char):
win.fill((0,0,0))
for bullet in list_of_bullets:
pygame.draw.rect(win,(0,0,255),(int(bullet[0]),int(bullet[1]),20,20))
point_to_mouse(x,y,char)
pygame.display.update()
(Delete the drawing for the main application loop)
Minimal example:
import math
import pygame
def blit_point_to_mouse(target_surf, char_surf, x, y):
mouse_x, mouse_y = pygame.mouse.get_pos()
vector_x, vector_y = mouse_x - x, mouse_y - y
angle = (180 / math.pi) * -math.atan2(vector_y, vector_x) - 90
rotated_surface = pygame.transform.rotate(char_surf, round(angle))
rotated_surface_location = rotated_surface.get_rect(center = (x, y))
target_surf.blit(rotated_surface, rotated_surface_location)
def spawn_bullet(list_of_bullets, x, y):
mouse_x, mouse_y = pygame.mouse.get_pos()
vector_x, vector_y = mouse_x - x, mouse_y - y
distance = math.hypot(vector_x, vector_y)
if distance == 0:
return
speed = 5
move_vec = (speed * vector_x / distance, speed * vector_y / distance)
list_of_bullets.append([x, y, move_vec])
pygame.init()
window = pygame.display.set_mode((500,500))
clock = pygame.time.Clock()
rocket = pygame.image.load('Rocket64.png')
rocket_rect = rocket.get_rect(center = window.get_rect().center)
velocity = 6
list_of_bullets = []
bullet = pygame.Surface((20, 20), pygame.SRCALPHA)
pygame.draw.circle(bullet, (64, 64, 64), (10, 10), 10)
pygame.draw.circle(bullet, (96, 96, 96), (10, 10), 9)
pygame.draw.circle(bullet, (128, 128, 128), (9, 9), 7)
pygame.draw.circle(bullet, (160, 160, 160), (8, 8), 5)
pygame.draw.circle(bullet, (192, 192, 192), (7, 7), 3)
pygame.draw.circle(bullet, (224, 224, 224), (6, 6), 1)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
spawn_bullet(list_of_bullets, *rocket_rect.center)
for bullet_pos in list_of_bullets:
bullet_pos[0] += bullet_pos[2][0]
bullet_pos[1] += bullet_pos[2][1]
if not (0 <= bullet_pos[0] < window.get_width() and 0 < bullet_pos[1] < window.get_height()):
del list_of_bullets[list_of_bullets.index(bullet_pos)]
continue
keys = pygame.key.get_pressed()
rocket_rect.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * velocity
rocket_rect.y += (keys[pygame.K_UP] - keys[pygame.K_DOWN]) * velocity
rocket_rect.clamp_ip(window.get_rect())
window.fill(0)
for bullet_pos in list_of_bullets:
window.blit(bullet, bullet.get_rect(center = (round(bullet_pos[0]),round(bullet_pos[1]))))
blit_point_to_mouse(window, rocket, *rocket_rect.center)
pygame.display.flip()
pygame.quit()
exit()
How To Make Bullets Move Towards Cursor From Player Point?
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
# [...]
How Can I Shoot Projectiles with my mouse at any position?
You have to add the moving direction (dirx
, diry
) to the class projectile. Further add a method which moves the bullet:
class projectile(object):
def __init__(self, x, y, dirx, diry, color):
self.x = x
self.y = y
self.dirx = dirx
self.diry = diry
self.slash = pygame.image.load("heart.png")
self.rect = self.slash.get_rect()
self.rect.topleft = ( self.x, self.y )
self.speed = 10
self.color = color
def move(self):
self.x += self.dirx * self.speed
self.y += self.diry * self.speed
def draw(self, window):
self.rect.topleft = (round(self.x), round(self.y))
window.blit(slash, self.rect)
Compute the direction form the player to the mouse when the mouse button is pressed and spawn a new bullet.
The direction is given by the vector form the player to the muse position (mouse_x - start_x
, mouse_y - start_y
).
The vector has to be normalized (Unit vector) by dividing the vector components by the Euclidean distance:
for event in pygame.event.get():
# [...]
if event.type == pygame.MOUSEBUTTONDOWN:
if len(bullets) < 2:
start_x, start_y = playerman.x+playerman.width//2, playerman.y + playerman.height-54
mouse_x, mouse_y = event.pos
dir_x, dir_y = mouse_x - start_x, mouse_y - start_y
distance = math.sqrt(dir_x**2 + dir_y**2)
if distance > 0:
new_bullet = projectile(start_x, start_y, dir_x/distance, dir_y/distance, (0,0,0))
bullets.append(new_bullet)
Move the bullets in a loop in te main application loop and remove the bullet if it is out of the window
run = True
while run:
# [...]
for event in pygame.event.get():
# [...]
for bullet in bullets[:]:
bullet.move()
if bullet.x < 0 or bullet.x > 500 or bullet.y < 0 or bullet.y > 500:
bullets.pop(bullets.index(bullet))
# [...]
Example code
runninggame = True
while runninggame:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
runninggame = False
if event.type == pygame.MOUSEBUTTONDOWN:
if len(bullets) < 2:
start_x, start_y = playerman.x+playerman.width//2, playerman.y + playerman.height-54
mouse_x, mouse_y = event.pos
dir_x, dir_y = mouse_x - start_x, mouse_y - start_y
distance = math.sqrt(dir_x**2 + dir_y**2)
if distance > 0:
new_bullet = projectile(start_x, start_y, dir_x/distance, dir_y/distance, (0,0,0))
bullets.append(new_bullet)
for bullet in bullets[:]:
bullet.move()
if bullet.x < 0 or bullet.x > 800 or bullet.y < 0 or bullet.y > 800:
bullets.pop(bullets.index(bullet))
# [...]
How do I make the bullets to shoot towards my mouse when I leftclick in this code?
When you click the mouse, you need to calculate the normalized direction vector (Unit vector) from the start position of the bullet to the mouse position. Define the speed of the bullet and multiply the direction vector by the speed:
dx = event.pos[0] - bulletX
dy = event.pos[1] - bulletY
dist = math.sqrt(dx*dx + dy*dy)
bulletX_change = bullet_speed * dx/dist
bulletY_change = bullet_speed * dy/dist
relevant changes:
def fire_bullet(x, y):
screen.blit(bulletImg, (x + 35, y + 10))
running = True
while running:
# [...]
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# [...]
if event.type == pygame.MOUSEBUTTONUP and event.button == LEFT:
if bullet_state == "ready":
#bullet_Sound = mixer.Sound('.wav')
#bullet_Sound.play()
bulletX = playerX
bulletY = playerY
bullet_state = "fire"
dx = event.pos[0] - bulletX
dy = event.pos[1] - bulletY
dist = math.sqrt(dx*dx + dy*dy)
bulletX_change = bullet_speed * dx/dist
bulletY_change = bullet_speed * dy/dist
# [...]
if playerX < 0 or bulletY < 0 or playerX > 1280 or bulletY > 720:
bullet_state = "ready"
if bullet_state == "fire":
bulletX += bulletX_change
bulletY += bulletY_change
fire_bullet(bulletX, bulletY)
pygame.display.update()
How to shoot a bullet towards mouse cursor in pygame
To calculate speed correctly you can't use directly from_player_x
and from_player_y
but use it to calculate angle
and then use sin()
, cos()
to calculate speed_x, speed_y
player = pygame.Rect(screen_rect.centerx, screen_rect.bottom, 0, 0)
SPEED = 5
#---
mouse_x, mouse_y = pygame.mouse.get_pos()
distance_x = mouse_x - player.x
distance_y = mouse_y - player.y
angle = math.atan2(distance_y, distance_x)
speed_x = SPEED * math.cos(angle)
speed_y = SPEED * math.sin(angle)
Minimal working example
import pygame
import math
# === CONSTANS === (UPPER_CASE names)
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = ( 0, 255, 0)
BLUE = ( 0, 0, 255)
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 400
# === MAIN === (lower_case names)
# --- init ---
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
screen_rect = screen.get_rect()
# --- objects ---
player = pygame.Rect(screen_rect.centerx, screen_rect.bottom, 0, 0)
start = pygame.math.Vector2(player.center)
end = start
length = 50
SPEED = 5
all_bullets = []
# --- mainloop ---
clock = pygame.time.Clock()
is_running = True
while is_running:
# --- events ---
for event in pygame.event.get():
# --- global events ---
if event.type == pygame.QUIT:
is_running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
is_running = False
elif event.type == pygame.MOUSEMOTION:
mouse = pygame.mouse.get_pos()
end = start + (mouse - start).normalize() * length
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = pygame.mouse.get_pos()
distance_x = mouse_x - player.x
distance_y = mouse_y - player.y
angle = math.atan2(distance_y, distance_x)
# speed_x, speed_y can be `float` but I don't convert to `int` to get better position
speed_x = SPEED * math.cos(angle)
speed_y = SPEED * math.sin(angle)
# I copy `player.x, player.y` because I will change these values directly on list
all_bullets.append([player.x, player.y, speed_x, speed_y])
# --- objects events ---
# empty
# --- updates ---
# move using speed - I use indexes to change directly on list
for item in all_bullets:
# speed_x, speed_y can be `float` but I don't convert to `int` to get better position
item[0] += item[2] # pos_x += speed_x
item[1] += item[3] # pos_y -= speed_y
# --- draws ---
screen.fill(BLACK)
pygame.draw.line(screen, RED, start, end)
for pos_x, pos_y, speed_x, speed_y in all_bullets:
# need to convert `float` to `int` because `screen` use only `int` values
pos_x = int(pos_x)
pos_y = int(pos_y)
pygame.draw.line(screen, (0,255,0), (pos_x, pos_y), (pos_x, pos_y))
pygame.display.update()
# --- FPS ---
clock.tick(25)
# --- the end ---
pygame.quit()
PyGame has module pygame.math and object Vector2 which can make calculation simpler
player = pygame.Rect(screen_rect.centerx, screen_rect.bottom, 0, 0)
start = pygame.math.Vector2(player.center)
SPEED = 5
# ---
mouse = pygame.mouse.get_pos()
distance = mouse - start
position = pygame.math.Vector2(start) # duplicate # start position in start of canon
#position = pygame.math.Vector2(end) # duplicate # start position in end of canon
speed = distance.normalize() * SPEED
and later
position += speed
Minimal working example
import pygame
# === CONSTANS === (UPPER_CASE names)
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = ( 0, 255, 0)
BLUE = ( 0, 0, 255)
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 400
# === MAIN === (lower_case names)
# --- init ---
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
screen_rect = screen.get_rect()
# --- objects ---
player = pygame.Rect(screen_rect.centerx, screen_rect.bottom, 0, 0)
start = pygame.math.Vector2(player.center)
end = start
length = 50
SPEED = 5
all_bullets = []
# --- mainloop ---
clock = pygame.time.Clock()
is_running = True
while is_running:
# --- events ---
for event in pygame.event.get():
# --- global events ---
if event.type == pygame.QUIT:
is_running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
is_running = False
elif event.type == pygame.MOUSEMOTION:
mouse = pygame.mouse.get_pos()
end = start + (mouse - start).normalize() * length
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse = pygame.mouse.get_pos()
distance = mouse - start
position = pygame.math.Vector2(start) # duplicate # start position in start of canon
#position = pygame.math.Vector2(end) # duplicate # start position in end of canon
speed = distance.normalize() * SPEED
all_bullets.append([position, speed])
# --- objects events ---
# empty
# --- updates ---
for position, speed in all_bullets:
position += speed
# --- draws ---
screen.fill(BLACK)
pygame.draw.line(screen, RED, start, end)
for position, speed in all_bullets:
# need to convert `float` to `int` because `screen` use only `int` values
pos_x = int(position.x)
pos_y = int(position.y)
pygame.draw.line(screen, (0,255,0), (pos_x, pos_y), (pos_x, pos_y))
pygame.display.update()
# --- FPS ---
clock.tick(25)
# --- the end ---
pygame.quit()
How do you shoot a bullet towards mouse in pygame at a constant speed?
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.
You have to do it the other way around. Change self.x_loc
and self.y_loc
, but update self.bullet_rect.center
:
class Bullet:
# [...]
def update(self):
self.x_loc += self.x_move
self.y_loc += self.y_move
self.bullet_rect.center = round(self.x_loc), round(self.y_loc)
rect = screen.blit(self.image, self.bullet_rect)
if not screen.get_rect().contains(rect):
bullets.remove(self)
See also these questions:
Shooting a bullet in pygame in the direction of mouse and How can you rotate the sprite and shoot the bullets towards the mouse position.
How do I move an object towards the mouse without having the mouse position affect speed in Pygame?
If you have to points
x1 = 10
y1 = 10
x2 = 100
y2 = 500
then you can calculate distance and use pygame.math.Vector2
import pygame
dx = x2-x1
dy = y2-y1
distance = pygame.math.Vector2(dx, dy)
or
v1 = pygame.math.Vector2(x1, y1)
v2 = pygame.math.Vector2(x2, y2)
distance = v2 - v1
and then you can normalize it
direction = distance.normalize()
It should always gives distance 1
print('distance:', direction[0]**2 + direction[1]**2) # 0.999999999999
# or
print('distance:', direction.length() )
And then you move object using speed
pos[0] += direction[0] * speed
pos[1] += direction[1] * speed
EDIT:
If you will use Rect
SIZE = (10, 10)
bullet_rect = pygame.Rect((0, 0), SIZE)
bullet_rect.center = (x1, y1)
then you can also calculate
distance = v2 - bullet_rect.center
direction = distance.normalize()
and move it with one line
bullet_rect.center += direction * speed
Rect
has many useful functions. But has one minus - it keeps position as integers
so it rounds float values and sometimes it gives strange moves or lost one pixel every few moves.
Doc: PyGame.math
Related Topics
Style Active Navigation Element with a Flask/Jinja2 MACro
Typeerror: Use() Got an Unexpected Keyword Argument 'Warn' When Importing Matplotlib
What's the Ruby Equivalent of Python's Os.Walk
Ruby Equivalent to Python's Help()
Ipython Reads Wrong Python Version
Python: Maximum Recursion Depth Exceeded
Python Sockets Error Typeerror: a Bytes-Like Object Is Required, Not 'Str' with Send Function
Encoding an Image File with Base64
Alternative Way to Split a List into Groups of N
How to Plot and Annotate a Grouped Bar Chart
Python Script for Minifying CSS
Plotting of 2D Data:Heatmap with Different Colormaps
List Comprehension in Haskell, Python and Ruby
Output Seckeycopyexternalrepresentation
Running an Outside Program (Executable) in Python