PDA

Ver la Versión Completa : Algoritmo de movimiento


Antigol
08-12-2003, 05:05:02
Bueno tal como me recomendaron, abro esta nueva discusión para hacer una pregunta:

Estoy programando un juego de fútbol que estaba haciendo en VB pero preferí hacerlo en Delphi ahora.

Yo necesito lo siguiente:

Tengo un control TImage, que se encuentra en por ejemplo las coordenadas left= 500 y top= 1000.

SUpongamos que yo quiero que el objeto se mueva hasta el punto left= 1300 y top= 1200 pero obviamente que se mueva despacio punto por punto pq la persona tiene que verlo.

Yo hice un algoritmo que lo que hace es devolverme de cuanto en cuanto tiene que moverse la TImage para llegar al punto 1300,1200.

Por ejemplo el numero que me tira el algoritmo es: 3 y 6 entonces la TImage se moveria 3 de left y 6 de top asi hasta llegar al punto 1300,1200, de esa manera se ve como el TImage se va moviendo.

Sería asi por ej

imagen.top=1000 +resultado_algoritmo_y (en este caso 3)
imagen.left=500 + resultado_algoritmo_x (en este caso 6)

La cuestión es que yo quiero darle una velocidad que yo quiera por ejemplo en vez de moverse de a 3 y 6, quiero que el número máximo sea 1 por lo que se movería de 1 y 0.5. (ya que 0.5 es la mitad de 1 lo mismo que 3 de 6). Entonces la velocidad se la daría multiplicando el 1 y el 0.5 por 5 por ejemplo (la pelota se moveria a una velocidad "virtual" de 5 pero punto por punto sería de left 5 y de top 2.5)

Por lo tanto sería, antes de la multipliacion por 5

imagen.top=1000 +resultado_algoritmo_y (en este caso 0.5)
imagen.left=500 + resultado_algoritmo_x (en este caso 1)

Y después de la multipliacion por 5

imagen.top=1000 +resultado_algoritmo_y (en este caso 2.5)
imagen.left=500 + resultado_algoritmo_x (en este caso 5)

Hasta ahi vamos bien, el problema surge que ese algoritmo me devuelve siempre 1 (que es lo que quiero) y otro número que obviamente es menor que 1 y es con decimales y las propiedades top y left no aceptan decimales.

En visual basic lo hacía sin problemas pq no me jodia conque no podia mezclar real con integer pero acá en delphi no se como hacer.

Imagino que esta restricción no tendrá algun "truco" para salvarla por lo que seguramente tengo que hacer un algoritmo que me permita buscar otro camino.

Bueno espero haberme explicado bien adios!

roman
08-12-2003, 08:09:23
Aun cuando VB haga conversiones automáticas no entiendo cómo es que te funcionaba ya que no hay tal cosa como fracciones de pixeles.

De cualquier forma mira a ver si te sirven las funciones Trunc o Round para convertir números reales en enteros.

// Saludos

Antigol
08-12-2003, 09:48:19
Bueno gracias por contestar, es lo que me vengo a dar cuenta yo ahora, igualmente en vb por algun motivo que no se andaba bien posiblemente es que para mi "ojo humano no preciso" parecía que hacía todo bien.

Igualmente pensá esto:

Si o si, uno de los dos numeros va a ser 1 y el otro número va a ser menor a 1 y mayor a 0, la pelota se movía bien, o sea no tenía problemas pq si el vb me agarraba solo el entero del numero pasado como parámetro, si tengo 0.5 me va a tomar 0 no? es por eso que no se entiende como andaba bien, ya que al tener por ejemplo el x=0 y el y=1 la pelota siempre se moveria hacia abajo y nada hacia algun costado, pero sin embargo hacía todo bien, si se tenia que mover por ejemplo en diagonal 67 grados supuestamente la lógica me indica que al pasarle el numero menor a 1 se pondria en 0 (o en 1 no se como se hace exacto el redondeo) pq lo "redondea" y no tendria que funcionar.

