visualizador visor ver pantalla online grados giro girar girada fotos como algorithm

algorithm - visor - Hacer que la computadora se realice 360 grados=0 grados, girando una torreta de pistola



visor fotos 360 online (14)

Normalizado a [0,360):

(Es decir, un medio rango abierto)

Utilice el operador de módulo para realizar "obtener el resto de la división":

361 % 360

será 1.

En lenguajes de estilo C / C ++ / ... esto sería

gundeg %= 360

Nota (gracias a un comentario): si gundeg es un tipo de punto flotante, necesitará usar una función de biblioteca, en C / C ++: fmod, o hacerlo usted mismo (.NET):

double FMod(double a, double b) { return a - Math.floor(a / b) * b; }

¿Qué manera de girar?

Cualquiera sea el camino más corto (y si el giro es 180 °, entonces la respuesta es arbitraria), en C #, y suponiendo que la dirección se mide en sentido contrario a las agujas del reloj

TurnDirection WhichWayToTurn(double currentDirection, double targetDirection) { Debug.Assert(currentDirection >= 0.0 && currentDirection < 360.0 && targetDirection >= 0.0 && targetDirection < 360.0); var diff = targetDirection - currentDirection ; if (Math.Abs(diff) <= FloatEpsilon) { return TurnDirection.None; } else if (diff > 0.0) { return TurnDirection.AntiClockwise; } else { return TurnDirection.Clockwise; } }

NÓTESE BIEN. Esto requiere pruebas.

Observe el uso de asert para confirmar la condición previa de los ángulos normalizados, y yo uso una aserción porque esta es una función interna que no debe recibir datos no verificados. Si se tratara de una función generalmente reutilizable, la verificación de argumentos debería generar una excepción o devolver un error (según el idioma).

