Comunidad de diseño web y desarrollo en internet online

Demasiados condicionales

Citar            
MensajeEscrito el 17 Abr 2008 11:50 am
Muy buenas; estoy haciendo un juego de billar en Flash, y me ha surgido una duda. La manera que tengo de que la bola rebote contra los límites del tablero es la siguiente: con una función que detecta la colisión entre dos MC (versión mejorada de hitTest), y otra función asociada a un evento Timer que se ejecuta una vez cada milisegundo, compruebo un montón de condicionales, uno por cada borde con el que choca la bola (dos laterales y cuatro arriba y abajo, más las troneras o agujeros). ¿Existe alguna manera de optimizar este código, o de comprobar ese choque de alguna otra manera?

P.D.: tengo que decir que este código me funciona sin problemas, pero me parece un poco pesado y a lo mejor alguien con más conocimientos que yo pudiera darme una solución mejor...

Código :

public function mover_bola(evento:TimerEvent):void
      {
         if (vel >= 0)
         {//Entrada en las troneras
            if (Collision.isColliding(blanca, tronera1) == true ||
            Collision.isColliding(blanca, tronera2) == true ||
            Collision.isColliding(blanca, tronera3) == true ||
            Collision.isColliding(blanca, tronera4) == true ||
            Collision.isColliding(blanca, tronera5) == true ||
            Collision.isColliding(blanca, tronera6) == true)
            {
               blanca.alpha = 0;
            }
            //Colisión con los laterales
            if (Collision.isColliding(blanca, largo1) == true)
            {
               [...]
            }
            if (Collision.isColliding(blanca, largo2) == true)
            {
               [...]
            }
            //Colisión superior o inferior
            if (Collision.isColliding(blanca, corto1) == true || 
            Collision.isColliding(blanca, corto2) == true)
            {
               [...]
            }
            if (Collision.isColliding(blanca, corto3) == true || 
            Collision.isColliding(blanca, corto4) == true)
            {
               [...]
            }
            [...]
         }
         else
         {
            [...]
         }
      }

Por Juanlu_001

Claber

690 de clabLevel

6 tutoriales

 

firefox
Citar            
MensajeEscrito el 17 Abr 2008 05:31 pm
La opción de hitTest funciona para el billar, aunque es peligrosa para juegos con mucha acción. La otra opción incluye trayectorias predictivas (conociendo el vector de movimiento, se sabrá donde estará en determinado momento algún elemento), pero no te ahorras código, al contrario. Recuerdo varios ejemplos de billar en un par en libros de juego que he leído en otras épocas. El cálculo de los rebotes incluye mucha trigonometría (supongo que ya tendrás incorporado eso en el motor de movimiento) pero siempre se basa en chequear constantemente las colisiones (aunque comparando posiciones, no por hitTest)
Te dejo un tocho de un ejemplo entre dos bolas en donde se aplica una serie de cosas que viene explicando el libro y separado en tres partes: movimiento, detección y render (lo vez en el onEnterFrame del final). En el escenario, un par de instancias de un redondelito a modo de bolas (20 x 20 px) con nombres de instancia ball1 y ball2

Código :

