Scale Handling(manejador de escala) en Flash
2 11 2006Cuando se realizan juegos en 2D con ciertas características de 3d, como lo es la profundidad, es necesario muchas veces hacer coincidir la escala de los objetos con la perspectiva propia del escenario. En este tutorial muestro como crear un Scale Handling en flash, visual y preciso, que puede ser utilizado en juegos de diversa naturaleza.
Introducción
La perspectiva en los juegos 2D es utilizada con éxito en juegos de todas clases con excelentes resultados. Mientras que en algunos se utiliza como un (muy efectivo)recurso cosmético, en otros es una característica ineludible de funcionalidad. Tal es el caso del típico juego de patear penales al arco.

O en el típico juego de carreras de autos en 2D.

Otros ejemplos del uso de perspectiva podemos verla en Monkey Island, donde el personaje se desplaza por escenarios de distintas naturalezas que combinan varios tipos de perspectivas. En estos casos, el tamaño del sprite debe acompañar al escenario. Si bien esto ultimo es posible evitarlo diseñando convenientemente los escenarios para que el personaje se desplace siempre en un mismo plano, esto le quitaría flexibilidad a los artistas que diseñan los mismos y le haría perder al juego gran parte de su encanto.

Problemas comunes
Al desarrollar estos tipos de juegos, suele darse el caso en que el escenario cambia de alguna manera, o bien su perspectiva, el ángulo del mismo, la línea del horizonte o lo que fuera. Como la imagen de fondo no esta relacionada con el modelo matemático del juego, estos cambios pueden llegar a traernos verdaderos quebraderos de cabeza al intentar hacer coincidir el comportamiento de los objetos con la imagen de fondo.
La solución más común es agregarle a la fórmula que calcula la escala, constantes para ir corriendo la línea del horizonte o variar el ángulo de la "cámara", la altura, etc. Ensuciando nuestro algoritmo y haciéndolo difícil de mantener.
Otra solución es la utilizada por el motor SCUMM, con el que se han hecho las aventuras gráficas de Lucas Arts, como Monkey Island o Indiana Johnes y la ultima cruzada. Lo que se hace en este caso es, dada una grilla que representa el área caminable(y que se utiliza también para el path finding) asignarle a cada celda de la misma un valor de escala, con lo que el personaje cambia la misma dependiendo de la celda en que esté parado. Esta solución es elegante y efectiva, aunque depende del programador asegurar la coherencia del valor de escala de cada celda y su correspondencia con la imagen que se utiliza como fondo. Si bien no he experimentado personalmente con esto, me imagino que en escenarios complejos con más de 100 celdas puede tornarse un tanto engorroso.
La solución
La solución que planteo no pretende ser la definitiva, pero es fácil de implementar y funciona en el 90% de los casos. La misma esta basada en el concepto de "Punto de Fuga".
El punto de fuga es utilizado en el dibujo de escenarios para indicar las líneas paralelas que se unen en el horizonte. La idea que mejor describe el concepto es:
"Mientras mas cerca están los objetos de los puntos de fuga, mas pequeños estos se verán ante el ojo del observador"

Partiendo de este simplísimo concepto generaremos un punto de fuga, bajo la acción del cual, nuestros personajes/objetos/whatever se encojan al acercarse a el. El mismo tendrá las siguientes consideraciones:
- Tendrá un radio de acción: Esto es por un tema de performance, para que no se calcule la escala de los objetos que están fuera de alcance.
- Tendrá una escala mínima: Esta escala final mínima será la que tendrá el objeto al estar posicionado directamente sobre el punto de fuga, evitando de esta manera que su tamaño sea cero para todos los puntos de fuga, haciéndolo invisible. Su escala irá de 1:1 a 1:[escala mínima]. Siendo 1:1 la que corresponde al límite del radio de acción del punto de fuga.
- Solo se calculará la escala para los objetos que se indiquen en una lista creada para tal fin, evitando que otros objetos que están quietos consuman tiempo del procesador innecesariamente.
El siguiente diagrama ilustra lo anterior:

