amigo aquí esta la solución

https://www.youtube.com/watch?v=uRaE-NaxFJA

en la descripción esta el link de descarga del archivo
saludos
PURA VIDA



Ocarina, gracias por el aporte, es una solución alternativa, pero como te fijaras es un puzzle deslizante, el tema de mi proyecto es de puzzle piezas, que se cortan, se revuelven, y se distribuyen alrededor del marco de la imagen para luego ir colocando en ""Drag an Drop".Gracias nuevamente por tu interés en ayudar.
Saludos.

Hola Clamud, no quiero ser insistente,pero me podrías ayudar con el código para dispersar las piezas, es que  quiero terminarlo pronto para luego ir mejorandolo con el tiempo, te agradezco como siempre, tu disposición para ayudar.
Saludos.

hola, hoy le sucedió algo extraño al juego, me marca error en una variable, no he cambiado nada, pues funcionaba muy bien , a que se debe este cambio inusual. te adjunto foto para ver si encuentras alguna razón por la que me arroja este error.
Saludos y gracias

El error debe estar en el evento Create del controlador, ¿se borró la línea de declara la variable?

Acabo de hacer una actualización al motor, hoy no me va a dar tiempo de dar una explicación, funciona bien en :GM8:, pero hay algunos comportamientos extraños en :GMS: (nada grave). Hasta luego.

#20 Julio 17, 2015, 07:45:02 PM Ultima modificación: Julio 17, 2015, 07:47:49 PM por Clamud
Ahora sí, comenzaré a explicar el funcionamiento de del proyecto. He procurado hacerlo lo más sencillo posible, aún así, su nivel de complejidad ha aumentado bastante.

También he subido una nueva versión, en la que se soluciona el comportamiento extraño de las piezas. El problema era suponer que al cambiar la depth también se cambia el orden de los eventos de las instancias, como sucede en GM8, sin embargo, en GMS sólo se cambia el orden de los eventos Draw. Al hacer click en una pieza no se seleccionaba la de menor profundidad, en cambio, se seleccionaba la última que fue creada. Al soltar la pieza seleccionada no quedaba encima, y a veces la profundidad cambiaba de forma extraña. El nuevo método de selección evalúa la depth y usa una lista para guardar los ids en orden de profundidad.

El nuevo comportamiento de la variable depth en GMS ha complicado un poco las cosas. Se han empleado algunos recursos que por lo general son difíciles de dominar por los que inician. Esta forma de seleccionar se podría evitar si se diseña un sistema que no permita la superposición de muchas piezas a la vez, no obstante, el diseño de ese sistema es más complicado.
Aunque en GM8 el sistema funciona bien, no es a prueba de errores. El nuevo sistema es más robusto en lo 2 casos.



Primero se han agregado las siguientes variables al controlador
[gml]
btn_mezclar = false; //estado del boton mezclar
global.calzadas = 0; //numero de piezas calzadas
global.lista = ds_list_create(); //para guardar los ids
[/gml]
para entender la última linea, leer Data Structures en el manual.
Cuando se crean las piezas (con el doble ciclo for) también se guardan los ids
[gml]
...
ds_list_add( global.lista, id ); //guardar id
...
[/gml]

Pasemos al objeto pieza. Según la descripción que diste en mensajes anteriores, se pueden identificar 3 estados:
Al iniciar el nivel el rompecabezas está armado, las piezas inmóviles y no pueden ser arrastradas, llamemos CALZA a este estado.
Al presionar el botón "mezclar" las piezas se dispersan, en este estado, que llamaremos MEZCLA, las piezas no pueden ser desplazadas manualmente.
Una vez dispersas, el usuario puede arrastrar las piezas y armar el rompecabezas, el estado se llama ACTIVO.
Cuando el usuario suelta una pieza cerca o en la posición correcta la pieza se bloquea y no puede ser arrastrada, por lo tanto regresa al estado CALZA.
(Siento que los nombres de los estados y las variables no son muy buenos, tal vez puedas mejoralos XD.)