var game:Object = {};
game.numBalls = 2;
for (var i = 1; i<=game.numBalls; ++i) {
   var name:String = "ball"+i;
   game[name] = {};
   game[name].clip = _root[name];
   game[name].xpos = game[name].clip._x;
   game[name].ypos = game[name].clip._y;
   game[name].radius = game[name].clip._width/2;
   game[name].xmov = 0;
   game[name].ymov = 0;
}
game.ball1.xmov = 0;
game.ball1.mass = 1;
game.ball1.ymov = 2;
game.ball2.mass = 1;
game.ball2.ymov = 0;
function moveBalls() {
   for (var i = 1; i<=game.numBalls; ++i) {
      var ob:Object = game["ball"+i];
      ob.tempx = ob.xpos+ob.xmov;
      ob.tempy = ob.ypos+ob.ymov;
   }
}
function renderBalls() {
   for (var i = 1; i<=game.numBalls; ++i) {
      var ob = game["ball"+i];
      ob.xpos = ob.tempx;
      ob.ypos = ob.tempy;
      ob.clip._x = ob.xpos;
      ob.clip._y = ob.ypos;
   }
}
function ball2BallReaction(b1:Object, b2:Object, x1:Number, x2:Number, y1:Number, y2:Number, time:Number) {
   //get the masses
   var mass1:Number = b1.mass;
   var mass2:Number = b2.mass;
   // -----set initial velocity variables
   var xVel1:Number = b1.xmov;
   var xVel2:Number = b2.xmov;
   var yVel1:Number = b1.ymov;
   var yVel2:Number = b2.ymov;
   var run:Number = (x1-x2);
   var rise:Number = (y1-y2);
   var Theta:Number = Math.atan2(rise, run);
   var cosTheta:Number = Math.cos(Theta);
   var sinTheta:Number = Math.sin(Theta);
   //Find the velocities along the line of action
   var xVel1prime:Number = xVel1*cosTheta+yVel1*sinTheta;
   var xVel2prime:Number = xVel2*cosTheta+yVel2*sinTheta;
   //Find the velocities perpendicular to the line of action
   var yVel1prime:Number = yVel1*cosTheta-xVel1*sinTheta;
   var yVel2prime:Number = yVel2*cosTheta-xVel2*sinTheta;
   // Conservation Equations
   var P:Number = (mass1*xVel1prime+mass2*xVel2prime);
   var V:Number = (xVel1prime-xVel2prime);
   var v2f:Number = (P+mass1*V)/(mass1+mass2);
   var v1f:Number = v2f-xVel1prime+xVel2prime;
   var xVel1prime:Number = v1f;
   var xVel2prime:Number = v2f;
   //Project back to Flash's x and y axes
   var xVel1:Number = xVel1prime*cosTheta-yVel1prime*sinTheta;
   var xVel2:Number = xVel2prime*cosTheta-yVel2prime*sinTheta;
   var yVel1:Number = yVel1prime*cosTheta+xVel1prime*sinTheta;
   var yVel2:Number = yVel2prime*cosTheta+xVel2prime*sinTheta;
   //change old pos
   b1.tempx = b1.xpos+bl.xmov*time;
   b1.tempy = b1.ypos+b1.ymov*time;
   b2.tempx = b2.xpos+b2.xmov*time;
   b2.tempy = b2.ypos+b2.ymov*time;
   b1.xmov = xVel1;
   b2.xmov = xVel2;
   b1.ymov = yVel1;
   b2.ymov = yVel2;
}
function ballToBallDetection(b1:Object, b2:Object) {
   //set the speed variables
   var xmov1:Number = b1.xmov;
   var ymov1:Number = b1.ymov;
   var xmov2:Number = b2.xmov;
   var ymov2:Number = b2.ymov;
   //set the position variables
   var xl1:Number = b1.xpos;
   var yl1:Number = b1.ypos;
   var xl2:Number = b2.xpos;
   var yl2:Number = b2.ypos;
   //define the constants
   var R:Number = b1.radius+b2.radius;
   var a:Number = -2*xmov1*xmov2+xmov1*xmov1+xmov2*xmov2;
   var b:Number = -2*xl1*xmov2-2*xl2*xmov1+2*xl1*xmov1+2*xl2*xmov2;
   var c:Number = -2*xl1*xl2+xl1*xl1+xl2*xl2;
   var d:Number = -2*ymov1*ymov2+ymov1*ymov1+ymov2*ymov2;
   var e:Number = -2*yl1*ymov2-2*yl2*ymov1+2*yl1*ymov1+2*yl2*ymov2;
   var f:Number = -2*yl1*yl2+yl1*yl1+yl2*yl2;
   var g:Number = a+d;
   var h:Number = b+e;
   var k:Number = c+f-R*R;
   //solve the quadratic equation
   var sqRoot:Number = Math.sqrt(h*h-4*g*k);
   var t1:Number = (-h+sqRoot)/(2*g);
   var t2:Number = (-h-sqRoot)/(2*g);
   if (t1>0 && t1<=1) {
      var whatTime:Number = t1;
      var ballsCollided:Boolean = true;
   }
   if (t2>0 && t2<=1) {
      if (whatTime == null || t2<t1) {
         var whatTime:Number = t2;
         var ballsCollided:Boolean = true;
      }
   }
   if (ballsCollided) {
      //Collision has happened, so throw a trace
      ball2BallReaction(b1, b2, xl1, xl2, yl1, yl2, whatTime);
   }
}
_root.onEnterFrame = function() {
   moveBalls();
   ballToBallDetection(game.ball1, game.ball2);
   renderBalls();
};