Como puede verse, el objeto al alejarse del radio de acción, mantiene su escala, en el diagrama se ve un caso en el que el Coeficiente Final de Escala(c.f.e) es cero, esto debería cambiarse para ajustarlo al escenario.
El Coeficiente Actual de Escala(c.a.e) es el correspondiente a la posición actual del objeto(en este caso un personaje) dentro del radio de acción del punto de fuca, responde a la siguiente fórmula:
cae = (d0/d)*100-cfe)+cfe
El coeficiente actual de escala(cae) es la variable con la que setearemos las propiedades _xscale e _yscale del movieclip que deseamos someter bajo el control de nuestro "scale handler". Este se obtiene con el cociente entre distancia actual al centro del punto de fuga(d0) y el radio de acción de nuestro punto de fuga(d) multiplicado por 100 menos el coeficiente final de escala(cfe) mas el cfe.
(gracias a Matías Moncho por ayudarme con la fórmula)
Implementación en AS 2.0
La implementación en ActionScript es la siguiente: En las acciones del frame se agrega el siguiente código:
-
//ARRAY de objetos a ser incluidos en la rutina de Scale Handling
-
var arrDepthObjects:Array = new Array();
-
arrDepthObjects.push(this.heroe);
-
var objVanishingPoint:MovieClip = this.VanishingPoint;
-
this.onEnterFrame = function() {
-
switch (Key.getCode()) {
-
case (Key.UP) :
-
heroe._y -= 10;
-
break;
-
case (Key.DOWN) :
-
heroe._y += 10;
-
break;
-
case (Key.LEFT) :
-
heroe._x -= 10;
-
break;
-
case (Key.RIGHT) :
-
heroe._x += 10;
-
break;
-
}
-
handleScale(objVanishingPoint, arrDepthObjects);
-
};
-
//--- Procesa el array de objetos
-
function handleScale(pVanishPoint:MovieClip, pArrObjects:Array) {
-
var i:Number;
-
//var pArrObjects:Array;
-
var mc:MovieClip;
-
trace(pArrObjects.length);
-
for (i = 0; i <pArrObjects.length; i++) {
-
if (pVanishPoint.hitTest(pArrObjects[i]._x, pArrObjects[i]._y, true)) {
-
this.semaforo.text = "HIT!!!";
-
pArrObjects[i]._xscale = pArrObjects[i]._yscale = calculateScale(pVanishPoint, pArrObjects[i], 25);
-
} else {
-
this.semaforo.text = "no hit";
-
}
-
}
-
}
-
//Calcula la escala para un movieclip
-
function calculateScale(pVanishPoint:MovieClip, pTargetObject:MovieClip, pnCFE:Number):Number {
-
var d0:Number = distancia(pVanishPoint, pTargetObject);
-
var d:Number = pVanishPoint._width/2;
-
return (d0/d)*(100-pnCFE)+pnCFE;
-
}
-
-
//Función de la distancia Euleriana, para determinar la distancia entre los objetos y el punto de fuga
-
-
function distancia(p0:MovieClip, p1:MovieClip) {
-
return Math.sqrt((p0._x - p1._x) * (p0._x - p1._x) + (p0._y - p1._y) * (p0._y - p1._y));
-
}
Como es fácil de ver en el código, agrego todos los movieclips que deseo someter al control del ScaleHandler y luego llamo en cada frame a la función que ajusta la misma dependiendo de la distancia de estos al punto de fuga. Lo interesante es que podemos tener varios puntos de fuga que controles los objetos, lo cual dota a esta técnica de flexibilidad y sinplesa.
Ejemplo
Es conocido el dicho de que "una imagen vale más que mil palabras", imagínense a cuantas palabras equivale un ejemplo gráfico con el cual se puede interactuar y experimentar!. Hagan click sobre el flash y luego, con las teclas de cursor, muevan el personaje hacia el horizonte para comprobar la escala.
Para terminar
Esta técnica es bastante flexible, se puede utilizar para otras cosas, como por ejemplo, si el personaje se acerca a una luz de color rojo, su color debería ir cambiando deacuerdo a su proximidad con la fuente de luz. Cambiar el algoritmo para este u otros fines similares es trivial por lo que la técnica de puntos de fuga cobra más utilidad por sus distintas áreas de aplicación.
Archivos del tutorial
Como de costumbre, les dejo los archivos de ejemplo para que los descarguen.








ala me salvaron en esta pagina encontre algo que habia estado buscando aproximadamente 7 horas
Me alegro que te haya servido.
Es un buen tuto, si… y muy interesante pero a mi no me sirve esto concretamente pues estoy buscando informacion sobre la propiedad “scale” del método “Matrix”, un método que según lo que leo, además de reducir la imágen la distorsiona (imaginaos un cuadrado que acercándose desde la derecha al punto de fuga, los bordes izquierdos del rectángulo tienden a “encogerse” hacia el punto de fuga, y llegado un punto, los de la derecha también. Para que lo entendáis mejor, lo que haría un agujero negro a un objeto, o lo que hacen las ventanas de un mac al reducirlas en el dock).
En todo caso este ejemplo que expones es interesante y te felicito por este tuto.
No funciona ni el swf ni el zip
Ya esta!, desde que migré el sitio de directorio algunos links estan rotos.