How to make arrow shoot in direction of mouse?

2019-08-01 05:21发布

问题:

I am attempting to shoot an arrow on mouse click, however in the direction of the mouse. I've accomplished the shooting part, however I am not able to have it shoot towards the mouse. I've listed my code below and I really appreciate all help!

The portion of the code which is relevant to the arrow shooting is:

for shot in daggers:
    index = 0
    shotx = math.cos(shot[0])*35
    shoty = math.sin(shot[0])*35
    shot[1] += shotx
    shot[1] += shoty
    if shot[1] < -40 or shot[1] > 900 or shot[1] < -40 or shot[1]> 600:
        daggers.pop(index)
    index =+ 1

    for shoot in daggers:
        daggerOne = pygame.transform.rotate(daggerPlayer, 360 - shoot[0]*57.29)
        screen.blit(daggerOne, (shoot[1], shoot[1]))

        pygame.display.update()

EDIT- Entire Code:

#Import the necessary modules
import pygame
import sys
import os
import math

#Initialize pygame
pygame.init()

# Set the size for the surface (screen)
screenSize = (900,600)
screen = pygame.display.set_mode((screenSize),0)

# Set the caption for the screen
pygame.display.set_caption("Neverland")

#Define Colours
WHITE = (255,255,255)
BLUE = (0,0,255)
BLACK = (0,0,0)
GRAY = (128, 128, 128)
MAROON = (128, 0, 0)
NAVYBLUE = (0, 0, 128)
OLIVE = (128, 128, 0)
PURPLE = (128, 0, 128)
TEAL = (0,128,128)
PINK = (226,132,164)
MUTEDBLUE = (155,182,203)
PLUM = (221,160,221)

#Clock Setup
clock = pygame.time.Clock()

#Load Images
peterPlayer = pygame.image.load('pixelPirateOne.png')
nightBackground = pygame.image.load ('skyTwo_1.png')
daggerPlayer = pygame.image.load('daggerPlayer.png')

#Settting Variables for Moving Character
xPlayer = 200
yPlayer = 275
dxPlayer = 0
dyPlayer = 0
playerPosition = (200,275)
accuracyShot = [0,0]
daggers = []
angle = 0

def quitGame():
    pygame.quit()
    sys.exit()

def outOfBounds(shot):
    return shot[0] < -40 or shot[0] > 900 or shot[1] < -40 or shot[1] > 600

go = True
while go:

#Quit Game
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
                quitGame()

#Move Player                
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                dxPlayer -= 25
            elif event.key == pygame.K_RIGHT:
                dxPlayer += 25
            elif event.key == pygame.K_UP:
                dyPlayer -= 25
            elif event.key == pygame.K_DOWN:
                dyPlayer += 25

        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
                dxPlayer = 0
            elif event.key == pygame.K_UP or event.key == pygame.K_DOWN:
                dyPlayer = 0

        elif event.type == pygame.MOUSEBUTTONDOWN:
            mousePosition = pygame.mouse.get_pos()   
            velx = math.cos(angle)*6
            vely = math.sin(angle)*6
            daggers.append([math.atan2(mousePosition[1]-(playerPositionNew[1]+32), mousePosition[0]-(playerPositionNew[0]+26)), playerPositionNew[1]+32]) 

#Update move player
    xPlayer = xPlayer + dxPlayer
    yPlayer = yPlayer + dyPlayer
    pygame.display.update()

#Learned about atan2 from --> https://docs.python.org/2/library/math.html
#Allows To Rotate Player With Mouse    
    mousePosition = pygame.mouse.get_pos()   
    angle = math.atan2(mousePosition[1]-(yPlayer+32),mousePosition[0]-(xPlayer+26))
    playerRotate = pygame.transform.rotate(peterPlayer, 360-angle*57.29)
    playerPositionNew = (xPlayer-playerRotate.get_rect().width/2, yPlayer-playerRotate.get_rect().height/2)
    pygame.display.update()

#Learned about cos and sin in python from --> https://docs.python.org/2/library/math.html
#Learned about .pop from --> https://docs.python.org/2/tutorial/datastructures.html
#Draw daggers to screen
    filtered_daggers = []
    for shot in daggers:
        if not outOfBounds(shot[0]):
            filtered_daggers.append(shot)
        daggers = filtered_daggers

    for shot in daggers:
        shot[0][0] += shot[1][0]
        shot[0][1] += shot[1][1]

    screen.blit(nightBackground, (0,0))
    screen.blit(playerRotate, playerPositionNew)


    for shot in daggers:
        x, y = shot[0]       
        screen.blit(daggerPlayer, (shot[2], shot[0]))

    pygame.display.update()
    clock.tick(30)

回答1:

To calculate the velocity of the projectiles, take the angle and use math.cos for the x-velocity and math.sin for the y-velocity, scale them to the desired speed by multiplying them with an arbitrary number.

