Comunidad de diseño web y desarrollo en internet online

Problema con colisiones complejas

Citar            
MensajeEscrito el 15 Dic 2010 04:58 pm
Buenas!
Soy Gold y escribo porque tengo un problema con un pequeño proyecto que estoy llevando a cabo. Incluyo los ficheros implicados abajo para que se entienda mejor el problema.

Utilizo Flash CS4, y he hecho varios juegos sencillos (de formas geométricas simples y tal), aún ando algo verde con AS3 (Utilizaba AS2 hasta hace poco) pero me gusta ir experimentando.

Bueno, al grano. He intentado ir un paso mas adelante y crear un juego con un fondo segmentado formado por MovieClips de dibujos irregulares que se mueven a diferente velocidad.
Todo me ha ido bien hasta el momento de incluir un personaje que interactua con él. He utilizado un detector de colisiones complejo (que crea un mapa de Bits y compara colores) con un personaje improvisado para que me devolviera "true" cuando el personaje tocara el suelo del fondo...Pero no funciona.
No saltan errores y la función parece funcionar, pero siempre devuelve "false", como si el personaje no impactara con el suelo de la pantalla. No solo no reconoce el impacto con el suelo, si no que tampoco reconoce el impacto con todo el fondo, un MovieClip que ocupa toda la pantalla, un impacto que evidentemente existe.

He abierto el código de la función y he puesto traces por todas partes para ver en que momento falla, pero no se que ocurre. He probado la misma función en nuevos .fla y me los reconoce perfectamente y sin problemas, pero en el juego no.

Así que mis dudas son...¿Que ocurre para que no reconozca el impacto del personaje con el fondo? ¿Cómo se puede solucionar? Y en caso de no poder utilizar esa función ¿Que podría utilizar para conseguir el mismo efecto?

Aquí está el proyecto entero (Siento subirlo a Megaupload, soy nuevo aquí y no se cómo subir proyectos complejos)
http://www.megaupload.com/?d=UG2DURA7
La función de colisión esta en el .as del personaje

Y esta es la función de impacto complejo que estoy utilzando
http://www.esedeerre.com/ejemplo/4/141/actionscript-30-colisiones-a-nivel-de-pixel

¡Muchas gracias por su atención!

Por Gold

4 de clabLevel



 

firefox
Citar            
MensajeEscrito el 15 Dic 2010 05:37 pm
Antes de bajarme tu proyecto y pasar un buen rato viendolo, vamonos por lo mas simple.

Podrias postear ls funcion donde se checa la colision y como es que la usas para ver si hay algo alli, si despue de analizar todo paso a paso no se soluciona nada ya veremos si hay algo mas en tu proyecto.

No creo que nadie mire antes el proyecto completo ;)

Saludos.

Por Angel Roberto

Claber

248 de clabLevel



 

firefox
Citar            
MensajeEscrito el 16 Dic 2010 05:14 pm
Hola, siento el retraso

Veamos, las funciones utilizadas son estas.
Para el escenario:

Código ActionScript :

package clases{

   import flash.display.MovieClip;
   import flash.display.Stage;

   public class General extends MovieClip {
      public function General() {
         var escenario:FFondo = new FFondo(stage);
         var prota:Personaje = new Personaje(stage, escenario);
         
         stage.addChildAt(escenario,0);
         stage.addChild(prota);
      }
   }

}

No hay mayor complicación. Se importa lo necesario, se crean dos variables para poner el fondo y el personaje, y se añaden el movie clip del personaje y del fondo al Stage.

Ahora, el fondo. Pongo anotaciones:

Código ActionScript :

