Efecto de Explosión

El efecto de una explosión en términos de implementación es afectar a los elementos dentro de un radio en particular. En este caso usaremos el ejemplo del empuje físico de los elementos, el producto final debería mostrar como varios objetos son empujados en dirección contraria al centro de la explosión, variando el empuje en base a la distancia de los mismos al centro.

Prefabs

Iniciemos con las prefabs que vamos a necesitar:

  • Una bomba
  • Una Explosión
  • Objetos que empujar

Bomba

En este ejemplo, nuestra bomba va a tener que explotar luego de cierto tiempo, pero en otra aplicación podemos reemplazar la bomba por un proyectil o una mina que explota al contacto. La idea es crear el código de la manera más reusable posible.

Nuestra clase bomba va a ser muy simple, con una referencia a la prefab de explosión y una duración de fusible. En vez de usar un timer, usamos una corutina para hacer el proceso mas legible, aparte de eso, no necesitamos controlar con tanto detalle el valor del tiempo transcurrido.

Bomb
Clase bomba
2018-03-04 18_28_53-Photos
Así se vería nuestra prefab en el editor

 

Explosión

En lo que respecta a la explosión, sabemos que es un objeto que vive por poco tiempo (lo que sea que dure el efecto visual) pero además sabemos que el efecto físico de empuje y el daño va a existir por una fracción de segundo. Si hacemos que el efecto físico este activo por toda la duración del efecto visual, podemos terminar viendo comportamientos extraños, como por ejemplo que un objeto entre en rango de la explosión luego del frame donde esta se activó siendo afectado por el empuje de la misma.

Para esto vamos a tener que crear un script de explosión con un float que maneje la duración del efecto físico en particular sin afectar al efecto visual.

 

expl1
Clase Explosión, mantiene un control interno de la duración del efecto físico. Así como una colección de objetos empujables.

[/caption]En la clase definimos una lista de IPushable, dicha interfaz va a ser nuestro objeto empujable. La lista existe para no aplicar el efecto de la explosión mas de una vez en cada objeto, es una forma de “marcar” el objeto como ya afectado

expl2
Por cada elemento que exista dentro del área designada, la explosión va a llamar a la función PushFrom, esto solo va a ocurrir durante la duración asignada del efecto.

[/caption]Una cosa notable en esta función es que a diferencia con el uso más popular de los eventos de colisiones (OnTriggerEnter, OnCollisionEnter) opte por no usar comparación de Tags. Principalmente porque prefiero optar por un código mas type-safe que estar usando strings para bindear objetos. Si el motor detecta una colisión contra un objeto que no es empujable o que ya fue afectado por la explosión, la sentencia if de esa función se encarga de filtrarlo.

expl prefab
Así se vería la prefab, el componente AutoDispose simplemente destruye el objeto luego de determinado tiempo.

 

Objeto Empujable

Ahora tenemos que darle sentido a eso de IPushable, la idea detrás de usar una interfaz para esto es por extensibilidad del script. En este ejemplo estamos trabajando con física, pero si queremos hacer algo a mano con Transform.Translate, vamos a necesitar otro script y para que ese script sea compatible con nuestra explosión, va a implementar IPushable. En otras palabras, digamos que tenemos objetos físicos y no físicos en la escena, si queremos que ambos sean afectados por la misma explosión, ¿por qué tendríamos que escribir mas líneas en la explosión? Manejando interfaces nos sacamos ese problema de encima, incluso podemos hacer que el efecto de empuje se comporte totalmente diferente según el objeto y no nos representaría ningún cambio en la explosión.

interface
Interfaz IPushable, aplicable y extensible a todo lo que queramos que sea afectado por explosiones.

Como ya se mencionó previamente, este ejemplo usa física, por ende, vamos a estar usando Rigidbody y AddForce. Por ende, vamos a crear un nuevo script que implemente IPushable y le vamos a llamar PhysicsPushable.

ppush1
Al tratarse de un script que manipula física, vamos a necesitar una referencia a nuestro propio rigidbody.

Es hora de implementar la función PushFrom(Vector3 pos, float pushForce) definida en la interfaz. Así como la función Translate(Vector3), AddForce(Vector3) requiere que el parámetro de vector sea transformado a espacio local, es decir, cualquier punto que le pasemos va a tomarse en relación al cuerpo donde es aplicada la fuerza. Logicamente uno pensaría que tenemos que aplicar una fuerza igual al vector definido por la posición de la explosión hacia la posición de nuestro cuerpo, pero como se explica en Posicion relativa y seguimiento el vector resultante de eso como fuerza sería igual a la posición global de nuestro cuerpo rígido.

Lo que en realidad queremos es transformar la posición global de la explosión al espacio local del cuerpo rígido. Con eso tenemos el vector distancia expresado en coordenadas origen. El problema ahora es que no podemos empujar con ese vector porque apuna hacia el centro de la explosión, sino que al vector opuesto. Por último, queremos normalizar el vector (convertirlo a módulo 1) y dividirlo por la distancia entre la explosión y el cuerpo rígido. Cuanta menos distancia, mas fuerte es el efecto.

 

norm
De esta manera calculamos nuestro vector de empuje, usando únicamente la posición de la explosión y la fuerza de empuje
2018-03-04 18_29_43-Photos
Así lucirá nuestra prefab de objeto empujable, para este ejemplo use cubos.
exp
Efecto final del ejemplo

 

Variaciones

Objetos no físicos

¿Cómo podríamos implementar la versión no física de esto? Podemos seguir usando los mismos principios para casi todo, digamos que tenemos una clase similar a la del movimiento de la nave en Chase camera elastica con aceleración y velocidad máxima usando Transform.Translate(). Funciona de la misma manera, el script no físico que implementa IPushable llamaría a otro script de movimiento indicándole hacia donde tiene que moverse y por cuanto tiempo quizás, calculado por la fuerza de empuje. Quizás seria simplemente forzar una aceleración hacia la dirección de empuje y dejar que esta decaiga.

Atracción

También podemos implementar atracción en vez de empuje, podemos declarar la función PushTo(Vector3, float) en la interfaz pushable, la implementación para este ejemplo sería copiando la función PushFrom(vector3, float) y removiendo el negativo en la función de la distancia.

Modularización

En este ejemplo, la fuerza y radio de la explosión estaban definidas en la explosión, pero ¿qué tal si estamos armando un sistema de armas dinámico? ¿Esas variables no deberían pertenecer al arma y no a la explosión? Hacer ese cambio tiene sentido dependiendo el juego que estemos desarrollando, hay que plantearse, en el contexto del juego, ¿que es mas protagonista? ¿La bomba o la explosión? En esos casos podemos implementar soluciones como que los valores de la explosión sean inyectados por la bomba en el momento de la creación de la misma.

2018-03-04 22_10_42-UnityLab - Microsoft Visual Studio2018-03-04 22_10_29-UnityLab - Microsoft Visual Studio

Este tipo de comportamientos son muy abiertos para extender y seguir experimentando con los mismos, de una manera idéntica a esta podemos aplicar un IDamageable para dañar objetos además de empujarlos.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s