Ir al contenido

publicidad
publicidad

Foto

Angulos y proyectiles, hasta las narices


Este tema ha sido archivado. Esto significa que no puedes responder en este tema.
7 respuestas en este tema

#1

Escrito 17 febrero 2010 - 21:40

Veamos, mi intencion es hacer que un avion enemigo dispare, el problema es que los puntos y las posiciones las mido en pixeles.

Evidentemente, a la hora de calcular el angulo de tiro sale más grande de lo que deberia.

Uso resolucion 1024x768, es evidente que el angulo me sale más grande, porque por ejemplo si tengo yo la posicion de origen en la 700,500 y la del objetivo 4,512 me sale un angulo de 30º.

Mi metodo es el siguiente:

Hago 2 vectores a partir del origen y el destino, el primer vector es el que va del 700,500 al 4,512 y el otro es el que va del 700,500 al 4,500 y calculo el angulo entre estos 2 vectores.

Mis preguntas son estas:

1. Sabeis si existe alguna manera de calcular el angulo entre 2 puntos (no 2 vectores)? Diferente de mi metodo, claro

2. Que notacion usais para las posiciones y los angulos? Pixeles, centimetros, quilometros...?

A ver si alguien sabe resolver mis dudas XD

  • The_Hans

  • Ultima

  • vida restante: 100%
  • Registrado: 27 ene 2004
  • Mensajes: 7.490
#2

Escrito 17 febrero 2010 - 22:26

Yo para interpolación de posiciones entre varios puntos utilizo siempre dos clases, dependiendo de si es una recta lo que busco o si por el contrario quiero algo más suave, entonces utilizo una spline. Nunca he necesitado calcular ángulos sino era para rotar objetos en una dirección concreta (por ejemplo para posicionar un enemigo mirando al jugador). La idea es que calcules un punto, el punto objetivo y algún punto intermedio si quieres contruir una curva (por ejemplo para simular una parábola) y entonces calculas la spline y refrescas hasta el final para recibir cada posición intermedia interpolada en base al tiempo.

Si necesitas ayuda pregunta, porque no tengo estas clases muy documentadas debido a que sólo las construí para mi y son bastante básicas :mrgreen:

Te pego las dos clases, a ver si te sirve alguna. Están en C#.

[code:1]using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;


public class cRecta
{
private Vector3 puntoA;
private Vector3 puntoB;
private Vector3 lastPosition;

private float totalLength = 100;
private float actualLength = 0;

private bool endState = false;

public cRecta()
{
puntoA = Vector3.Zero;
puntoB = Vector3.Zero;
lastPosition = puntoA;
}

public void SetInitialPoint(Vector3 initial)
{
puntoA = initial;
lastPosition = puntoA;
}

public void SetTarget(Vector3 target)
{
puntoA = lastPosition;
puntoB = target;
}

public bool EndState
{
get { return endState; }
}

public float TotalLength
{
get { return totalLength; }
set { totalLength = value; }
}

public float ActualLength
{
get { return actualLength; }
set { actualLength = value; }
}

public void Restart()
{
actualLength = 0;
endState = false;
}

public Vector3 GetPoint(float length)
{
float distancia = Vector3.Distance(puntoA, puntoB);
totalLength = distancia / 1;

float porcentajeTiempo = actualLength / totalLength;

lastPosition = (porcentajeTiempo * puntoB + (1 - porcentajeTiempo) * puntoA);

return lastPosition;

}

public Vector2 GetPoint2D(float length)
{
Vector3 point = GetPoint(length);
return (new Vector2(point.X, point.Y));
}

public Vector3 GetActualPoint()
{
return GetPoint(actualLength);
}

public Vector2 GetActualPoint2D()
{
Vector3 point = GetActualPoint();
return new Vector2(point.X, point.Y);
}

public void UpdateUntilEnd(GameTime gameTime, float multiplier)
{
if (!endState)
{
actualLength += multiplier * gameTime.ElapsedGameTime.Milliseconds;
if (actualLength > totalLength)
{
actualLength = totalLength;
endState = true;
}
}
if (actualLength == 0) endState = false;
}

public void Update(GameTime gameTime, float multiplier)
{
actualLength += multiplier * gameTime.ElapsedGameTime.Milliseconds;
if (actualLength > totalLength - 1) Restart();
}
}
[/code]


[code:1]
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