package clases{
   import flash.events.*;
   import flash.display.MovieClip;
   import flash.display.Stage;
   import flash.utils.Timer;
   import flash.events.*;

   public class FFondo extends MovieClip {
      private var vhorizonte:Number=0.9; //Velocidad horizontal de la capa mas lejana del fondo
      private var vdelantera:int=6; //Velocidad horizontal de la capa mas lejana del fondo (la que interesa)
      private var vnubes:Array=new Array(0.1,0.3,0.2); //Velocidad de las tres nubes
      private var pantalla:Stage; //El escenario
      private var desplazarse:Timer=new Timer(10,0); 
      private var nubemov:Timer=new Timer(150,0);

      public function FFondo(pantalla:Stage) {
         this.pantalla=pantalla; //Ahora pantalla es una referencia al escenario
         
         desplazarse.addEventListener(TimerEvent.TIMER,FMover);
         nubemov.addEventListener(TimerEvent.TIMER,MovNube);
         desplazarse.start();
         nubemov.start();
      }
      private function FMover(e:TimerEvent):void {
//Esta función mueve las dos capas del fondo según marquemos arriba.
//Si la mas cercana llega a su límite derecho, vuelve a 0, creando un scroll interminable.
         if (horizonte.x>(-(horizonte.width)+pantalla.stageWidth)) {
            horizonte.x-=vhorizonte;
         }
         if (delantera.x>(-(delantera.width)+pantalla.stageWidth)) {
            delantera.x-=vdelantera;
         } else {
            delantera.x=0;
         }
      }
      private function MovNube(e:TimerEvent):void { 
//Esta función mueve las nubes del fondo a poca velocidad.
//Cuando alcanzan el límite derecho, reaparecen a la izquierda.
         for (var i:uint = 0; i <= nubes.numChildren-1; i++) {
         nubes.getChildAt(i).x+=vnubes[i];
            if (nubes.getChildAt(i).x>=pantalla.stageWidth+nubes.getChildAt(i).width) {
               nubes.getChildAt(i).x=-(nubes.getChildAt(i).width);
            }

         }
      }
   }
}


Ahora el personaje

Código ActionScript :

package clases{

   import flash.display.MovieClip;
   import flash.display.Stage;
   import flash.events.*;
   import clases.HitTest;

   public class Personaje extends MovieClip {
      //referencia al stage y fondo
      private var pantalla:Stage;
      private var escenario:FFondo;

      public function Personaje(pantalla:Stage,escenario:FFondo) {
         this.pantalla=pantalla;
         this.escenario=escenario;

         //Recoloca el personaje para que impacte con el fondo
         this.y=430;
         this.x=200;
         addEventListener(Event.ENTER_FRAME,contacto,false,0,true);

      }
      private function contacto(e:Event):void {
         //Hace un impacto complejo. Si se están dando, devuelve true
         //si no se dan, devuelve false. Siempre devuelve false, ese es
         //mi problema.
         //No funciona ni de la forma general (comparando el personaje
         //con el fondo, como en este caso) ni específica (comparando
         //un movie clip de un personaje con un movie clip del escenario).
         trace(HitTest.complexHitTestObject(this,escenario))
      }

   }

}


Y por último, la función de impacto complejo, sacada del link que mencioné en el anterior mensaje. Voy a intentar explicarlo lo mejor que pueda.

Código ActionScript :

