¿Cómo saber el ángulo entre dos vectores?
Estoy haciendo caza menor con pygame y he hecho una pistola que gira alrededor de su centro. Mi problema es que quiero que el arma gire por sí sola hacia la dirección del enemigo, pero no pude hacerlo porque no puedo encontrar el ángulo entre el arma y el enemigo para hacer que el arma gire hacia él. He buscado y Descubrí que tengo que usar atan2
pero no encontré ningún código que funcione, así que espero que alguien pueda ayudarme.
Aquí está mi código:
import pygame
from pygame.locals import*
pygame.init()
height=650
width=650
screen=pygame.display.set_mode((height,width))
clock=pygame.time.Clock()
gun=pygame.image.load("m2.png").convert_alpha()
gun=pygame.transform.smoothscale(gun,(200,200)).convert_alpha()
angle=0
angle_change=0
RED=(255,0,0)
x=525
y=155
while True :
screen.fill((150,150,150))
for event in pygame.event.get():
if event.type==QUIT:
pygame.quit()
quit()
if event.type==KEYDOWN:
if event.key==K_a:
angle_change=+1
if event.key==K_d:
angle_change=-1
elif event.type==KEYUP:
angle_change=0
angle+=angle_change
if angle>360:
angle=0
if angle<0:
angle=360
pygame.draw.rect(screen,RED,(x,y,64,64))
position = (height/2,width/2)
gun_rotate=pygame.transform.rotate(gun,angle)
rotate_rect = gun_rotate.get_rect()
rotate_rect.center = position
screen.blit(gun_rotate, rotate_rect)
pygame.display.update()
clock.tick(60)
Y aquí una imagen que intenta dejarlo claro:
¿Cómo soluciono el problema?
La tangente del ángulo entre dos puntos se define como delta y / delta x Es decir (y2 - y1)/(x2-x1). Esto significa que math.atan2(dy, dx)
da el ángulo entre los dos puntos suponiendo que conoces el eje base que define las coordenadas.
Se supone que su arma es el punto (0, 0) de los ejes para calcular el ángulo en radianes. Una vez que tenga ese ángulo, podrá usarlo para el resto de sus cálculos.
Tenga en cuenta que, dado que el ángulo está en radianes, debe usar math.pi en lugar de 180 grados dentro de su código. Además, no es necesaria su prueba de más de 360 grados (2*math.pi). La prueba de negativo (< 0) es incorrecta ya que luego la fuerza a 0, lo que obliga al objetivo a estar en el eje x en la dirección positiva.
Su código para calcular el ángulo entre el arma y el objetivo es así
myradians = math.atan2(targetY-gunY, targetX-gunX)
Si quieres convertir radianes a grados
mydegrees = math.degrees(myradians)
Para convertir de grados a radianes
myradians = math.radians(mydegrees)
Pitón ATAN2
La función Python ATAN2 es una de las funciones matemáticas de Python que se utiliza para devolver el ángulo (en radianes) desde el eje X hasta el punto especificado (y, x).
matemáticas.atan2()
Definición Devuelve la tangente(y,x) en radio.
Sintaxis
math.atan2(y,x)Parámetros
y,x=númerosEjemplos
La devolución es:>>> import math >>> math.atan2(88,34) 1.202100424136847 >>>
En general, el ángulo de un vector (x, y) se puede calcular mediante math.atan2(y, x)
. El vector se puede definir por 2 puntos (x1, y1) y (x2, y2) en una recta. Por tanto el ángulo de la recta es math.atan2(y2-y1, x2-x1)
. Tenga en cuenta que el eje y debe invertirse ( -y
respectivamente y1-y2
) porque el eje y generalmente apunta hacia arriba, pero en el sistema de coordenadas PyGame el eje y apunta hacia abajo. La unidad del ángulo en el math
módulo Python es Radian , pero la unidad del ángulo en PyGame funciona pygame.transform.rotate()
como Grado . Por lo tanto, el ángulo debe convertirse de radianes a grados mediante math.degrees
:
import math
def angle_of_vector(x, y):
return math.degrees(math.atan2(-y, x))
def angle_of_line(x1, y1, x2, y2):
return math.degrees(math.atan2(-(y2-y1), x2-x1))
Esto se puede simplificar utilizando el angle_to
método del pygame.math.Vector2
objeto. Este método calcula el ángulo entre 2 vectores en el sistema de coordenadas PyGame en grados. Por lo tanto, no es necesario invertir el eje y y convertir de radianes a grados. Simplemente calcule el ángulo entre el vector y (1, 0) :
def angle_of_vector(x, y):
return pygame.math.Vector2(x, y).angle_to((1, 0))
def angle_of_line(x1, y1, x2, y2):
return angle_of_vector(x2-x1, y2-y1)
Ejemplo mínimo:
import pygame
import math
def angle_of_vector(x, y):
#return math.degrees(math.atan2(-y, x)) # 1: with math.atan
return pygame.math.Vector2(x, y).angle_to((1, 0)) # 2: with pygame.math.Vector2.angle_to
def angle_of_line(x1, y1, x2, y2):
#return math.degrees(math.atan2(-y1-y2, x2-x1)) # 1: math.atan
return angle_of_vector(x2-x1, y2-y1) # 2: pygame.math.Vector2.angle_to
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 50)
angle = 0
radius = 150
vec = (radius, 0)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
cpt = window.get_rect().center
pt = cpt[0] + vec[0], cpt[1] + vec[1]
angle = angle_of_vector(*vec)
window.fill((255, 255, 255))
pygame.draw.circle(window, (0, 0, 0), cpt, radius, 1)
pygame.draw.line(window, (0, 255, 0), cpt, (cpt[0] + radius, cpt[1]), 3)
pygame.draw.line(window, (255, 0, 0), cpt, pt, 3)
text_surf = font.render(str(round(angle/5)*5) + "°", True, (255, 0, 0))
text_surf.set_alpha(127)
window.blit(text_surf, text_surf.get_rect(bottomleft = (cpt[0]+20, cpt[1]-20)))
pygame.display.flip()
angle = (angle + 1) % 360
vec = radius * math.cos(angle*math.pi/180), radius * -math.sin(angle*math.pi/180)
pygame.quit()
exit()
angle_to
se puede utilizar para calcular el ángulo entre 2 vectores o líneas:
def angle_between_vectors(x1, y1, x2, y2):
return pygame.math.Vector2(x1, y1).angle_to((x2, y2))
Ejemplo mínimo:
import pygame
import math
def angle_between_vectors(x1, y1, x2, y2):
return pygame.math.Vector2(x1, y1).angle_to((x2, y2))
def angle_of_vector(x, y):
return pygame.math.Vector2(x, y).angle_to((1, 0))
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 50)
angle = 0
radius = 150
vec1 = (radius, 0)
vec2 = (radius, 0)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
cpt = window.get_rect().center
pt1 = cpt[0] + vec1[0], cpt[1] + vec1[1]
pt2 = cpt[0] + vec2[0], cpt[1] + vec2[1]
angle = angle_between_vectors(*vec2, *vec1)
window.fill((255, 255, 255))
pygame.draw.circle(window, (0, 0, 0), cpt, radius, 1)
pygame.draw.line(window, (0, 255, 0), cpt, pt1, 3)
pygame.draw.line(window, (255, 0, 0), cpt, pt2, 3)
text_surf = font.render(str(round(angle/5)*5) + "°", True, (255, 0, 0))
text_surf.set_alpha(127)
window.blit(text_surf, text_surf.get_rect(bottomleft = (cpt[0]+20, cpt[1]-20)))
pygame.display.flip()
angle1 = (angle_of_vector(*vec1) + 1/3) % 360
vec1 = radius * math.cos(angle1*math.pi/180), radius * -math.sin(angle1*math.pi/180)
angle2 = (angle_of_vector(*vec2) + 1) % 360
vec2 = radius * math.cos(angle2*math.pi/180), radius * -math.sin(angle2*math.pi/180)
pygame.quit()
exit()
Específicamente para trabajar con shapely linestring
objetos, asumiendo que su objeto (dos puntos) tiene la forma(min long, min lat, max long, max lat)
from math import atan2,degrees
line = #Your-LineString-Object
lineList = list(line.coords)
def AngleBtw2Points(pointA, pointB):
changeInX = pointB[0] - pointA[0]
changeInY = pointB[1] - pointA[1]
return degrees(atan2(changeInY,changeInX)) #remove degrees if you want your answer in radians
AngleBtw2Points(lineList[0],lineList[1])
Usé el siguiente código.
import math
def get_angle(x1,y1,x2,y2):
myradians = math.atan2(y1-y2, x1-x2)
mydegrees = math.degrees(myradians)
return mydegrees
# it should return -90 degree
print(get_angle(0,0,0,100))