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.
Advertisement
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