La explicación de la trigonometría de todo esto ocupa casi todo el capítulo, pero me entra por un oído y me sale por el otro cuando terminan de pagarme. En fin, que si te interesa el tema y eres afin a las matemáticas podrás sacarle provecho

Jorge

Por solisarg

BOFH

13669 de clabLevel

4 tutoriales
5 articulos

Genero:Masculino   Bastard Operators From Hell Premio_Secretos

Argentina

firefox
Citar            
MensajeEscrito el 17 Abr 2008 05:33 pm
Me olvidé de aclarar, por si no fuera obvio, que esto está escrito en AS2 y del viejito

Jorge

Por solisarg

BOFH

13669 de clabLevel

4 tutoriales
5 articulos

Genero:Masculino   Bastard Operators From Hell Premio_Secretos

Argentina

firefox
Citar            
MensajeEscrito el 17 Abr 2008 08:57 pm
Ufff, muchas gracias por tomarte la molestia y resolver mi duda (que no queda otro remedio XD). Me leeré tu código mañana, más despejado. Y sí, ya metí toda la parte de trigonometría, aunque de momento me limito a impulsar una sola bola con el taco.

Por Juanlu_001

Claber

690 de clabLevel

6 tutoriales

 

firefox
Citar            
MensajeEscrito el 19 Abr 2008 12:16 pm
Buenas, sólo añadir algunas cosas:

Para AS3 existen varios motores de física geniales, como Box2DFlashAS3 o APE. Esto te cubriría a la perfección todo el tema de las colisiones, con un rendimiento mejor del que puedas sacar tú (digo yo xD), aunque quizá sea demasiado para un juego de billar.

Juanlu_001 escribió:

...con una función que detecta la colisión entre dos MC (versión mejorada de hitTest)

En caso de que prefieras seguir con tú código, utiliza el método hitTestPoint en vez del hitTest, que se encarga de detectar las colisiones a nivel de pixel. Es simple, fácil y preciso, una gozada. Como decía Jorge, la tigonometría siempre será la mejor opción, pero si tu controlas los vectores, fuerzas, etc. y dejas únicamente el hitTestPoint para detectar si hay colisión o no, te funcionará a la perfección.

Juanlu_001 escribió:

... y otra función asociada a un evento Timer que se ejecuta una vez cada milisegundo, compruebo un montón de condicionales...

Utilizar un timer una vez por milisegundo es una locura, ninguna máquina te va a dar ese rendimiento. Para asegurarte que cualquier máquina (más o menos decente) te dé un rendimiento similar debería estar entre 30/50.
De todas formas, hay la creencia que es mejor utilizar timers que enterFrame, ya que puedes ejecutarlos más veces por segundo, pero no es así. Siempre que haya que hacer una visualización por pantalla que dependa de un cálculo dará mucho mejor rendimiento utilizar un enterFrame (obviamente a un valor adecuado, yo suelo tener todas mis películas a 31fps).

Y mejor aún, cuando tengas que ejecutar muchas operaciones que sean costosas para la CPU, lo mejor es tener un único motor para la película. Dicho de otra manera, es mejor un enterFrame que controle 30 bolas, que no 30 bolas con un enterFrame.

Este es un tema que he discutido un millón de veces, y al final cada uno tiene sus opiniones, pero creéme que me he peleado mucho con esto durante años y lo tengo más que comprobado :wink:

Saludos!

Por llops

294 de clabLevel

1 tutorial

 

Barcelona