package clases
{
        
        import flash.display.BitmapData;
        import flash.display.BlendMode;
        import flash.display.DisplayObject;
        import flash.display.Sprite;
        
        import flash.geom.ColorTransform;
        import flash.geom.Matrix;
        import flash.geom.Point;
        import flash.geom.Rectangle;
        
        public class HitTest
        {
 
                public static function complexHitTestObject( target1:DisplayObject, target2:DisplayObject,  accurracy:Number = 1 ):Boolean
                {
                  //Función dentro de la función estática
                        return complexIntersectionRectangle( target1, target2, accurracy ).width != 0;
                }
                
                public static function intersectionRectangle( target1:DisplayObject, target2:DisplayObject ):Rectangle
                {
                        //No pueden colisionar si no están en el escenario y si una colision común o funciona.
                        if( !target1.root || !target2.root || !target1.hitTestObject( target2 ) ) return new Rectangle();
                        
                        // Obtiene la bandas de cada objeto, con las coordenadas de la root como punto de referencia
                        var bounds1:Rectangle = target1.getBounds( target1.root );
                        var bounds2:Rectangle = target2.getBounds( target2.root );
                        
                        // Determina los limites del area de impacto. Devuelve x430 y200, las coordenadas que puse en Personaje, ademas de  80x80, las dimensiones del personaje. En principio esta bien.
                        var intersection:Rectangle = new Rectangle();
                        intersection.x   = Math.max( bounds1.x, bounds2.x );
                        intersection.y    = Math.max( bounds1.y, bounds2.y );
                        intersection.width      = Math.min( ( bounds1.x + bounds1.width ) - intersection.x, ( bounds2.x + bounds2.width ) - intersection.x );
                        intersection.height = Math.min( ( bounds1.y + bounds1.height ) - intersection.y, ( bounds2.y + bounds2.height ) - intersection.y );
                        return intersection;
                }
                
                public static function complexIntersectionRectangle( target1:DisplayObject, target2:DisplayObject, accurracy:Number = 1 ):Rectangle
                {                     
                  //En caso de que la precisión sea mas pequeña o igual a 0 sale un error. No ocurre.
                        if( accurracy <= 0 ) throw new Error( "ArgumentError: Error #5001: Invalid value for accurracy", 5001 );
                        
                        // Esto hace una colisión normal. Si no hay colisión normal, naturalmente no hay colision compleja.
                  // Comprobé con un trace si la salida estaba aquí, y no lo estaba. Así que el programa llega hasta aquí bien.
                        if( !target1.hitTestObject( target2 ) ) return new Rectangle();
                        
                        var hitRectangle:Rectangle = intersectionRectangle( target1, target2 );
                        // Si sus límites no impactan, no puede haber colision. También puse un trace y no hubo error.
                        if( hitRectangle.width * accurracy <1 || hitRectangle.height * accurracy <1 ) return new Rectangle();
                        
                  //Crea un nuevo mapa de bits, con las dimensiones de la caja de impactos.
                        var bitmapData:BitmapData = new BitmapData( hitRectangle.width * accurracy, hitRectangle.height * accurracy, false, 0x000000 ); 
 
                        // Dibuja el primer objeto de impacto. Le pone un color determinado para comprobarlo luego.
                        bitmapData.draw( target1, HitTest.getDrawMatrix( target1, hitRectangle, accurracy ), new ColorTransform( 1, 1, 1, 1, 255, -255, -255, 255 ) );
                        // Crea un segundo objeto de impacto y lo entremezcla con el anterior, cambiando el color en las zonas de impacto.
                        bitmapData.draw( target2, HitTest.getDrawMatrix( target2, hitRectangle, accurracy ), new ColorTransform( 1, 1, 1, 1, 255, 255, 255, 255 ), BlendMode.DIFFERENCE );
                        
                        // Busca la interseccion
                        var intersection:Rectangle = bitmapData.getColorBoundsRect( 0xFFFFFFFF,0xFF00FFFF );
                        
                        bitmapData.dispose();
                        
                        // Esto es en caso de que la precisión sea diferente. No se cumple
                        if( accurracy != 1 )
                        {
                                intersection.x /= accurracy;
                                intersection.y /= accurracy;
                                intersection.width /= accurracy;
                                intersection.height /= accurracy;
                        }
                        
                  //Y ahora la intersección toma las dimensiones del hitRectangle
                        intersection.x += hitRectangle.x;
                        intersection.y += hitRectangle.y;
                        //Y a estas alturas la dimension ya es 0, y ya no detecta la colisión
                        return intersection;
                }
                
                
                protected static function getDrawMatrix( target:DisplayObject, hitRectangle:Rectangle, accurracy:Number ):Matrix
                {
                        var localToGlobal:Point;;
                        var matrix:Matrix;
                        
                  //Crea una matriz
                        var rootConcatenatedMatrix:Matrix = target.root.transform.concatenatedMatrix;
                        
                  //Especifica las coordenadas en global, ajusta su posicion y dimensión
                        localToGlobal = target.localToGlobal( new Point( ) );
                        matrix = target.transform.concatenatedMatrix;
                        matrix.tx = localToGlobal.x - hitRectangle.x;
                        matrix.ty = localToGlobal.y - hitRectangle.y;
                        
                        matrix.a = matrix.a / rootConcatenatedMatrix.a;
                        matrix.d = matrix.d / rootConcatenatedMatrix.d;
                  //Si la precisión es diferente a 1, escalarla de acuerdo a ella. No se cumple para mi caso.
                        if( accurracy != 1 ) matrix.scale( accurracy, accurracy );
 
                        return matrix;
                }
 
        }
 
}