public class cSpline
{
//-------------------------------------------------------------------------------------------
/// Variables
private cBase dBase;
private List keyPoints = new List();
private List cList = new List();

private int margen = 0;
private Vector3 lastValue = Vector3.Zero;
private float actualLength;
private int totalLength;
private bool endState;
private float c = 1.0f;

//-------------------------------------------------------------------------------------------
/// Constructor
public cSpline(cBase baseVariable)
{
dBase = baseVariable;

actualLength = 0;
totalLength = 1000;
endState = false;
}
//-------------------------------------------------------------------------------------------
/// Get the end state
public bool EndState
{
get { return endState; }
}
//-------------------------------------------------------------------------------------------
/// Get and set the total length
public int TotalLength
{
get { return totalLength; }
set { totalLength = value; }
}
//-------------------------------------------------------------------------------------------
/// Constructor
public float ActualLength
{
get { return actualLength; }
set { actualLength = value; }
}
//-------------------------------------------------------------------------------------------
/// Constructor
public void AddPoint(Vector3 point, bool close)
{
keyPoints.Add(point);
Build();
}
//-------------------------------------------------------------------------------------------
/// Constructor
public void AddPoint2D(Vector2 point, bool close)
{
keyPoints.Add(new Vector3(point.X, point.Y, 0));
Build();
}
//-------------------------------------------------------------------------------------------
/// Constructor
public void Build()
{
cList.Clear();

for (int i = -margen; i < keyPoints.Count + margen; i++)
{
c = 1.0f;

for (int n = -margen; n < keyPoints.Count + margen; n++)
{
if (n != i) c /= (i - n);
}
cList.Add©;
}
}
//-------------------------------------------------------------------------------------------
/// Constructor
public void Clear()
{
keyPoints.Clear();
}
//-------------------------------------------------------------------------------------------
/// Constructor
public void Restart()
{
actualLength = 0;
endState = false;
}
//-------------------------------------------------------------------------------------------
/// Constructor
public Vector3 GetPoint(float length)
{
length = length * (keyPoints.Count - 1) / totalLength;

lastValue.X = 0;
lastValue.Y = 0;
lastValue.Z = 0;

for (int i = -margen; i < keyPoints.Count + margen; i++)
{
c = 1.0f;

for (int n = -margen; n < keyPoints.Count + margen; n++)
{
if (n != i)
{
c *= (length - n);
}
}
c *= cList[i];

int j = (i + keyPoints.Count) % keyPoints.Count;
lastValue.X += c * keyPoints[j].X;
lastValue.Y += c * keyPoints[j].Y;
lastValue.Z += c * keyPoints[j].Z;
}
return lastValue;
}
//-------------------------------------------------------------------------------------------
/// Constructor
public Vector2 GetPoint2D(float length)
{
Vector3 point = GetPoint(length);
return (new Vector2(point.X, point.Y));
}
//-------------------------------------------------------------------------------------------
/// Constructor
public Vector3 GetActualPoint()
{
return GetPoint(actualLength);
}
//-------------------------------------------------------------------------------------------
/// Constructor
public Vector2 GetActualPoint2D()
{
Vector3 actualPoint = GetPoint(actualLength);
return new Vector2(actualPoint.X, actualPoint.Y);
}
//-------------------------------------------------------------------------------------------
/// Constructor
public float GetLengthProportion()
{
return (float)(actualLength / totalLength);
}
//-------------------------------------------------------------------------------------------
/// Constructor
public void UpdateUntilEnd(float multiplier)
{
if (!endState)
{
float suma = multiplier*dBase.FSpeed;
actualLength += suma;
if (actualLength > totalLength)
{
actualLength = totalLength;
endState = true;
}
}
if (actualLength == 0) endState = false;
}
//-------------------------------------------------------------------------------------------
/// Constructor
public void Update(float multiplier)
{
actualLength += multiplier*dBase.FSpeed;
if (actualLength > totalLength - 1) Restart();
}
}
[/code]


Un consejo, create un sistema de coordenadas propio que se autocalcule en base a lo que tengas en cada momento, así si cambias la resolución no tienes que cambiar todo el código :P

  • Ellolo17

  • Zodiark

  • vida restante: 100%
  • Registrado: 16 nov 2006
  • Mensajes: 6.208
#3

Escrito 17 febrero 2010 - 22:27

Pues prueba a medir la distancia horizontal del objeto que dispara, luego la vertical y con eso tienes el vector que tiene que seguir el proyectil. Solo tienes que reducir el modulo a lo que sea necesario ;)

Hago 2 vectores a partir del origen y el destino, el primer vector es el que va del 700,500 al 4,512 y el otro es el que va del 700,500 al 4,500 y calculo el angulo entre estos 2 vectores.


Creo que eso que haces esta mal porque no entiendo para que lo haces. Con lo que te digo el primer vector es la distancia de 700 a 4 y el segundo de 500 a 512. Sumandolos tienes el vector de trayectoria del proyectil. Pero si sigue ese vector impacta instantaneamente asi que haz que en vez de recorrer toda la distancia "de un tiron" la haga de poco en poco para simular el movimiento.

Luego el angulo sale de calcular el seno o el coseno.
Imagen Enviada

a/c es el seno del angulo. a es la distancia de 500 a 512 -en este caso 12- y c es el resultado de la suma de los otros dos vectores