Y al ser punto por punto no es lo mismo 0.5 que un 1 o 0, ya que al moverse varias veces se notaría demasiado la diferencia entre un 1 y su mitad.

Yo habia pensado hacer algo como:

Si tengo x=1 e Y=0.5 hacer que como 0.5 es la mitad de 1, entonces el x se moveria 2 veces con valor=1 y el y 1 sola vez con valor=1, lo que no se es como quedaría visualmente eso y no se si sobrecargo al programa con tantos cálculos ya que esas cuentas se tendrian que hacer 22 veces por los jugadores y 2 o 3 veces en 1 segundo.

Saludos.

andres1569
09-12-2003, 19:11:44
Hola:

Lo normal es que si un objeto se mueve a intervalos muy cortos, es decir de uno o dos pixeles, no se aprecie demasiado por el ojo humano ese movimiento Sólo Vertical - Sólo Horizontal; otra cosa es si avanzase, por ejemplo 10 ó más pixels sobre un solo eje.

Si el ángulo no es de 45º, siempre habrá un eje sobre el que se desplace más pixels que sobre el otro, parecido a como se dibujan las líneas en una cuadrícula (dibuja una línea con el Paint, haz un zoom grande y verás eso con claridad).

Otra cosa es cómo calcules las posiciones sucesivas que debe avanzar cada vez el objeto. Supongamos que quieres mover un objeto desde un punto PIni a otro PFin, y quieres que entre dichas posiciones transcurran 10 pasos, por ejemplo.

En este ejemplo, deberías seguir más o menos este algoritmo:

procedure Muevete (PIni, PFin: TPoint; Steps: Integer);
var
DeltaX, DeltaY, i : Integer;
NewPos : TPoint;
begin
DeltaX := PFin.X - PIni.X;
DeltaY := PFin.Y - PIni.Y;
for i:=1 to Steps do
begin
NewPos.X := PIni.X + MulDiv (i, DeltaX, Steps);
NewPos.Y := PIni.Y + MulDiv (i, DeltaY, Steps);
Dibujate (NewPos);
end;
end;
Con la función MulDiv (en windows.pas) trabajamos con Integers de forma más rápida para operaciones tipo regla de tres, como ésta, que nos permite calcular la posición correspondiente a cada eje en el paso (Step) correspondiente, teniendo en cuenta que no tiene por qué avanzar los mismos pixels en X o en Y en un paso que en otro, y sin necesidad de recurrir a operaciones con números reales.

Espero que te sirva.

PD: Añadir que si quisieras forzar que el mínimo intervalo de movimiento fuera siempre de 1 pixel, en ese caso en vez de tomar el valor Steps de la lista de parámetros, lo podrías calcular tú mismo. Después de haber calculado los Deltas, haces esto:

if Abs(DeltaX) > Abs(DeltaY) then Steps := Abs(DeltaX)
else Steps := Abs(DeltaY);

De esta forma, el eje sobre el que se da el mayor desplazamiento, sólo se desplazará 1 píxel como máximo, y sobre el otro habrá pasos en que se desplace 1 pixel y en otros 0 pixels.

Antigol
10-12-2003, 03:24:46
Bueno gracias por tomarte el tiempo de escribir todo eso.

Yo probé lo que puse arriba de hacer que algo se mueva o con un 1 o con un 0 pero los cálculos que hacía si o si estan en reales ya que tengo que saber bien cada cuanto hay que poner en 1 o cada cuanto ponerlo en 0.

Para hacer la velocidad se me había ocurrido que un jugador si tiene de velocidad 10 y otro 5 entonces el primer jugador se mueva "mas veces" en un segundo que el que tiene 5.

EL problema que vi en Delphi es que al haber pocos pixeles en el ancho de la cancha es muy notorio cuando un jugador se mueve, cosa que no me pasaba en VB, por eso me había entrado la duda de donde hacerlo.

Despues vi que en VB los "pixeles" (si es que se le pueden llamar asi) son más ¿? por ejemplo la cancha de ancho mide 4000 puntos mientras que en Delphi mide 400 imagino que es como la resolución de la pantalla.