En el evento Create de obPieza se definen los estados
[gml]
///Estados

CALZA  = 0; //en posicion correcta
MEZCLA = 1; //al oprimir el boton mezclar
ACTIVO = 2; //se puede arrastrar

ESTADO = CALZA;
[/gml]
El uso de una variable estado es muy útil en la programación porque evitamos tener que usar muchos objetos y el código queda bien organizado.

El objeto controlador se encarga de dibujar el botón "mezclar", usando el script scTextoBoton, si lo deseas, el siguiente código se puede usar un un objeto aparte que actúe como botón.
[gml]
///Mezclar piezas

//si se presiona el boton mezclar
if( btn_mezclar )
{
    ds_list_shuffle( global.lista ); //desordenar lista
    global.calzadas = 0; //cero piezas calzadas
    //
    with( obPieza ) //con todos los objetos pieza
    {
        ESTADO = MEZCLA; //cambiar estado
        xfinal = irandom_range( 480,768 ); //posicion final
        yfinal = irandom_range( 64, 448 );
        depth = ds_list_find_index( global.lista, id ); //profundidad aleatoria
    }
}
[/gml]
Aquí se emplea la lista para asignar la profundidad, las piezas quedan con una profundidad aleatoria (de 0 a 8 ) sin repetirse. El estado cambia al apropiado. Y las variables xfinal,yfinal se definen aleatoriamente entre los límites de un rectángulo imaginario en la parte derecha de la ventana.

Como las piezas se encuentran en el estado MEZCLA, ejecutan el sig. código en Step
[gml]
///Mezclar

if( ESTADO = MEZCLA ) //mezclando
{
    if mp_linear_step( xfinal,yfinal,32, false ) //mover linealmente
        ESTADO = ACTIVO; //cambiar estado al llegar a la pos. final
}
[/gml]
(revisa el manual por información adicional)
Cuando la pieza llega a su posición final el estado cambia a ACTIVO y se puede arrastrar
[gml]
///Arrastrar

if( ESTADO == ACTIVO ) //si esta activa
if( global.seleccion == id ) //y seleccionada
{
    if( depth <> 0 ) //si la profundidad es diferente de cero
    {
        for( i=depth; i>0; i-=1 ) //para las piezas que tienen menor profundidad
        {
            global.lista[|i] = global.lista[|i-1]; //recorrer un lugar
            global.lista[|i].depth += 1; //aumentar la profundidad
        }
        depth = 0; //esta se coloca encima de otras piezas
        global.lista[|0] = id;
    }
    x = mouse_x - rx; //se puede arrastrar
    y = mouse_y - ry;
}
[/gml]
Aquí se emplea nuevamente la lista y se usa una sintaxis que se explica en la sección Accesors del manual.

Mas tarde continuaré la explicación.  ;)

Gracias Clamoud, tienes todo mi respeto, voy a estudiarlo para digerir mejor los códigos, has sobrepasado mis expectativas. Una pequeña pregunta, en que momento se destruye la informacion de las listas y datos y cuando se reconoce que el juego se ha completado con exito? Ahora voy a descargarlo , probarlo y luego te comento.

Antes de profundizar en el código para arrastrar la pieza, es necesario revisar el código para seleccionar; en el evento Left Pressed se tiene lo siguiente
[gml]
///Seleccionar pieza

//solo se puede seleccionar en estado activo
if( ESTADO == ACTIVO )
{
    if( global.seleccion <> noone ) //si hay una pieza seleccionada
    if( global.seleccion.depth < depth ) //y su profundidad de es menor
        exit; //salir
       
    global.seleccion = id; //seleccionar esta pieza
    rx = mouse_x - x; //posicion relativa
    ry = mouse_y - y; // al puntero
}
[/gml]
Primero se revisa que el estado es ACTIVO, después, si ya hay una pieza seleccionada se hace una comparación de profundidades, si la pieza seleccionada tiene una profundidad menor ya no tiene caso continuar y se escribe exit.
Si no se cumplen las condiciones anteriores la pieza actual tiene posibilidades de quedar seleccionada, entonces se guarda el id y se calcula la posición relativa al puntero.