firefox
Citar            
MensajeEscrito el 19 Abr 2008 01:07 pm
Muchísimas gracias por tus consejos llops, quería decir un par de cosas:

- La primera, la función que utilizo para detectar las colisiones es una versión para AS3 de una hecha por un tal Grant Skinner, desde la URL http://www.gskinner.com/blog/archives/2005/08/flash_8_shape_b.html. Creo que la función hitTestPoint no me sirve para lo que quiero, porque yo necesito comprobar colisiones entre dos objetos, no entre un objeto y un punto. Aunque bien podrías también esclarecerme este punto :-P

- La segunda: en lo de los Timer y el EnterFrame acabo de caer en la cuenta de que tienes toda la razón :-O. Creo que ahora mismo voy a ir a modificar un par de programas que tengo escritos por ahí :-)

Muchas gracias de nuevo, saludos!

Por Juanlu_001

Claber

690 de clabLevel

6 tutoriales

 

firefox
Citar            
MensajeEscrito el 19 Abr 2008 02:03 pm
En el code que te pasé (y casi siempre que se use una fórmula predictiva) no se usa hitTest sino una serie de cálculos en base a la posición de las bolas. Se toma el centro de cada uno y en base al radio se calcula si hay colisión. Esto es porque hitTest no es muy confiable, sobre todo si la velocidad del elemento en movimiento es grande.
El tal Grant Skinner escribió muchas de las clases básicas para el desarrollo de aplicaciones, recuerdo un gestor de eventos y como no el modelador UML. Muy recomendable recorrer su blog

Jorge

Por solisarg

BOFH

13669 de clabLevel

4 tutoriales
5 articulos

Genero:Masculino   Bastard Operators From Hell Premio_Secretos

Argentina

firefox
Citar            
MensajeEscrito el 19 Abr 2008 10:28 pm

Juanlu_001 escribió:

- La primera, la función que utilizo para detectar las colisiones es una versión para AS3 de una hecha por un tal Grant Skinner, desde la URL http://www.gskinner.com/blog/archives/2005/08/flash_8_shape_b.html. Creo que la función hitTestPoint no me sirve para lo que quiero, porque yo necesito comprobar colisiones entre dos objetos, no entre un objeto y un punto. Aunque bien podrías también esclarecerme este punto

Pues a esclarecer! La función hitTestPoint utiliza exactamente el mismo sistema que la clase de Skinner, sólo que AS3 ya la incorpora de forma nativa. La detección siempre es entre objetos, así que no te preocupes ;)

solisarg escribió:

En el code que te pasé (y casi siempre que se use una fórmula predictiva) no se usa hitTest sino una serie de cálculos en base a la posición de las bolas. Se toma el centro de cada uno y en base al radio se calcula si hay colisión. Esto es porque hitTest no es muy confiable, sobre todo si la velocidad del elemento en movimiento es grande

También considero que utilizar trigonometría sea lo mejor, pero si uno no se quiere meter (normalmente porque es complicado), se puede utilizar un objeto "no visible" que vaya un frame por delante del objeto "real". Por ejemplo, en la bola de billar, se puede dejar una bola que no esté en la displayList para realizar la predicción, y así la bola real siempre actuará con un comportamiento lógico.

Básicamente es lo que se hace con trigonometría, que se calculan posiciones y si alguna sobrepasa un objeto se hace una corrección.

Saludos!

pd: el blog de Grant Skinner es un "must read" :alabado:

Por llops

294 de clabLevel

1 tutorial

 

Barcelona

firefox
Citar            
MensajeEscrito el 20 Abr 2008 10:52 am
Hay una cosa que sigo sin entender... :oops:

Adobe LiveDocs escribió:

hitTestPoint(x:Number, y:Number, shapeFlag:Boolean = false):Boolean
Evalúa el objeto de visualización para comprobar si se solapa o presenta un punto de intersección con el punto especificado por los parámetros x e y.

¿Esto no es entonces entre objeto de visualización y punto? :?

En cambio, y lo acabo de descubrir ahora...

Adobe LiveDocs escribió:

