Search code examples
pythonpygamemotion

How to control the speed of bullets?


I have made a simple 2D game, but when the hero shoots, some of the bullets move faster then others, especially the bullets that come out from the hero's corners (the shape of hero is simple box).

Here is how I wrote the shooting code (I wrote that line just to make sure the bullets go into the right direction). I am using Python 3.5.2.

Run the code and keep shooting randomly everywhere, and you will see that some of them are faster.

import pygame
from pygame.locals import*
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (0,25)
pygame.init()
x=pygame.display.set_mode((1360,705))
clock = pygame.time.Clock()
kx=[]
ky=[]
kx1=[]
ky1=[]
while True :
     x.fill((0,0,0))
     for event in pygame.event.get():
                if event.type==QUIT :
                    pygame.quit()
                    quit()
                if event.type== pygame.MOUSEBUTTONDOWN and event.button == 1:
                     xvx=pygame.mouse.get_pos()
                     xa=(int(xvx[0]))
                     ya=(int(xvx[1]))
                     wox=(xa-680)
                     woy=(ya-352)
                     ox=wox/70
                     oy=woy/70
                     while True :
                         if xa >= 700 or xa <=660 or ya>=372 or ya<=332 :
                              xa=xa-ox
                              ya=ya-oy
                         else :
                              break
                     pygame.draw.line(x,(255,150,100),(xa,ya),(680,352))
                     kx.append(xa-1)
                     ky.append(ya-1)
                     kx1.append(680)
                     ky1.append(352)
     for i in range (len(kx)):
          wox=(kx[i]-kx1[i])
          woy=(ky[i]-ky1[i])
          ox=wox/20
          oy=woy/20
          kx[i]=kx[i]+ox
          ky[i]=ky[i]+oy
          kx1[i]=kx1[i]+ox
          ky1[i]=ky1[i]+oy
          pygame.draw.rect(x,(250,250,250),(kx[i],ky[i],2,2))
     pygame.display.update()
     clock.tick(60)

Solution

  • Your current code appears to use the position of the mouse when you click it to determine the speed. A click further away results in a faster shot. But it sounds like you want the position of the mouse only to determine the direction of the shot, with all bullets getting the same speed.

    This is a job for vector normalization!

    You can turn the position vector you calculate by subtracting the click position from your character position into a unit vector by dividing it by its own length. Then you can multiply the unit vector by the desired speed.

    Try something like this:

    if event.type== pygame.MOUSEBUTTONDOWN and event.button == 1:
         xvx=pygame.mouse.get_pos()
         click_x = int(xvx[0]) - 680 # get relative position of the click
         click_y = int(xvx[1]) - 352
         distance = math.sqrt(click_x**2 + click_y**2)
         unit_x = click_x / distance            # make a unit vector
         unit_y = click_y / distance
         BULLET_SPEED = 20   # this constant could be defined elsewhere
         velocity_x = unit_x * BULLET_SPEED     # make the velocity vector
         velocity_y = unit_y * BULLET_SPEED
         pygame.draw.line(x, (255, 150, 100),
                          (680, 352), (680+velocity_x, 352+velocity_y))
         kx.append(680 + velocity_x)
         ky.append(352 + velocity_y)
         kx1.append(680)
         ky1.append(352)
    

    I've used more meaningful variable names, so hopefully the code is easier to understand than your original with its excessively terse names. You could shorten things a bit if you want by combining some of the sequential operations into one combined operation on one line (e.g. there's no need for the unit_x and unit_y variables to exist, you could go straight to velocity_x and velocity_y by multiplying in the speed at the same time you divide by distance).

    Note that I've left the kx/ky and kx1/ky1 lists as they were, but might make more sense to store the components of the velocity of the bullet in kx1/ky1 instead of storing the former position (since you're subtracting the positions to get a velocity anyway).

    You might also consider making a Bullet class to store the position and velocity of the bullet together in one object rather than having several parallel lists.