Estoy trabado en eso, se me generan dudas con respecto a ese tema.

delphi.com.ar
10-12-2003, 14:57:14
Antigol comentó:
EL problema que vi en Delphi es que al haber pocos pixeles en el ancho de la cancha es muy notorio cuando un jugador se mueve, cosa que no me pasaba en VB, por eso me había entrado la duda de donde hacerlo.

Despues vi que en VB los "pixeles" (si es que se le pueden llamar asi) son más ¿? por ejemplo la cancha de ancho mide 4000 puntos mientras que en Delphi mide 400 imagino que es como la resolución de la pantalla.
La resolución de la pantalla no es algo que manejen los lenguajes, en VB y en Delphi los dispositivos tienen exactamente la misma cantidad de píxeles de alto y ancho. En VB es muy normal trabajar en TWIPS y no en Píxeles. Los Twips son algo así como una medida lógica proporcional, para evitar hacer cálculos al dibujar en dispositivos con resoluciones diferentes. En este hilo (http://www.clubdelphi.com/foros/showthread.php?s=&threadid=5344) puedes ver dos funciones que he hecho para trabajar con Twips desde Delphi. De todos modos no te recomiendo esto, lo que te recomiendo es que juegues un poco mas con los tiempos porque el efecto del movimiento tiene que ser el mismo.

Saludos!

andres1569
11-12-2003, 12:25:08
Hola:

Conozco poco de VB y no sabía ese detalle que apunta Delphi.Com.Ar, pero ten en cuenta que los pixels de pantalla son los mismos (por supuesto), no importa el lenguaje con el que programes, lo que indica que si VB trabaja con esa resolución virtual, que te facilitaba el trabajo, luego debe dibujarlos y eso implica una conversión a pixels de pantalla.

No sé en qué medida eso es más o menos lento pero sí ten en cuenta que si trabajas con una resolución virtual mayor (en Delphi también se puede cambiar la resolución/orientación cuando se dibuja sobre un TCanvas, mediante las API's SetViewPortEx, SetMapMode ...), luego al convertirse en pixels de pantalla, puede que dos posiciones diferentes se conviertan en una misma sobre el monitor, por lo que ahí hay un cálculo innecesario, más aún si como tú dices quieres que el movimiento sea muy suave, de pixel en pixel. Me explico, pongamos las posiciones virtuales(1200, 678) y (1202, 679), puede que al convertirse en coordenadas de pantalla resulten en la misma (supongamos 300, 170), por estar muy próximas y ser una conversión de mayor a menor, por lo que estarías haciendo cálculos innecesarios.

El código que te puse arriba lo he usado a la hora de calcular desplazamientos y puesto que los pixels son siempre enteros, resulta bastante adecuado para esos menesteres el uso del procedimiento MulDiv. Otra cosa sería que trabajaras con coordenadas reales a convertir en coordenadas en pantalla, ahí no habría más remedio que echar mano de aritmética real y luego redondear con ROUND o TRUNC.

Saludos

Antigol
12-12-2003, 03:39:45
Bueno Andrés, en realidad yo no entiendo tan profundo todo eso de los pixeles, pero por ejemplo en visual basic no tengo dramas en la parte lentitud o no a la hora de moverse todo. En vb anda perfecto, o sea si lo muevo 0,3 a un tipito casi no se nota, como debe ser.

En delphi tenes que elegir o 0 o 1. El problema de eso es que si lo muevo de 1 en 1 ya de por si se nota demasiado y no le encuentro manera lógica para la vista del ser humano para representar la velocidad que si puedo hacer en vb ya que en este último lo multiplico por 20 por ejemplo y la velocidad 20 se refleja de manera justa.

A mi me gusta mucho más el delphi que el vb pero en esto no le encuentro forma de representar la velocidad.

En vb por ejemplo si yo hago objeto.left + 0,3 se mueve eso, ya que al final del algoritmo el jugador termina exactamente en la posicion que quiero. EN delphi hice un algoritmo que convierte a 1 o 0 como corresponde el X y el Y pero no encuentro manera "lógica" de representar la velocidad.

Saludos

andres1569
12-12-2003, 12:10:26
Hola:

En ese caso, prueba de trabajar en coordenadas "virtuales", es decir, trabaja como si estuvieras en VB, con una resolución mayor, haces los cálculos de igual manera y a la hora de asignar el Left - Top del TImage en cuestión haces la conversión a coordenadas de pantalla (píxeles). Si por ejemplo trabajas con un factor de escala de 8 (ampliado 8 veces), una vez tengas la posición deseada de un objeto, en números reales, la conviertes a píxeles, números enteros, mediante algo así como:

Image1.Left := ROUND (Pos.X / 8);
Image1.Top := ROUND (Pos.Y / 8);

donde Pos es el punto con el que normalmente trabajabas en VB.

Esto supone saber con qué escala estamos trabajando, en el mensaje de Delphi.Com.Ar creo que indica cómo averiguar la relación Twips / Pixels.

Un saludo

Antigol
19-12-2003, 03:17:06
Hay algo que sigo sin entender debo ser medio bolu. :D

No entiendo como, si delphi se maneja en pixeles, como se podria usar twips, o sea un 1 twip es menos que 1 pixel, como se podria hacer para que el delphi use los twips en pantalla pq esa conversion que me decis vos es para usarlo al final en pixeles.

Si por ejemplo 1 pixel son 10 twips, como haría el programa para poner mi jugador en el twip 5 si solo acepta pixeles??

Esa es mi duda.

andres1569
19-12-2003, 12:56:20
Hola de nuevo,

Veamos: manejes la escala que manejes (sean Twips, mm, cm, etc..) si sabes cuál es el factor de conversión (supongamos tal como dices que es 10), de lo que se trata es de hacer dicha conversión sólo en el momento en que vayas a visualizar en pantalla ese objeto.

Así es como funciona cualquier programa que maneja gráficos, tienes almacenadas las posiciones de cada objeto en coordenadas reales y a la hora de visualizar los objetos, traduces dichas coordenadas a pixels de pantalla, que sólo pueden expresarse como números enteros, y dibujas el objeto.

Sin embargo, entiendo tu problema: lo que te sucede es que, por lo visto, manejas Controles TImage utilizando sus propiedades Left y Top para desplazarlos. Dichas propiedades te sirven a la vez para almacenar la posición y para mostrarse en pantalla, ahí no vale ninguna conversión, cosa que no te sucedía con VB pues VB trabaja con una resolución virtual que luego se encarga de traducir a pixel (ése es un proceso interno de VB y transparente al usuario/programador), de modo que si le ponías al control VB, Left = 2000, en pantalla aparecía en el píxel 200 (p. ejemplo). Esa ventaja no la tienes en Delphi (hay cientos de ventajas de Delphi respecto a VB, supongo, pero ésa no), lo cual te obliga a almacenar las posiciones en una lista aparte (un array de TPoint por ejemplo), manejarte sobre ese array y traducir a pixels a la hora de asignar el Left y el Top.

Es más farragoso pero creo que no hay otra opción. Si acaba interesándote esta opción, hay trucos para asociar por ejemplo un record a un Control, de modo que incluso podrías prescindir de dicha lista y almacenar esos valores junto a cada Control.

He mirado la ayuda de una propiedad que traen los Forms en Delphi, que es PixelsPerInch, en teoría debería servir para trabajar con una resolución virtual al estilo VB, pero parece ser que sólo afecta a las fuentes (tipos de letras), he hecho alguna prueba y no funciona para los Controles.

Un saludo, espero que te sirvan estos comentarios ...

PD: No sé qué es ser Bolu, me lo imagino, pero creo que tú no tienes nada de eso ... ;)

jachguate
19-12-2003, 16:36:27
Orientandolo a una solución POO, podes crear un descendiente de TImage que tenga algunas propiedades nuevas, digamos

AbsoluteLeft : Double o Extended
AbsoluteTop : Double o Extended
UnitsPerPixel: Double o Extended.

Cada vez que se modifica la propiedad AbsoluteLeft o AbsoluteTop, se utiliza el valor de UnitsPerPixel para encontrar la posición en pantalla, haciendo el proceso transparente al programador, tal como ocurria en VB.

Hasta luego.,

;)

