He estado pensando en otras cosas o cansado, pero aquí vuelvo
Fíjate bien lo que te puse, y de ser necesario, ve las explicaciones de mensajes anteriores, porque además te estás saltando algún paso.
Vale, luego de leer tu post.
Por ejemplo, aquí:
DistanciahastaRed = DistanciahastaDestinoXY*Math.abs(RedZ-BallZ)/Math.abs(DistanciahastaDestinoY);
En vez de RedZ-BallZ va RedY - BallY.
Ok, asumí que RedY era la altura así que lo llamé RedZ, y cuando vi -BallY me confundí y lo cambié por BallZ, supongo que fue por eso. Me confunde que Y sea Z...
Luego, como te comenté (y expliqué con más detalle en mi segundo mensaje), si Disc es negativo, no puedes hacer el tiro, no tienes la fuerza necesaria, así que ya ni siquiera tiene sentido calcular los ángulos y todo lo demás.
¿Qué sucede si se calcula? Pensé que el tiro saldría corto ¿no ocurre eso? Si no se tiene suficiente fuerza igual debe haber un tiro, como si el personaje hubiera querido hacer un buen tiro. No tiene sentido que no se haga un tiro.
En todo caso, podrías escoger un nuevo destino más cercano, como lo mencionaste en relación al punto 5, y repetir el procedimiento a ver si este sí lo puedes hacer.
Sí ¿y si el más cercano tampoco sirve?
Además, fíjate que después de calcular AlturaEnRedTheta y AlturaEnRedPhi puse:
"Si ambos pasan por encima, elige el más alto o bajo, según prefieras"
Pero tú ya directamente supones que ambos lo hacen y pasas a elegir el ángulo más bajo. Primero debes comprobar si alguno de los ángulos pasa por arriba de la red. Si sólo uno de los ángulos lo hace, obviamente lo tienes que usar, no hay otra opción. Únicamente si ambos pasan sobre la red, toca elegir el más bajo o alto. Y naturalmente, si ninguno lo consigue, toca aquí también elegir nuevo destino o lo que quieras.
Pensé que tu método indicaba si pasaban o no.
...
Se me hizo líoso hacerlo, pero creo que está. También corregí, creo, que en vez de usar los ángulos usaba las alturas. ¿Disc qué abrevia o qué significa?
este cálculo no es correcto:
BallZSpeed = BallZ-BallZAnterior;
porque simplemente te daría la velocidad promedio a lo largo del último intervalo de tiempo, en este caso, el último segundo (porque creo que incrementas el tiempo un segundo a la vez, ¿no?), pero tú lo que buscas es el valor de la velocidad en el instante actual, que es distinto. La forma de hacerlo sería esta:
BallZSpeed = VelocidadZ - Gravedad * Tiempotranscurridodeltiro;
Creo que eso me quedó sin borrar del método anterior, creo que no influye salvo en el pique, que aún no lo arreglé. Gracias igual, si llego a eso y no lo puedo resolver te consulto. Lo borro por ahora a ver.
Una última cosa: supongo que estás usando escalas adecuadas, ¿no?
Probablemente no, el personaje es muy bajo por ejemplo. En el juego original los saques se hacen por encima del alcance de la raqueta y no se nota, pero en mi caso son acordes al alcance y me parece un poco bajo. El juego original:
https://www.youtube.com/watch?v=fc0_P2IGgJ4¿Crees que habrá mucha diferencia en las escalas con el mío? Que yo sepa lo único que hice fue que la cancha entrara toda en la pantalla y también parte del fondo y los laterales, ya que en el juego original hay como paredes invisibles que si la bola toca es punto
(no se nota salvo casos muy poco probables). También achiqué un poco al personaje porque ahí pareciera que ocuparan demasiado de la cancha.
En mi juego la cancha mide 239.9 de ancho (sin contar el corredor de dobles) y 480 de largo. Ese .9 es por motivos que no comprendo ni sé arreglar (ni volveré a dedicar tiempo a intentarlo), pero es mínimo, debería ser 240. La altura de la red es de unos 17.4 (pero la bola debe pasar a 18.6 mínimo). En fin, puse la gravedad que me pareció agradable a mi vista, no creo que sea ni tan poca ni tanta, pero ahora que lo dices, si con tu método es menos intensa aunque se use el mismo número, puede que deba aumentarla.
En el juego la fuerza de los tiros depende del tiempo de preparación, la fuerza de brazos, piernas, calidad de la raqueta y cuánto se usó en darle efecto a la bola, no sé si algo más... No he pensado o consultado tanto sobre eso. No tiene sentido que te de más detalles de eso tampoco ¿no? Te digo porque me llama la atención el 25 que pusiste ¿a qué se debe?
No tengo mucho interés en que sea tan realista en cuanto a velocidades o tamaños (especialmente la bola, sino no la veo mucho), tengo pensado que haya gran variedad de campeonatos variando el tamaño de algunas cosas por ejemplo. En otros aspectos sí quiero que sea realista, como he explicado.
Bueno, reviso el código de principio a fin, lo comparo con lo que has puesto.
Pusiste:
Disc = v0 * v0 * v0 * v0 - (g * (g * DistanciahastaDestinoXY * DistanciahastaDestinoXY - 2 * v0 * v0 * h))
Puse:
Parte = AlexFuerzadetiros*AlexFuerzadetiros*AlexFuerzadetiros*AlexFuerzadetiros;
Disc = Parte-(Gravedad*(Gravedad*DistanciahastaDestinoXY*DistanciahastaDestinoXY-2*AlexFuerzadetiros*AlexFuerzadetiros*BallZ));
Luego debido a los ifs es complicado poner parte por parte, lo haré más adelante; este es el código con las partes más relevantes de este contexto supongo:
Gravedad = 0.01;
RedZ = 18.6;
// Datos para determinar velocidades del tiro.
DistanciahastaDestinoX = BallX-280.1;
DistanciahastaDestinoY = BallY-180;
// DistanciahastaRed = BallY-300;
// DistanciahastaMinsafeBallZ = BallZ-18.6;
DistanciahastaDestinoXY = Math.sqrt(DistanciahastaDestinoX*DistanciahastaDestinoX+DistanciahastaDestinoY*DistanciahastaDestinoY);
// Se calcula el ángulo o ángulos.
Parte = AlexFuerzadetiros*AlexFuerzadetiros*AlexFuerzadetiros*AlexFuerzadetiros;
Disc = Parte-(Gravedad*(Gravedad*DistanciahastaDestinoXY*DistanciahastaDestinoXY-2*AlexFuerzadetiros*AlexFuerzadetiros*BallZ));
if (Disc<0) {
// Fuerza insuficiente, si es posible habrá que acercar el destino.
TiroRealizable = "Fuerza insuficiente";
} else {
DistanciahastaRed = DistanciahastaDestinoXY*Math.abs(RedZ-BallY)/Math.abs(DistanciahastaDestinoY);
// Le he llamado RedZ a lo que has llamado RedY.
// Se calcula el ángulo Z;
theta = Math.atan((AlexFuerzadetiros*AlexFuerzadetiros+Math.sqrt(Disc))/(Gravedad*DistanciahastaDestinoXY));
// Se calcula el tiempo que se tarda en llegar a la red.
TiempoHastaRedTheta = (DistanciahastaRed)/(AlexFuerzadetiros*Math.cos(theta));
// Se calcula la altura a la que pasa la bola al llegar a la red.
AlturaEnRedTheta = BallZ+AlexFuerzadetiros*Math.sin(theta)*TiempoHastaRedTheta-(Gravedad*TiempoHastaRedTheta*TiempoHastaRedTheta)/2;
if (Disc == 0) {
// Sólo hay 1 ángulo Z posible.
if (AlturaEnRedTheta>=18.6) {
// Si la bola pasa la red con ese ángulo, usarlo.
TiroRealizable = "1 ángulo, ok";
Angulo = theta;
} else {
TiroRealizable = "1 ángulo, imposible";
// Probar acercar destino supongo.
Angulo = "Ninguno";
}
} else {
// Disc>0, hay 2 angulos posibles, calcular el faltante.
phi = Math.atan((AlexFuerzadetiros*AlexFuerzadetiros-Math.sqrt(Disc))/(Gravedad*DistanciahastaDestinoXY));
TiempoHastaRedPhi = (DistanciahastaRed)/(AlexFuerzadetiros*Math.cos(phi));
AlturaEnRedPhi = BallZ+AlexFuerzadetiros*Math.sin(phi)*TiempoHastaRedPhi-(Gravedad*TiempoHastaRedPhi*TiempoHastaRedPhi)/2;
if (AlturaEnRedTheta>=18.6) {
if (AlturaEnRedPhi>=18.6) {
// Con ambos ángulos la bola pasa la red, usar el más bajo.
if (AlturaEnRedTheta<AlturaEnRedPhi) {
Angulo = theta;
} else {
Angulo = phi;
}
} else {
// Sólo sirve el 1er ángulo.
Angulo = theta;
}
} else if (AlturaEnRedPhi>=18.6) {
// Sólo sirve el 2ndo ángulo.
Angulo = phi;
} else {
// Ningún ángulo sirve.
Angulo = "Ninguno";
// Probar acercar destino supongo.
}
}
if (Angulo != "Ninguno") {
// Cálculo de las velocidades.
VelocidadAdelante = AlexFuerzadetiros*Math.cos(Angulo);
VelocidadArriba = AlexFuerzadetiros*Math.sin(Angulo);
// Esta variable es redundante, pero clarifica lo que se esta haciendo.
VelocidadZ = VelocidadArriba;
if (DistanciahastaDestinoX == 0) {
BallXSpeed = 0;
BallYSpeed = VelocidadAdelante*-1;
} else {
AnguloPendiente = Math.atan(DistanciahastaDestinoY/DistanciahastaDestinoX);
BallXSpeed = VelocidadAdelante*Math.cos(AnguloPendiente)*-1;
BallYSpeed = VelocidadAdelante*Math.sin(AnguloPendiente)*-1;
}
// Datos para determinar posiciones de la pelota.
Tiempotranscurridodeltiro = 0;
BallXwhenstriked = BallX;
BallYwhenstriked = BallY;
BallZwhenstriked = BallZ;
}
}
Dijiste:
Si Disc < 0, no puedes hacer el tiro
Puse:
if (Disc<0) {
// Fuerza insuficiente, si es posible habrá que acercar el destino.
TiroRealizable = "Fuerza insuficiente";
} else {
Y que yo sepa nada ocurrirá ahí, en el caso de true. Lo de cambiar destino lo haré más adelante.
Dijiste:
Si Disc == 0, hay un solo angulo posible
Si Disc > 0, hay dos angulos
Esos casos son mi else, calculo 1 ángulo y luego si Disc>0 calculo el otro. Alteré un poco el orden de tu código pero creo que no hay problema.
Haces:
theta = arctan((v0 * v0 + raiz(Disc)) / (g * DistanciahastaDestinoXY))
DistanciahastaRed = DistanciahastaDestinoXY * abs(RedY - BallY) / abs(DistanciahastaDestinoY)
TiempoHastaRedTheta = (DistanciahastaRed) / (v0 * cos(theta))
Hice:
DistanciahastaRed = DistanciahastaDestinoXY*Math.abs(RedZ-BallY)/Math.abs(DistanciahastaDestinoY);
// Le he llamado RedZ a lo que has llamado RedY.
theta = Math.atan((AlexFuerzadetiros*AlexFuerzadetiros+Math.sqrt(Disc))/(Gravedad*DistanciahastaDestinoXY));
TiempoHastaRedTheta = (DistanciahastaRed)/(AlexFuerzadetiros*Math.cos(theta));
AlturaEnRedTheta = BallZ+AlexFuerzadetiros*Math.sin(theta)*TiempoHastaRedTheta-(Gravedad*TiempoHastaRedTheta*TiempoHastaRedTheta)/2;
Lo último te lo copié de esto:
AlturaEnRedTheta = h + v0 * sen(theta) * TiempoHastaRedTheta - (g * TiempoHastaRedTheta * TiempoHastaRedTheta) / 2
Luego lo que hago depende del caso como habías dicho:
if (Disc == 0) {
// Sólo hay 1 ángulo Z posible.
if (AlturaEnRedTheta>=18.6) {
// Si la bola pasa la red con ese ángulo, usarlo.
TiroRealizable = "1 ángulo, ok";
Angulo = theta;
} else {
TiroRealizable = "1 ángulo, imposible";
// Probar acercar destino supongo.
Angulo = "Ninguno";
}
} else {
// Disc>0, hay 2 angulos posibles, calcular el faltante.
phi = Math.atan((AlexFuerzadetiros*AlexFuerzadetiros-Math.sqrt(Disc))/(Gravedad*DistanciahastaDestinoXY));
TiempoHastaRedPhi = (DistanciahastaRed)/(AlexFuerzadetiros*Math.cos(phi));
AlturaEnRedPhi = BallZ+AlexFuerzadetiros*Math.sin(phi)*TiempoHastaRedPhi-(Gravedad*TiempoHastaRedPhi*TiempoHastaRedPhi)/2;
if (AlturaEnRedTheta>=18.6) {
if (AlturaEnRedPhi>=18.6) {
// Con ambos ángulos la bola pasa la red, usar el más bajo.
if (AlturaEnRedTheta<AlturaEnRedPhi) {
Angulo = theta;
} else {
Angulo = phi;
}
} else {
// Sólo sirve el 1er ángulo.
Angulo = theta;
}
} else if (AlturaEnRedPhi>=18.6) {
// Sólo sirve el 2ndo ángulo.
Angulo = phi;
} else {
// Ningún ángulo sirve.
Angulo = "Ninguno";
// Probar acercar destino supongo.
}
}
¿Todo bien?
Habías dicho:
phi = arctan((v0 * v0 - raiz(Disc)) / (g * DistanciahastaDestinoXY))
TiempoHastaRedPhi = (DistanciahastaRed) / (v0 * cos(phi))
AlturaEnRedPhi = h + v0 * sen(phi) * TiempoHastaRedPhi - (g * TiempoHastaRedPhi * TiempoHastaRedPhi) / 2
Yo ahí puse:
phi = Math.atan((AlexFuerzadetiros*AlexFuerzadetiros-Math.sqrt(Disc))/(Gravedad*DistanciahastaDestinoXY));
TiempoHastaRedPhi = (DistanciahastaRed)/(AlexFuerzadetiros*Math.cos(phi));
AlturaEnRedPhi = BallZ+AlexFuerzadetiros*Math.sin(phi)*TiempoHastaRedPhi-(Gravedad*TiempoHastaRedPhi*TiempoHastaRedPhi)/2;
Luego dices:
VelocidadAdelante = v0 * cos(angulo)
VelocidadArriba = v0 * sen(angulo)
// Esta variable es redundante, pero clarifica lo que se esta haciendo
VelocidadZ = VelocidadArriba
Puse:
if (Angulo != "Ninguno") {
// Cálculo de las velocidades.
VelocidadAdelante = AlexFuerzadetiros*Math.cos(Angulo);
VelocidadArriba = AlexFuerzadetiros*Math.sin(Angulo);
// Esta variable es redundante, pero clarifica lo que se esta haciendo.
VelocidadZ = VelocidadArriba;
Dices:
si (DistanciahastaDestinoX == 0)
BallYSpeed = VelocidadAdelante
BallXSpeed = 0
sino
AnguloPendiente = arctan(DistanciahastaDestinoY / DistanciahastaDestinoX)
BallXSpeed = VelocidadAdelante * cos(AnguloPendiente)
BallYSpeed = VelocidadAdelante * sen(AnguloPendiente)
Puse:
if (DistanciahastaDestinoX == 0) {
BallXSpeed = 0;
BallYSpeed = VelocidadAdelante*-1;
} else {
AnguloPendiente = Math.atan(DistanciahastaDestinoY/DistanciahastaDestinoX);
BallXSpeed = VelocidadAdelante*Math.cos(AnguloPendiente)*-1;
BallYSpeed = VelocidadAdelante*Math.sin(AnguloPendiente)*-1;
}
Los *-1 los quito si veo que los tiros van para atrás o algo.
Los cambios en la pantalla por el movimiento los hago así:
Tiempotranscurridodeltiro = Tiempotranscurridodeltiro+1;
BallX = BallXwhenstriked+BallXSpeed*Tiempotranscurridodeltiro;
BallY = BallYwhenstriked+BallYSpeed*Tiempotranscurridodeltiro;
BallZ = BallZwhenstriked+VelocidadZ*Tiempotranscurridodeltiro-(Gravedad*Tiempotranscurridodeltiro*Tiempotranscurridodeltiro)/2;
Veo que habías dicho:
x = v0 * cos(θ) * t
y = v0 * sen(θ) * t - (g * t2)/2
Y más recientemente:
BallZ = h + VelocidadZ * t - (g * t * t)/2
Ahí me maree. BallZ lo tengo igual parece, lo otro no y como es la perspectiva 2d me confundo.
Bueno, pruebo el código, grabo video y demás y te muestro el resultado.
...
De momento me da errores gráficos no sé bien por qué (la pantalla se agranda o se achica, me ha pasado otras veces cuando se ordena modificar el tamaño de un objeto inexistente). He probado con distintas fuerzas y obtuve casos de fuerza insuficiente y de 2 ángulos pero que ninguno sirve. Agregué más estados de la variable TiroRealizable para ver eso. No sé, voy a probar quitar el código e irlo poniendo de a poco a ver cual es el problema. O pruebo desde la versión anterior mejor.
...
Ya arreglé el tema de los gráficos pero sólo obtengo casos de fuerza insuficiente si le pongo muy poca o casos de "2 ángulos pero ninguno sirve".
Por ejemplo:
Gravedad = 0.01
RedZ = 18.6
AlexFuerzadetiros = 2.975
AlexX = 488
AlexY = 548
DistanciahastaDestinoX = 240.9
DistanciahastaDestinoY = 368
DistanciahastaDestinoXY = 439.837253992883
Parte = 78.333562890625
Disc = 66.960524890625
DistanciahastaRed = 632.744136586501
theta = 1.31809820149062
TiempoHastaRedTheta = 850.689542137363
AlturaEnRedTheta = -1122.89688542468
phi = 0.150652307511496
TiempoHastaRedPhi = 215.123727738993
AlturaEnRedPhi = -90.2989548532666
TiroRealizable = "2 ángulos, pero ninguno sirve"
Angulo = "Ninguno"
BallXwhenstriked = 521
BallYwhenstriked = 548
BallZwhenstriked = 45.04
Medio supongo que tiene que ver con esas alturas negativas en la red, voy a revisar si hice algo mal ahí y si es el caso edito el post medio pronto, sino es que no sé.