User needs to move mouse to fire bullet

Tags: , , , ,



When the user hits q to fire a bullet, there is no smooth motion. They need to move the mouse around the screen in order for the bullet to travel.

I’ve tried looking around StackOverflow, youtube, reorganizing the code.

def tank(x,y,turretpos):
        x = int(x)
        y = int(y)
        possibleturrets = [(x-25, y-2), (x-25, y-4), (x-25, y-6), (x-25, y-8), (x-24, y-10),
                  (x-24, y-12), (x-23, y-14), (x-20, y-16), (x-18, y-18), (x-16, y-20)]

        pygame.draw.circle(screen,black,(x,y),10)
        pygame.draw.rect(screen,black,(x-tankheight,y,tankwidth,tankheight))
        pygame.draw.line(screen,black,(x,y),possibleturrets[turretpos], turretwidth)
        pygame.draw.line(screen,black,(x,y),possibleturrets[turretpos], turretwidth)

        startx = 15
        for i in range(7):
            pygame.draw.circle(screen,black,(x-startx,y+20),wheelwidth)
            startx -= 5

        return possibleturrets

def fire(gun,tankx,tanky,turretpos):
    fire = True
    startingshellx = gun[0][0]
    startingshelly = gun[0][1]
    while fire: 
        for event in pygame.event.get():
            startingshellx -= (12-turretpos)*2
            startingshelly += int((((startingshellx - (gun[0][0]))*0.015)**2) - (turretpos+turretpos/(12-turretpos)))
            pygame.draw.circle(screen,red,(startingshellx, startingshelly-turretpos),5)
            if startingshelly > height-100:
                fire = False
            pygame.display.update()

I want the user to hit q and the bullet should fire with a parabola shape smoothly.

Answer

You’ve to do the update of the position in the main loop rather than the event loop. Note, the event loop is executed only when an event occurs. This means it is executed one time, when the button is pressed and a second time when the button is released. The movement of the mouse is an event, too and causes the event loop to be executed.

The following may work. Instead of an event loop, the internal event handling is done by pygame.event.pump():

def fire(gun,tankx,tanky,turretpos):
    firebullet = True
    startingshellx = gun[0][0]
    startingshelly = gun[0][1]
    while firebullet: 

        pygame.event.pump()

        startingshellx -= (12-turretpos)*2
        startingshelly += int((((startingshellx - (gun[0][0]))*0.015)**2) - (turretpos+turretpos/(12-turretpos)))
        pygame.draw.circle(screen,red,(startingshellx, startingshelly-turretpos),5)
        if startingshelly > height-100:
            firebullet = False
        pygame.display.update()

But I recommend to change the design. Avoid functions and variables with the same name. Do not an extra loop with display updates inside the main loop:

def fire(shellpos, tankx, tanky, turretpos):
    shellpos[0] -= (12-turretpos)*2
    shellpos[1] += (((shellpos[0] - (gun[0][0]))*0.015)**2) - (turretpos+turretpos/(12-turretpos))
    pygame.draw.circle(screen,red,(round(shellpos[0]), round(shellpos[1]-turretpos)),5)
    return (shellpos[1] <= height-100), shellpos

run = True
firebullet = False
while run:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_q:
                firebullet, shellpos = True, gun[0]

    # [...]

    if firebullet:
        firebullet, shellpos = fire(shellpos, tankx, tanky, turretpos)

    # [...]    

    pygame.display.update()


If you want to generate a smooth motion, then you’ve to do the calculations with flotiong point values:

startingshellx -= (12-turretpos)*2
startingshelly += (((startingshellx - (gun[0][0]))*0.015)**2) - (turretpos+turretpos/(12-turretpos))

Cast (int()) or round the coordinates to integral values, when the object (circle) is drawn:

pygame.draw.circle(screen,red,(round(startingshellx), round(startingshelly-turretpos)),5)

Note if the value is truncated to an integral value, before it is added, then the decimal places are lost:

>>> int(0.3) + int(0.4) + int(0.5) + int(0.6)
0

If the cast operation is done later then the only the decimal places of the result are lost:

>>> int(0.3 + 0.4 + 0.5 + 0.6)
1

round() gives the next grater integral value, if the decimal places are >= 0.5:

>>> round(0.3 + 0.4 + 0.5 + 0.6)
2


Source: stackoverflow