Espero haber sido de ayuda ;) -aunque en mi motor hay funciones que hacen que el objeto apunte automaticamente al objetivo, luego seria solo hacer que se vaya moviendo-

Un saludo.

#4

Escrito 17 febrero 2010 - 22:43

[code:1] public void disparo()
{
int x, y;
float sin, cos;
x = Convert.ToInt32(getPos().X);
y = Convert.ToInt32(getPos().Y);

Vector2 u, v;
v = new Vector2(destino.X - origen.X, origen.Y);
u = new Vector2(destino.X - origen.X, destino.Y - origen.Y);
encontrarAngulo(v, u);

sin = (float)Math.Sin(angulo);
cos = (float)Math.Cos(angulo);

if (ID != 0)
{
setPos(x - velocidad * cos, y - velocidad * sin);
}
else setPos(x + velocidad, y);

if (pos.X > 1030 || pos.X < -5 || pos.Y > 770 || pos.Y < -5) destroy();
}


public void encontrarAngulo(Vector2 v, Vector2 u)
{
double producto,mv,mu;

producto=v.X * u.X + v.Y * u.Y;
mv=Math.Sqrt(Math.Pow(v.X,2)+Math.Pow(v.Y,2));
mu = Math.Sqrt(Math.Pow(u.X, 2) + Math.Pow(u.Y, 2));
angulo = (float)Math.Acos(producto / (mv * mu));
}[/code]

Esta es la parte de mi codigo en la que calculo el angulo, el origen y el destino forman parte de los atributos


EDITO: He hecho servir la funcion Atan2, y creo que ya la cosa va a mejor, pero me falta ajustar el angulo, aunque me gustaria saber para que sirve exactamente y como encuentra un angulo entre 2 puntos

  • Ellolo17

  • Zodiark

  • vida restante: 100%
  • Registrado: 16 nov 2006
  • Mensajes: 6.208
#5

Escrito 18 febrero 2010 - 01:32

Atan2 te devuelve en radianes el angulo de dos puntos. Pero te lo devuelve en radianes. Aqui tienes un link a la documentacion de c++ con informacion sobre esto:
http://www.cplusplus...ry/cmath/atan2/

Haces bien en usar Atan2 en vez de Atan porque Atan2 soluciona el problema del cuadrante en que se halla -aun asi seria facil de ver viendo si un punto esta por encima o abajo y a la derecha o a la izquierda del otro-

Te pongo un par de ejemplos de atan2

atan2(10-20, 10-2) = -51.34 degrees
atan2(20-10, 2-10) = 128.66 degrees

Puede que el problema venga de colocar mal las coordenadas aqui :S Recuerda que es Atan2(y1-y2,x1-x2)

Un saludo.

  • Sante05

  • Methuselah

  • vida restante: 100%
  • Registrado: 21 jul 2001
  • Mensajes: 182
#6

Escrito 18 febrero 2010 - 20:02

¿Por que no tomas simplemente el vector entre los dos puntos, lo normalizas, y multiplicas la velocidad por las componentes resultantes?

  • The_Hans

  • Ultima

  • vida restante: 100%
  • Registrado: 27 ene 2004
  • Mensajes: 7.490
#7

Escrito 18 febrero 2010 - 21:46

¿Por que no tomas simplemente el vector entre los dos puntos, lo normalizas, y multiplicas la velocidad por las componentes resultantes?


Eso es lo que hace básicamente mi clase recta. No sé para qué quieren calcular ángulos xDD

#8

Escrito 19 febrero 2010 - 00:10

Que es exactamente lo que quieres hacer? Porque a mi me da que no te hace falta calcular ningun angulo entre dos vectore.
Creo que lo que quieres hacer es que el disparo enemigo se diriga a un punto. Si es asi, la solucion es lo que te ha dicho sante y hans.

Un saludo



PD: Hay una cosa que no me gusta en tu codigo. En la funcion

[code:1] public void encontrarAngulo(Vector2 v, Vector2 u)
{
double producto,mv,mu;

producto=v.X * u.X + v.Y * u.Y;
mv=Math.Sqrt(Math.Pow(v.X,2)+Math.Pow(v.Y,2));
mu = Math.Sqrt(Math.Pow(u.X, 2) + Math.Pow(u.Y, 2));
angulo = (float)Math.Acos(producto / (mv * mu));
}
[/code]

Esta funcion el lugar de ser void, haz que devuelva el angulo. Luego haces
[code:1]angulo = encontrarAngulo(v,u)[/code]
Aunque sin el codigo entero tampoco se exactamente lo que quieres hacer. A lo mejor para lo que tu querias hacer ese codigo esta bien.


Este tema ha sido archivado. Esto significa que no puedes responder en este tema.
publicidad
publicidad