Documentos de Académico
Documentos de Profesional
Documentos de Cultura
En Haxeflixel, para detectar si dos entidades colisionan se utilizan las funciones overlap() o
collide(), ambas son accesibles a través de la clase FlxG.
Tanto overlap() como collide() se utilizan de la misma manera: reciben como argumentos las
dos entidades para las cuales necesitamos saber si existe colisión y una función callback que
será invocada ante un resultado positivo.
class PlayState extends FlxState
{
var sprite1: FlxSprite;
var sprite2: FlxSprite;
En el ejemplo, cuando sprite1 y sprite2 se solapen se llamará a la función callback onCollision()
definida dentro de la escena. La función recibirá como argumentos ambos sprites que
chocaron (más adelante veremos la utilidad de recibirlos como argumentos).
Las llamadas a overlap()/c
ollide() deben realizarse (generalmente) dentro de update() ya que es
necesario comprobar colisiones en cada nueva actualización.
Tanto collide() como overlap() funcionan de la misma manera, la única diferencia es que la
primera, al detectar una colisión, además de invocar a la función callback separa los objetos
entre sí (invoca a FlxObject.separate() para hacerlo). Entonces, collide() resulta útil, por ejemplo,
cuando por ejemplo queremos que un personaje choque con las paredes (nos interesa
separarlo de la pared para que no quede “metido” dentro de ella). Por otro lado, overlap() es
adecuado, por ejemplo, cuando por ejemplo un proyectil impacta contra un enemigo. En este
caso no nos interesa corregir las posiciones, ya que seguramente eliminaremos al proyectil o al
enemigo.
Propiedades físicas de los objetos
Al igual que existen propiedades para modificar la posición y movimiento y de un sprite
(velocity, acceleration, drag, etc). Existen también otras propiedades que determinan el
comportamiento del objeto ante las colisiones. En la siguiente tabla se muestran y explican
algunas de esas propiedades.
Propiedad Tipo Descripción
width Float Ancho de la caja de colisión (AABB) del objeto (notar que el
tamaño de la caja de colisión puede no coincidir con el
tamaño del sprite).
height Float Alto de la caja de colisión (AABB) del objeto (notar que el
tamaño de la caja de colisión puede no coincidir con el
tamaño del sprite).
allowCollisions Int Indica sobre cuáles de los flancos es posible colisionar con
el objeto. Los valores posibles son: ANY, CEILING, DOWN,
FLOOR, LEFT, NONE, RIGHT (definidos en F lxObject). Los
valores se pueden combinar con el operador |. Por ejemplo,
la siguiente línea hará que sprite1 sólo pueda colisionar con
otros objetos por la derecha y por arriba:
sprite1.allowCollisions = FlxObject.CEILING |
FlxObject.RIGHT;
Resulta útil, por ejemplo, para definir plataformas con las
que se puede colisionar sólo desde arriba.
solid Bool Si el objeto puede o no colisionar con otros. Por defecto es
True. Si su valor es falso las colisiones no son procesadas
para este objeto.
mass Float Masa virtual del objeto. El valor por defecto es 1. Es
utilizado en conjunto con elasticity en la resolución de
colisiones.
Un grupo puede incluso contener a otros grupos, aunque es mejor, siempre que sea posible, que
un grupo contenga objetos de una misma clase (puede ayudar a simplificar algunas cosas). Un
objeto puede estar en más de un grupo a la vez (aunque se debe tener cuidado de no agregar un
objeto más de una vez a la escena, ya que se actualizará y dibujará varias veces) y no todos los
grupos tienen que ser agregados a la escena (algunos solo se arman para simplificar la
detección de colisiones)
En el código anterior, se invoca a forEach() pasando como argumento una función callback
inline o anónima. Esa función anónima se ejecutará una vez por cada elemento del grupo. En
cada ejecución recibirá como argumento uno de los elementos del grupo. Es necesario notar
que FlxGroup almacena los elementos que agrupa como referencias al tipo FlxBasic (el tipo de
objeto más básico que puede existir dentro de una escena). Es bastante probable que lo que
almacenemos dentro de un grupo no sean objetos de tipo FlxBasic, sino de algún tipo derivado
como FlxSprite o un tipo propio (en el caso del ejemplo anterior, el tipo Enemy, definido por
nosotros). Si necesitamos invocar a alguna función miembro que se encuentre definida en la
clase del objeto (en éste caso la función getDamagePoints() se encuentra en Enemy no en
FlxBasic) será necesario convertir la referencia de FlxBasic al tipo correspondiente utilizando la
orden cast().
Profundizaremos más adelante estas cuestiones relacionadas con clases propias y
conversiones de tipos. Por ahora sólo es importante saber que, entre otras cosas, existe una
función para recorrer los miembros de un grupo.
Nuevamente, no se puede colisionar contra un grupo, sino contra algunos de sus elementos. La
función callback siempre recibirá los objetos que colisionaron (y no el grupo). Si existiera una
colisión simultánea entre hero y más de un objeto del grupo walls a la vez, la función
onHeroWalls() será invocada varias veces, por cada par de objetos que colisionan.
Entonces, la manera más eficiente de manejar colisiones es agrupar objetos (se pueden incluir
grupos dentro de otros grupos) de manera de realizar la menor cantidad de llamadas a
collide()/overlap(). El siguiente ejemplo muestra cómo agrupar una serie de elementos que
pueden colisionar con el personaje principal.
// crear al personaje
var hero = new FlxSprite();
// comprobar colisiones
FlxG.overlap(hero, heroCollideables, onHeroCollide);
En el ejemplo, la función overlap() se invoca una única vez con el personaje y un grupo que a su
vez contiene otros grupos que contienen todas las cosas con las que el personaje puede
colisionar.
La función onHeroCollide() será invocada con el personaje (hero) y el elemento del grupo
heroCollideables que haya chocado con él. Si bien esto resulta muy eficiente surge otro
problema: la función onHeroCollide() recibirá como segundo parámetro un objeto que puede ser
un item o una pared. Como claramente el manejo de la colisión es muy distinto para cada caso,
es necesario disponer de alguna forma de averiguar el tipo del objeto. Esto puede lograrse
mediante la función g etClass() como muestra el siguiente ejemplo:
function onHeroCollide(hero: Hero, object: FlxObject){
if(Type.getClass(object) == Wall){
FlxObject.separate(hero, object);
}else if(Type.getClass(object) == Item){
var item: Item = cast(object, Item);
points = points + item.getPoints();
item.kill();
}
}
En primer lugar, cabe mencionar que siempre conviene crear una clase específica para cada
tipo de entidad del juego (ya veremos más adelante cómo hacerlo). De esta manera no sólo
queda más ordenado el código, sino que se puede utilizar getClass() para distinguir entre
distintos tipos de objetos.
Cuando un objeto recibido por la función callback puede ser de varios tipos distintos el
parámetro debería ser de un tipo más general (en este caso FlxObject, ya que todos los objetos
derivan de esa clase).
Una vez dentro de la función, se puede utilizar getClass() para saber cuál es el verdadero tipo
del objeto (el objeto hereda de FlxObject pero su tipo verdadero es otro más específico, en este
caso Wall o Item). Si necesitamos invocar a alguna función específica de una clase será
necesario hacer una conversión con cast() (en este caso getPoints() pertenece a la clase Item y
no está disponible en FlxObject). En el caso de que el objeto sea una pared, simplemente
separamos al personaje de la misma invocando a s eparate() con ambos objetos.
Depurador visual
Haxeflixel incorpora un potente depurador gráfico. El mismo puede abrirse utilizando las teclas
F2 y ‘\’. Alternativamente, la interfaz del depurador se puede mostrar/ocultar desde código
haciendo:
FlxG.debugger.visible = true;
Es importante notar que, para que el depurador pueda habilitarse, es necesario que el comando
utilizado para construir el ejecutable incorpore el flag -debug. Por ejemplo:
lime test neko -debug
El depurador posee muchas funcionalidades. Una que nos resultará particularmente útil es la de
visualizar las cajas de colisión de los objetos, la cual se puede activar presionando el botón con
forma de cubo ubicado en la esquina superior derecha de la interfaz. Alternativamente, también
se puede activar por código escribiendo:
FlxG.debugger.drawDebug = true;
Se puede consultar acerca de éstas y otras funcionalidades en la d
ocumentación de Haxeflixel.
Descargar el código
completo del ejemplo
1
Descargar el código
completo del ejemplo
2