andres1569
20-12-2003, 13:33:48
Me adhiero a la propuesta de Jachguate, es una buena forma de simular el comportamiento de VB para estos menesteres, de forma que te resultaría igual de sencillo que en VB el asignar esas posiciones.

Aunque quizás fuera bueno que el factor de conversión (propiedad UnitsPerPixel) fuera ajeno al TImage, es decir que residiera en un objeto común, quizás en la propiedad Tag (que sirve de comodín para truquillos como estos) del componente que hace de contenedor (sea un Form, un TPanel, un TTabSheet ...etc). De manera que cuando se asigna la propiedad AbsoluteLeft y/o AbsoluteTop, se acceda al Tag del Parent y de ahí se obtenga ese factor de conversión. Es una sugerencia.

Saludos

Antigol
21-12-2003, 03:11:56
No se si me falla el parietal derecho pero sigo sin entender. :(

Supongamos que yo hago todo en twips

Pixel 1................................ Pixel 2

Twip 1 twip 2 twip 3 twip 4 twip 5

Todos los cálculos en twips, cuando haga la conversion a pixeles, que pasaria si me queda twip 3, si después se maneja en pixeles supuestamente la posicion visual en la pantalla va a ser de pixeles por lo que al hacer la conversion me va a quedar o pixel 1 o pixel 2 por lo que estaría en lo mismo que al principio.

O sea no entiendo como en la parte visual una vez pasado a pixeles se puede ubicar en ese twip si al final todo es en pixeles.

DarkSide
22-12-2003, 04:00:43
Hola Antigol, la verdad he estado leyendo este hilo y las soluciones de jachguate y andres, me parecen muy acertadas para lo que quieres hacer, ya que no veo la diferencia entre mover un imagen 1 pixel o 0,5 pixel (si se pudiera), ya que en ambos caso seria casi imperceptible para el ojo humano, no asi una cantidad mayor de pixeles, pero en ese caso 100 pixeles, me parecen lo mismo que 100,5 pixeles.

Saludos

andres1569
22-12-2003, 17:05:13
Hola Antigol:

Lo que te estamos sugiriendo es una solución en la que sigas manejándote en Twips, de forma que no tengas que cambiar la forma de trabajar que tenías en VB.

Sencillamente tendrías un control derivado de TImage, llamémosle TTwipsImage, con dos propiedades AbsLeft y AbsTop, que asignarías tal como hacías con VB. El control ya se encargará (hay que programarlo a tal efecto, claro) de traducir a pixels esas posiciones para poderse dibujar en pantalla, pero eso es algo que no debe preocuparte, ¡ por supuesto que habrá posiciones diferentes en twips, que sobre la pantalla se sitúen sobre el mismo pixel ! Pero eso ya sucedía en VB, aunque quizás no te dabas cuenta.

Como comenta Darkside, no puede haber 0.5 pixels, pero eso es de cajón, trabajamos sobre monitores con resoluciones limitadas por lo que cuando manejemos objetos que se mueven en una resolución mayor, es inevitable esa pérdida de precisión. Si por ejemplo renderizas en un monitor un mapa de tu país, es posible que entre pixel y pixel haya comprendidos varios kilómetros, y en cambio eso no se refleja en la pantalla, otra cosa distinta es que tengas aparte almacenada la posición real de cada objeto.

Espero haberme explicado y que captes la idea, saludos

Antigol
23-12-2003, 12:04:42
Ahh ahora entiendo, entonces por ejemplo cuando un objeto se movia a 20 twips en realidad era a 2 pixeles y cuando se movia a 5 twips se quedaba quieto, pero si le sumaba otros 5 twips entonces se le sumaban a lo anterior??

Ya le entiendo la mano, voy a probar asi, tengo que cambiar la sintaxis que tengo hecha en vb pq habia adelantado bastante.

Gracias por la ayuda!!