Cuando termina la iteración del código de selección en todas las piezas, la que queda seleccionada se puede arrastrar (en el evento Step)
[gml]
///Arrastrar

if( ESTADO == ACTIVO ) //si esta activa
if( global.seleccion == id ) //y seleccionada
{
    if( depth <> 0 ) //si la profundidad es diferente de cero
    {
        for( i=depth; i>0; i-=1 ) //para las piezas que tienen menor profundidad
        {
            global.lista[|i] = global.lista[|i-1]; //recorrer un lugar
            global.lista[|i].depth += 1; //aumentar la profundidad
        }
        depth = 0; //esta se coloca encima de otras piezas
        global.lista[|0] = id;
    }
    x = mouse_x - rx; //se puede arrastrar
    y = mouse_y - ry;
}
[/gml]
Como en el código previo, se revisa que el estado es ACTIVO, después se revisa que la pieza está seleccionada. Ahora viene la parte complicada, si la pieza tiene profundidad diferente de cero, es posible que esté parcialmente cubierta por otra pieza, pero se necesita que la pieza esté completamente visible, para logar ese objetivo la pieza actual se coloca en profundidad cero y las demás piezas aumentan de profundidad. El siguiente diagrama puede ayudar a entender el algoritmo


  • Las piezas tienen profundidades de 0 a 8, que se asignaron de forma aleatoria.
  • Se selecciona una pieza que no tiene profundidad cero, entonces la sacamos temporalmente de la lista.
  • Las demás piezas se recorren para ocupar el lugar vacío, aumentando su profundidad.
  • La pieza seleccionada se reinserta en la posición cero, y su profundidad se hace cero.

Vamos a observar qué sucede al soltar la pieza, en el Evento Left Released
[gml]
///Soltar pieza

if( ESTADO = ACTIVO ) //si esta activa
if( global.seleccion = id ) //y esta seleccionada
{
    global.seleccion = noone; //seleccionar nada
   
    //si esta cerca de la posicion correcta
    if( point_distance(x,y,xstart,ystart) < 64 )
    {
        x = xstart; //plop
        y = ystart;
        ESTADO = CALZA; //cambiar estado
        global.calzadas += 1; //incrementar contador
    }
}
[/gml]
Se reutilizan las 2 primeras líneas, después se indica que no hay nada seleccionado. Abajo se hace el efecto "snap"; se han agregado 2 líneas, una incrementa el contador de piezas calzadas y otra cambia el estado de la pieza. Entre esas líneas se puede reproducir el sonido "plop" que mencionado.

Sólo falta ver el código que determina que el rompecabezas se ha completado. Se encuentra en obControl, en el evento Begin Step
[gml]
///Nivel completado!

if( global.calzadas == 9 )
{
    show_message( "Nivel completado!" );
    global.calzadas = 0; //no mostrar el mensaje otra vez
}
[/gml]
Es bastante sencillo, se comprueba que el número de piezas calzadas es igual al número total de piezas, se muestra un mensaje y la variable global.calzadas se reinicia para no mostrar el mensaje constantemente. En esta demo no ocurre nada mas, bueno, se puede mezclar otra vez. Vale la pena mencionar que la función show_message() sólo se recomienda en Windows.

Esto ha quedado bastante extenso, y eso que es la parte fácil, aún quedan un montón de detalles por agregar y muchas pruebas por hacer. Intentaré ayudar en lo posible, hasta pronto.