Y eso es. Logré discernir dos puntos donde parece ir bien y un punto en el que ya no funciona, pero no logro saber que es lo que falla entre ellos dos.

Espero que así sea mas cómodo.
Gracias!

Por Gold

4 de clabLevel



 

firefox
Citar            
MensajeEscrito el 16 Dic 2010 05:56 pm
Antes que nada comprueba que funciona tu codigo para las colisiones, vete a un archivo mas simple y trata de hacer colisionar 2 objetos, para ir viendo que pasa te recomendaria que trazes en pantalla esos bitmaps y rectangulos que van creando el HitTest para que sepas que esta pasando.

No lo hagas en un addEventListener porq no seras capaz de distinguir nada.

Por Angel Roberto

Claber

248 de clabLevel



 

firefox
Citar            
MensajeEscrito el 16 Dic 2010 05:57 pm
...Con no lo hagas en un addEventLsitener me equivoque queria poner enterFrame haz la prueba en un nivel mucho mas lento, viendo que rectangulos traza tu codigo de colision vas a ver que pasa en realidad.

Por Angel Roberto

Claber

248 de clabLevel



 

firefox
Citar            
MensajeEscrito el 16 Dic 2010 06:14 pm
¿Quieres decir que compruebe si el código funciona? Si, es correcto. O al menos para en todos los casos menos en este en específico.

Antes de publicarlo aquí hice varias pruebas simples:
- En un nuevo fla. cogí la capa mas cercana del fondo (la que me interesa comprobar) y la puse en la root (copia pegando el movie clip) y la comparé con otro objeto estático. Detectaba bien la colisión entre los dos objetos.

- Modifiqué el segundo objeto para que se moviera según el movimiento del ratón. La colisión funcionaba correctamente con el movimiento de un objeto.

-Modifiqué el fondo para que se fuera desplazando a una velocidad constante como en el juego normal. Funcionaba correctamente (Así que no hay problema con que se estén moviendo)

-Modifiqué el MC del fondo, seleccionando un fragmento del dibujo (el cactus) y transformándolo en un MC nuevo. Me detectaba tanto la colisión con el movie clip en general como con el movie clip dentro del movie clip, según lo que le pidiese (Así que no tiene que ver con que sea un MC dentro de un MC)

Por acabar, hice todas estas pruebas con símbolos nuevos en el juego per se. Y todas funcionaban. Así que el código funciona con todas las condiciones físicas que tiene el fondo. El problema es ese, que no solo no reconoce la colisión del objeto si no que ni siquiera reconoce la colisión con el fondo completo. Y lo que es mas, como la función no devolvió los traces en las primeras instancias, se por seguro que una colisión normal entre los dos devuelve "true". Pero por alguna razón, la compleja no.

Así que si, no tengo la mas remota idea de lo que está pasando y para mi que debe ser la mas grande de las tonterías xP.

Por Gold

4 de clabLevel



 

firefox
Citar            
MensajeEscrito el 16 Dic 2010 06:22 pm
Haz una doble comparacion de colision una simple y una compleja, la normal es un hecho que va funcionar,lo malo que no es a nivel pixel es por esto que usas la compleja, si un hitTest normal te lanza true y el complex false entonces algo pasa en el codigo de colision, tal vez venga en la parte de conversion de coordenadas.

La colision simple es nada mas un target1.hitTestObject( target2 ).

Hago unas pruebas y te avizo, cualquier avance lo tiras por aca.