Store the position and the velocity of the projectile in a list or other data structure (I recommend creating a class for the projectiles), append this projectile list to your daggers list and in the while loop iterate over the daggers to update the positions by adding the velocities.

Some more notes:

  • To convert the angle to degrees, call math.degrees instead of multiplying it by 57.29. This makes the code more readable.

  • Don't call pygame.display.update() in the for shot in daggers loop. It should be called only once per frame.

  • Never modify a list (or other iterables) while you're iterating over it or you'll get unexpected results. Build a new filtered list instead.

  • Use a pygame.time.Clock to limit the frame rate.

  • And again I recommend using vectors, pygame sprites and sprite groups, since they'll help to make your code cleaner and easier to read.


import sys
import math
import pygame as pg


def out_of_bounds(position):
    """Check if the position of a projectile is out of bounds.

    Args:
        position (list): The coordinates of the projectile.

    Returns:
        bool: True if out of bounds, False if not.
    """
    x, y = position  # You can unpack a list or tuple like so.
    return x < -40 or x > 500 or y < -40 or y > 400


pg.init()

screen = pg.display.set_mode((640, 480))

PLAYER_IMG = pg.Surface((50, 30), pg.SRCALPHA)
pg.draw.polygon(PLAYER_IMG, (30, 150, 90), ((0, 0), (50, 15), (0, 30)))
DAGGER_IMG = pg.Surface((30, 20), pg.SRCALPHA)
pg.draw.polygon(DAGGER_IMG, (190, 150, 90), ((0, 0), (30, 10), (0, 20)))

# Player variables. I store the position in a rect.
player = pg.Rect(200, 275, 50, 30)
velocity_x = 0  # The x-speed of the player.
velocity_y = 0  # The y-speed of the player.
angle = 0
# In this list I'll store the data for the projectiles,
# i.e. lists of the positions and the velocities.
daggers = []

clock = pg.time.Clock()  # Use a clock to limit the frame rate.

while True:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            pg.quit()
            sys.exit()
        # Move the player.
        elif event.type == pg.KEYDOWN:
            if event.key == pg.K_LEFT:
                velocity_x -= 5
            elif event.key == pg.K_RIGHT:
                velocity_x += 5
            elif event.key == pg.K_UP:
                velocity_y -= 5
            elif event.key == pg.K_DOWN:
                velocity_y += 5
        elif event.type == pg.KEYUP:
            if event.key == pg.K_LEFT or event.key == pg.K_RIGHT:
                velocity_x = 0
            elif event.key == pg.K_UP or event.key == pg.K_DOWN:
                velocity_y = 0
        # Here I create the projectiles and add them to the daggers list.
        elif event.type == pg.MOUSEBUTTONDOWN:
            # Use sine and cosine to get the velocity of the projectile.
            # To make it move faster, you need to scale the velocity
            # (i.e. multiply it by a number).
            vel_x = math.cos(angle) * 3
            vel_y = math.sin(angle) * 3
            # Now rotate the original image by the negative angle (because
            # pygame's y-axis is flipped).
            dagger_img = pg.transform.rotate(DAGGER_IMG, -math.degrees(angle))
            width, height = dagger_img.get_size()
            # The projectile data consists of position, velocity and the
            # rotated image. -width/2 and -height/2 to center them.
            daggers.append(
                [[player.centerx-width/2, player.centery-height/2],  # Pos
                 [vel_x, vel_y],  # Velocity
                 dagger_img  # Image
                 ])

    # Update the game.
    # Move the player by adding the velocity to the x- and y-coords
    # of the player rect.
    player.x += velocity_x
    player.y += velocity_y
    # Rotate player toward the mouse.
    mouse_position = pg.mouse.get_pos()
    # Distances to the mouse position.
    rise = mouse_position[1] - player.centery
    run = mouse_position[0] - player.centerx
    angle = math.atan2(rise, run)  # atan2 gives you the angle to the target.
    # Rotate the player image.
    player_rotated = pg.transform.rotate(PLAYER_IMG, -math.degrees(angle))
    player = player_rotated.get_rect(center=player.center)

    # Filter the daggers list.
    filtered_daggers = []
    for shot in daggers:
        if not out_of_bounds(shot[0]):
            filtered_daggers.append(shot)
    daggers = filtered_daggers
    # The 5 lines above can be reduced to this list comprehension.
    # daggers = [shot for shot in daggers if not out_of_bounds(shot[0])]

    # Move the daggers by adding the velocity to the position.
    # shot[0] is the pos, shot[1] the velocity.
    for shot in daggers:
        shot[0][0] += shot[1][0]
        shot[0][1] += shot[1][1]

    # Draw everything.
    screen.fill((30, 40, 50))
    screen.blit(player_rotated, player)
    # Blit the projectiles in a for loop.
    for shot in daggers:
        screen.blit(shot[2], shot[0])

    pg.display.update()
    clock.tick(30)  # Limit frame rate to 30 fps.