También tenga en cuenta. para resolver cosas como esta no hay nada mejor que un lápiz y un papel (mi versión inicial estaba equivocada porque estaba mezclando usando (-180,180] y [0,360).

Estoy haciendo un juego y en él hay una torreta de armas controlada por computadora. La torreta puede girar 360 grados.

Utiliza trigonometría para averiguar el ángulo que necesita para apuntar la pistola (objdeg) y el ángulo actual de la pistola se almacena en (gundeg)

El siguiente código hace girar la pistola a una velocidad establecida.

if (objdeg > gundeg) { gundeg++; } if (objdeg < gundeg) { gundeg--; }

El problema es que si hay un objeto a 10 grados, la pistola gira, lo dispara y lo destruye, si aparece otro objetivo a 320 grados, la pistola girará 310 grados en sentido contrario a las agujas del reloj en lugar de simplemente girar 60 grados en el sentido de las agujas del reloj para golpearlo.

¿Cómo puedo corregir mi código para que no actúe de forma estúpida?


¿Intenta dividir por 180 usando la división de enteros y los giros según el resultado par / impar?

749/180 = 4 Por lo tanto, gire hacia la derecha 29 grados (749% 180)

719/180 = 3 Por lo tanto, gire hacia la izquierda 1 grado (180 - 719% 180)


A riesgo de ir en bicicleta, almacenar grados como un número entero en lugar de como su propia clase podría ser un caso de "obsesión primitiva". Si recuerdo correctamente, el libro "El programador pragmático" sugirió crear una clase para almacenar títulos y realizar operaciones en ellos.


Aquí está el ejemplo de pseudo código de prueba corta que se me ocurre que responde al problema. Funciona en su dominio de ángulos positivos 0..359 y maneja las condiciones de borde antes de manejar las condiciones "normales".

if (objdeg >= 180 and gundeg < 180) gundeg = (gundeg + 359) % 360; else if (objdeg < 180 and gundeg >= 180) gundeg = (gundeg + 1) % 360; else if (objdeg > gundeg) gundeg = (gundeg + 1) % 360; else if (objdeg < gundeg) gundeg = (gundeg + 359) % 360; else shootitnow();


Aquí hay una muestra de C # de workign, esto girará en la dirección correcta. :

public class Rotater { int _position; public Rotater() { } public int Position { get { return _position; } set { if (value < 0) { _position = 360 + value; } else { _position = value; } _position %= 360; } } public bool RotateTowardsEx(int item) { if (item > Position) { if (item - Position < 180) { Position++; } else { Position--; } return false; } else if (Position > item) { if (Position - item < 180) { Position--; } else { Position++; } return false; } else { return true; } } } static void Main(string[] args) { do { Rotater rot = new Rotater(); Console.Write("Enter Starting Point: "); var startingPoint = int.Parse(Console.ReadLine()); rot.Position = startingPoint; int turns = 0; Console.Write("Enter Item Point: "); var item = int.Parse(Console.ReadLine()); while (!rot.RotateTowardsEx(item)) { turns++; } Console.WriteLine(string.Format("{0} turns to go from {1} to {2}", turns, startingPoint, item)); } while (Console.ReadLine() != "q"); }

Crédito a John Pirie por inspiración

Edit: no estaba contento con mi establecedor de posición, así que lo limpié


Así es como implementé algo similar en un juego recientemente:

double gundeg; // ... double normalizeAngle(double angle) { while (angle >= 180.0) { angle -= 360.0; } while (angle < -180.0) { angle += 360.0; } return angle; } double aimAt(double objAngle) { double difference = normalizeAngle(objdeg - gundeg); gundeg = normalizeAngle(gundeg + signum(difference)); }

Todas las variables de ángulo están restringidas a -180 .. + 180, lo que facilita este tipo de cálculo.


Debe decidir si va a girar hacia la izquierda o hacia la derecha, según cuál sea la distancia más corta. Entonces tendrás que tomar módulo:

if (objdeg > gundeg) { if (objdeg - gundeg < 180) { gundeg++; } else { gundeg--; } } if (objdeg < gundeg) { if (gundeg - objdeg < 180) { gundeg--; } else { gundeg++; } } if (gundeg < 0) { gundeg += 360; } gundeg = gundeg % 360;


El problema es encontrar la dirección que dará la distancia más corta.

Sin embargo , la resta puede dar lugar a números negativos y eso debe tenerse en cuenta.
Si está moviendo la pistola un paso en cada comprobación, no sé cuándo hará el módulo.
Y, si desea mover el arma en un solo paso, simplemente debe agregar / restar el delta correctamente.

Con este fin, Kirschstein parece estar pensando más cerca de mí.
Estoy trabajando con un entero en este simple psudo-código.

if (objdeg != gundeg) { // we still need to move the gun delta = gundeg - objdeg if (delta > 0) if (unsigned(delta) > 180) gundeg++; else gundeg--; else // delta < 0 if (unsigned(delta) > 180) gundeg--; else gundeg++; if (gundeg == 360) gundeg = 0; else if (gundeg == -1) gundeg = 359; }

Intente trabajar esto de manera incremental con gundeg = 10 y objdeg = 350 para ver cómo el gundeg se moverá de 10 a 0 y luego de 359 a 350.


En realidad, hay una manera más fácil de abordar este problema. El producto cruzado de dos vectores le da un vector que representa lo normal (por ejemplo, perpendicular). Como un artefacto de esto, dados dos vectores a, b, que se encuentran en el plano xy, axb = c implica c = (0,0, + -1).

El signo de la componente z de c (p. Ej., Si sale o va al plano xy) depende de si se trata de un giro z a la izquierda o a la derecha para que a sea igual a b.

Vector3d turret Vector3d enemy if turret.equals(enemy) return; Vector3d normal = turret.Cross(enemy); gundeg += normal.z > 0 ? 1 : -1; // counter clockwise = +ve


Esto podría ser un poco tarde ... Probablemente muy tarde ... Pero recientemente tuve un problema similar y encontré que esto funcionó bien en GML.

var diff = angle_difference(gundeg, objdeg) if (sign(diff)>0){ gundeg --; }else{ gundeg ++; }


Puede evitar la división (y el mod) por completo si representa sus ángulos en algo denominado ''BAMS'', que significa Sistema de medición de ángulo binario. La idea es que si almacena sus ángulos en un entero de N bits, use todo el rango de ese entero para representar el ángulo. De esa manera, no hay necesidad de preocuparse por el desbordamiento más allá de 360, porque las propiedades naturales de módulo-2 ^ N de su representación se encargan de usted.

Por ejemplo, digamos que usas 8 bits. Esto corta tu círculo en 256 orientaciones posibles. (Puedes elegir más bits, pero 8 es conveniente para el ejemplo). Deje que 0x00 represente 0 grados, 0x40 significa 90 grados, 0x80 es 180 grados y 0xC0 es 270 grados. No se preocupe por el bit de ''signo'', una vez más, BAMS es algo natural para los ángulos. Si interpreta 0xC0 como "sin signo" y se escala a 360/256 grados por conteo, su ángulo es (+192) (360/256) = +270; pero si interpreta 0xC0 como ''firmado'', su ángulo es (-64) (360/256) = -90. Observe que -90 y +270 significan lo mismo en términos angulares.

Si desea aplicar funciones trigonométricas a los ángulos de BAMS, puede realizar un cálculo previo de las tablas. Hay trucos para reducir las tablas, pero puedes ver que las tablas no son tan grandes. Para almacenar una tabla completa de seno y coseno de valores de doble precisión para BAMS de 8 bits no se necesita más de 4 K de memoria, la alimentación de pollos en el entorno actual.

Ya que menciona el uso de esto en un juego, probablemente podría salirse con las representaciones de 8 o 10 bits. Cada vez que agregue o reste ángulos, puede forzar el resultado en N bits utilizando una operación lógica AND, por ejemplo, ángulo & = 0x00FF para 8 bits.

Olvidó la mejor parte (editar)

El problema de giro a la derecha frente a giro a la izquierda se resuelve fácilmente en un sistema BAMS. Solo toma la diferencia y asegúrate de mantener solo los N bits significativos. Interpretar el MSB como un bit de signo indica de qué manera debe girar. Si la diferencia es negativa, gire a la inversa por el abs () de la diferencia.

Este pequeño y feo programa de C demuestra. Trate de darle entrada como 20 10 y 20 30 al principio. Luego trata de engañarlo envolviendo el punto cero. Dale 20 -10, girará a la izquierda. Dale 20 350, todavía gira a la izquierda. Tenga en cuenta que, dado que está hecho en 8 bits, ese 181 es indistinguible de 180, así que no se sorprenda si lo alimenta 20 201 y gira a la derecha en lugar de a la izquierda, en la resolución proporcionada por ocho bits, girando a la izquierda y girando a la derecha este caso son los mismos. Poner en 20 205 y se irá por el camino más corto.

#include <stdio.h> #include <math.h> #define TOBAMS(x) (((x)/360.0) * 256) #define TODEGS(b) (((b)/256.0) * 360) int main(void) { double a1, a2; // "real" angles int b1, b2, b3; // BAMS angles // get some input printf("Start Angle ? "); scanf("%lf", &a1); printf("Goal Angle ? "); scanf("%lf", &a2); b1 = TOBAMS(a1); b2 = TOBAMS(a2); // difference increases with increasing goal angle // difference decreases with increasing start angle b3 = b2 - b1; b3 &= 0xff; printf("Start at %7.2lf deg and go to %7.2lf deg/n", a1, a2); printf("BAMS are 0x%02X and 0x%02X/n", b1, b2); printf("BAMS diff is 0x%02X/n", b3); // check what would be the ''sign bit'' of the difference // negative (msb set) means turn one way, positive the other if( b3 & 0x80 ) { // difference is negative; negate to recover the // DISTANCE to move, since the negative-ness just // indicates direction. // cheap 2''s complement on an N-bit value: // invert, increment, trim b3 ^= -1; // XOR -1 inverts all the bits b3 += 1; // "add 1 to x" :P b3 &= 0xFF; // retain only N bits // difference is already positive, can just use it printf("Turn left %lf degrees/n", TODEGS(b3)); printf("Turn left %d counts/n", b3); } else { printf("Turn right %lf degrees/n", TODEGS(b3)); printf("Turn right %d counts/n", b3); } return 0; }


Si necesita girar más de 180 grados en una dirección para apuntar la torreta, entonces sería más rápido girar en la otra dirección.

Solo revisaría esto y luego giraría en la dirección apropiada

if (objdeg != gundeg) { if ((gundeg - objdeg) > 180) gundeg++; else gundeg--; }

EDITAR: Nueva solución

He refinado mi solución en base a los comentarios de los comentarios. Esto determina si el objetivo está a la izquierda o derecha de la torreta y decide en qué dirección girar. Luego invierte esta dirección si el objetivo está a más de 180 grados de distancia.

if (objdeg != gundeg) { int change = 0; int diff = (gundeg - objdeg)%360; if (diff < 0) change = 1; else change = -1; if (Math.Abs(diff) > 180) change = 0 - change; gundeg += change; }


Solo compara lo siguiente:

gundeg - objdeg objdeg - gundeg gundeg - objdeg + 360 objdeg - gundeg + 360

y elige el que tenga mínimo valor absoluto .


Tiendo a favorecer una solución que

  • No tiene muchas declaraciones anidadas si
  • no supone que ninguno de los dos ángulos esté en un rango particular, por ejemplo, [0, 360] o [-180, 180]
  • tiene un tiempo de ejecución constante

La solución de producto cruzado propuesta por Krypes cumple con este criterio, sin embargo, es necesario generar primero los vectores desde los ángulos. Creo que la técnica BAMS de JustJeff también satisface este criterio. Te ofrezco otro ...

Como se discutió en ¿Por qué el módulo es diferente en diferentes lenguajes de programación? que hace referencia al excelente artículo de Wikipedia , hay muchas formas de realizar la operación de módulo. Las implementaciones comunes redondean el cociente hacia cero o infinito negativo.

Sin embargo, si redondeas al entero más cercano:

double ModNearestInt(double a, double b) { return a - b * round(a / b); }

El tiene la buena propiedad que el resto devuelto es

  • siempre en el intervalo [-b / 2, + b / 2]
  • siempre la distancia más corta a cero

Asi que,

double angleToTarget = ModNearestInt(objdeg - gundeg, 360.0);

será el ángulo más pequeño entre objdeg y gundeg y la señal indicará la dirección.