Saludos.

Por Angel Roberto

Claber

248 de clabLevel



 

firefox
Citar            
MensajeEscrito el 16 Dic 2010 06:37 pm
..............Vale, he estado probando y ha ocurrido algo muy raro.

He cambiado el código del Personaje.

Código ActionScript :

package clases{

   import flash.display.MovieClip;
   import flash.display.Stage;
   import flash.events.*;
   import clases.HitTest;

   public class Personaje extends MovieClip {
      private var pantalla:Stage;
      private var escenario:FFondo;

      public function Personaje(pantalla:Stage,escenario:FFondo) {
         this.pantalla=pantalla;
         this.escenario=escenario;
         //Le he quitado la posición inicial X
         this.y=430;
         addEventListener(Event.ENTER_FRAME,contacto,false,0,true);

      }
      private function contacto(e:Event):void {
         //He hecho que el personaje se vaya desplazando hacia arriba a -2 por enter-frame
         this.y-=2
         trace(HitTest.complexHitTestObject(this,escenario))
      }

   }

}


Y que ocurre? Al principio está false, sube y cuando llega al borde superior se pone durante un momento en True, para luego pasar de nuevo a False. Mediante un trace he conseguido que me diga en que posición Y del personaje el contacto complejo es True, y es de 104 a -14. 118 puntos en total.
Puedo suponer que es un problema de coordenadas, pero el fondo no tiene 118 de altura. Ahora si que estoy perdido :s

Gracias por la constancia

Por Gold

4 de clabLevel



 

firefox
Citar            
MensajeEscrito el 16 Dic 2010 06:41 pm
Acabo de bajar tu proyecto.

Lo veo y te comento.

Por Angel Roberto

Claber

248 de clabLevel



 

firefox
Citar            
MensajeEscrito el 16 Dic 2010 06:51 pm
Man estoy en la oficina y no puedo abrir tu archivo me imaginoq ue es cs4 o c5 y aqui no tengo esas versiones, puedes bajarle la version al fla

Por Angel Roberto

Claber

248 de clabLevel



 

firefox
Citar            
MensajeEscrito el 16 Dic 2010 07:05 pm

Por Gold

4 de clabLevel



 

firefox
Citar            
MensajeEscrito el 16 Dic 2010 07:07 pm
Archivo desactivado (eso marca el link)

Por Angel Roberto

Claber

248 de clabLevel



 

firefox
Citar            
MensajeEscrito el 16 Dic 2010 07:18 pm
Ya está

Por Gold

4 de clabLevel



 

firefox
Citar            
MensajeEscrito el 16 Dic 2010 07:33 pm
Descagado

Por Angel Roberto

Claber

248 de clabLevel



 

firefox
Citar            
MensajeEscrito el 16 Dic 2010 07:54 pm
Intento encontrarle una explicacion logica a tu problema pero necesitaria analizar mas el codigo de la colision.


Para que funcione,en tu clase general no agregues personaje y fondo a stage agregalos a tu clase osea a this.

Código ActionScript :

//Cambia este codigo
stage.addChildAt(escenario,0);
stage.addChild(prota);

//Por este otro
this.addChildAt(escenario,0);
this.addChild(prota);

Por Angel Roberto

Claber

248 de clabLevel



 

firefox
Citar            
MensajeEscrito el 16 Dic 2010 08:18 pm
...Guau, sabía que tenía que ser algo ridículo...¡Pero esto es exagerado!

¡En fin, muchísimas gracias por la ayuda y constancia! ¡Te debo una! :D

Por Gold

4 de clabLevel



 

firefox
Citar            
MensajeEscrito el 16 Dic 2010 08:50 pm
Si esta muy extraño el error, si haces la prueba asi bien simple dos objetos en escena funciona pero si haces un stage.addchild de esos objetos ya no funciona,con mas tiempo vere porq.


Me cobrare eso no lo dudes ;)


Saludos

Por Angel Roberto

Claber

248 de clabLevel



 

firefox

 

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