Encontré un bug, cuando se suelta el botón del ratón y el puntero está fuera de la máscara de colisión, la pieza no se suelta, para arreglarlo cambia el evento Left Released por Global Left Released.

me gusta aprender cosas contigo clamud, eres una p... makina, te felicito..
haber si tambien puedes ir explicando como meter mas puzzles y de mas piezas.
saludos

hola, estoy de nuevo aquí, y darte infinitas gracias por tus aportes tan valiosos.Estoy anonadado con tanta información, es decir de lo que parecía un proyecto simple para mi se ha convertido un gran desafío.No te imaginas cuanto tiempo pase buscando en la red información para hacer este proyecto que a simple vista pareciera muy superficial, pero cuanta sabiduría has logrado impregnarle.Eres todo un maestro, con mucha paciencia, interés y sobre todo muy didáctico. Solo por curiosidad, has pensado en hacer tutoriales así de esta calidad, con sustentación teórica y con ejemplos demostrativos del comportamiento lógico del uso del GML? .
Comentario aparte, no estoy en contra de los juegos de plataforma, pero este tipo de juegos no violentos invitan a darle una nueva faceta al juego, sin balas sin agresión , ayuda mucho al desarrollo en los niños y adultos. Voy a proseguir con el proyecto y cuando lo tenga completo , con sonidos y uno que otro agregado , si lo tomas a bien, me gustaría enviártelo para compartir los resultados de tu enseñanza.
Saludos.

Hola Clamud, estoy batallando con la posición del rompecabezas en la room. Sucede que siempre me aparece en la esquina superior izquierda, el tamaño del sprite es width 322, Height 258, en sus coordenadas  Origin X 161 y 129 , esto da porque esta activado el botón de Center. En la room del juego el rectángulo negro inicia en la esquina x 128, y 64, el formato de la room es de 1280 x 720. En evento del objCntrol Draw, el rectángulo negro lo posicione en 128,64,864,608,false con una depth de 10, porque las piezas se hundían en el área negra y así logre superar ese detalle...he hecho de todo pero no encuentro por donde modificar la posición del rompe..me podrías orientar por donde buscar por favor-.
Gracias, saludos.

La posición del rompecabezas se cambia en el evento Create de obControl, en donde dice:
[gml]
with instance_create( i*dx+96, j*dy+96, obPieza )
[/gml]
Los números (96,96) son la posición inicial de la pieza que está arriba a la izquierda.
Saludos.

como insertar nuevos puzzles? hay que añadir un nuevo objeto control con la informacion del nuevo puzzle? estoy un poco perdido, pueden guiarme un poco?

Hola Clamud, Musteroix, saludos y gracias por acompañarnos en esta aventura. Ya resolví lo de la posición en la room, peque;os detalles hacen la diferencia...tengo que fijarme mejor, pero ahora me aparecen mucha separación entre las instancias, voy a revisar los sprites, pues creo que por ahi debe andar el asunto. Quiero preguntarles algo, como saber el id de cada pieza, es decir me gustaría que al hacer la colisión en el lugar correcto apareciera una especie de premio(mensaje) con sonido, y si es equivocado pues agregar el sonido de rrrrr y regresa a su lugar inicial,me imagino que se pueden usar alarmas?,... se le podrán agregar mensajes cada dos o tres piezas piezas correctas_...es mucho pedir?_....con lo de la cantidad  de piezas, desde mi novatada lo que hice fue duplicar el obj control y cambiarle el contenido en una nueva room,tengo dos una de 9 piezas y la segunda room  puse a 20 piezas tal vez no es lo ideal pues consume tal vez muchos recursos, pero es lo que se me ocurrió,no se como hacer para cambiar las imágenes( tener varias opciones de imágenes por cada cantidad de piezas , haría el juego bien entretenido..a lo mejor con algún switch como el botón de mezclar, pero no se como hacerlo...tal vez Clamoud nos ayude pronto pues esto se pone cada vez mas interesante.
Saludos.