hitTestObject(obj:DisplayObject):Boolean
Evalúa el objeto de visualización para comprobar si se solapa o presenta un punto de intersección con el objeto de visualización obj.


????

Por Juanlu_001

Claber

690 de clabLevel

6 tutoriales

 

firefox
Citar            
MensajeEscrito el 20 Abr 2008 04:28 pm
Tienes toda la razón Juanlu, mea culpa. Hablaba de cabeza y confundí los usos.
El que te detecta las colisiones entre objetos es el hitTestObject.

Sorry ^^

Por llops

294 de clabLevel

1 tutorial

 

Barcelona

firefox
Citar            
MensajeEscrito el 21 Abr 2008 12:03 pm
Ajá, muchas gracias entonces ^^

*Juanlu va a poner los EnterFrame correspondientes... :-P

Por Juanlu_001

Claber

690 de clabLevel

6 tutoriales

 

firefox
Citar            
MensajeEscrito el 21 Abr 2008 12:13 pm
Problema, problema :?

Creo que el método hitTestObject tiene el mismo problema que el viejo hitTest: comprueba las colisiones entre los rectángulos delimitadores de los clips...

Viva Grant Skinner :-P

Por Juanlu_001

Claber

690 de clabLevel

6 tutoriales

 

firefox
Citar            
MensajeEscrito el 21 Abr 2008 04:41 pm
Salu2 a todos,
Estoy en un caso muy similar y si hitTestObject detecta colision a nivel de BoundingBox. Creo q con hitTestPoint el problema sera el mismo...aunque focalice mejor con que punto colisiona, el boundingbox del objeto será el detonante. Respecto el método shapecolision de gskinner http://www.gskinner.com/blog/archives/2005/10/source_code_sha.html esta disponible en AS3?

Tb quisiera aprovechar, ya que mas arriba se ha comentado, el tema sobre cuando supervisar la colisión, si onEnterFrame(asi es como lo tengo ahora y bloquea un poco el sistema) o event. Yo creo q me gustaría usar un dispacher event que se active cada vez que mi personaje se mueve. El problema es que no puedo usar el evento ratón o teclado , sino que me debería de hacer una variable q se activa cada vez q se detecta q mi personaje se ha movido. En resumen... como activo un evento con una variable boleana q actualizo desde otra función?

Por charli_e

2 de clabLevel



 

firefox
Citar            
MensajeEscrito el 21 Abr 2008 05:33 pm

Por solisarg

BOFH

13669 de clabLevel

4 tutoriales
5 articulos

Genero:Masculino   Bastard Operators From Hell Premio_Secretos

Argentina

firefox
Citar            
MensajeEscrito el 22 Abr 2008 10:13 am
Si , la verdad es que parece la opción más sensata para el personaje y lo voy a tener que implementar (gracias por el link). De todas maneras hay un segundo protagonista en el juego (como un dios) que es totalmente diferente y que solo activa algunos eventos, con lo que prefiero usar la detección precisa de colisión entre objetos. Al final me he quedado con el primer algoritmo de colisión que me ha funcionado (un detector de colisiones derivado del gskiner). Aquí va el link http://www.adventuresinactionscript.com/blog/15-03-2008/actionscript-3-hittestobject-and-pixel-perfect-collision-detection#comment-8

Como en mi caso el personaje "Dios" ha sido construido con LineTo,Fill de graphics, la colisión me ha funcionado poniendo el parámetro de tolerancia a 255.

Código :

var collisionRect:Rectangle;
    collisionRect = HitTest.hitTestObject(siluetaMC,tubo,true,255);
    if(collisionRect) ......

Por charli_e

2 de clabLevel



 

firefox
Citar            
MensajeEscrito el 22 Abr 2008 12:58 pm
Pues Juanlu, estaba seguro que el hitTestObject no hacía la colisión mediante el boundingbox, pero veo que me equivoco... cuando tenga un rato repasaré conceptos :)

Por llops

294 de clabLevel

1 tutorial

 

Barcelona

firefox

 

Cristalab BabyBlue v4 + V4 © 2011 Cristalab
Powered by ClabEngines v4, HTML5, love and ponies.