Objetos que dan Buffs a grupos

Jastro

Buenos dias!

Mirando el hilo de Toy Brawler de que es un MOBA que se está realizando en Unity de la mano de @Zarkend ha surgido una duda muy interesante, que creo que seria interesante comentarla entre todos y a ver que nos encontramos x)

La pregunta es esta

Copio y pego aquí
El tema que me preocupa y me gustaría saber como programador, es como afrontáis el tema de calcular todas las variables de grupo, ya sean buffs de team, individuales. Es decir, si Player 1 tiene un objeto que da a la party -1 de CD a las habilidades cuando están cerca suya. Como afectaría que estuviera a un Player 2 entrando y saliendo del área. O un efecto que atañe al enemigo como 10% a defensa por decir algo.

2
BLZKZ

Curioso, y más con lo metido que estuve en MOBAs. Gracias por compartir :D

Ridote

Estamos hablando acerca de programación y rendimiento o simplemente de la lógica de que pasaría en caso de los buffos?

Si entras o sales yo el CD lo calcularía al lanzar la habilidad y si entras o sales te jodes, haberlo hecho dentro. No tiene sentido que cada vez que entres te reste a menos que lo que haga el buffo sea restar -1 cada X tiempo.

Que tienes dos buffos similares, una de dos. O aplicas el más gordo o los aplicas ambos. La estructura que tengas para modelar los buffos también hará que esto sea más o menos sencillo y más o menos eficiente.

Otra cosa muy interesante es lo que hacen en LOL, tener buffos únicos de manera que si me compro 20 objetos con buffos no te dé el buffo 20 veces, sólo una vez independientemenete de quién tenga el buffo. O incluso podrías hacer que se acumulase una vez por personaje.

Si pones unos cuantos ejemplos concretos y numerados podemos darte opiniones.

B

Voy a profundizar un poco lo que es la cuestión. Este tema viene por los MOBAS u otros juegos en los que los que haya buffs y debuffs en tiempo real.

Voy a usar la nomenclatura del LoL que fue tal como me lo explicaron a mí.

Tenemos a Player A con su AD = 10
Se compra la espada de Doran que aumenta en +8 su AD.

Una forma simple sería que al comprar la espada, su script buscase el stats del personaje y se lo añadiese. Pero ahora viene el inconveniente ¿y objetos que tienen habilidades especiales o que su extra es un %? ¿O efectos que son temporales como un extra de daño o un slow? Tendríamos que crear scripts personalizados para este tipo de eventos con lo que no estaríamos reutilizando nuestro código y a la hora de realizar un cambio, podría afectar a otras partes del código.

En este caso, usamos Delegates. Digamos sin entrar en detalles que un delegate es una función a la que los scripts pueden inscribirse y no importa quien ejecute la llamada que todos los que estén inscritos actuarán en consonancia con esa función dependiendo de lo que realice el script subscrito.

Voy a usar c# que es el que uso para Unity3D
# DELEGATE

public class PlayerEvents : Monobehaviour{

// Declaración del Delegate
public delegate void PlayerBuff(TypeStat statAffected, float valueAffected);

// Declaración de los eventos a los que los scripts se subscriben
public static event PlayerBuff onPlayerStatUp;
public static event PlayerBuff onPlayerStatDown;

public static void PlayerStatUp(TypeStat statAffected, float valueAffected){
         if(onPlayerStatUp != null)
               onPlayerStatUp(statAffected, valueAffected);
}
public static void PlayerStatDown(TypeStat statAffected, float valueAffected){
         if(onPlayerStatDown != null)
               onPlayerStatDown(statAffected, valueAffected);
}
}

#PLAYER CONTROLLER

public class PlayerController : Monobehaviour {

Stats statsPlayer[TypeStats.Count];

//Al activarse el jugador
public void Awake(){
     //Nos subscribimos al evento de Buffs
     PlayerEvents.onPlayerStatUp += this.PlayerStatUp;
     PlayerEvents.onPlayerStatDown += this.PlayerStatDown;
}

//Al destruirse
public void Destroy(){
     //Nos desubscribimos al evento de Buffs
     PlayerEvents.onPlayerStatUp -= this.PlayerStatUp;
     PlayerEvents.onPlayerStatDown -= this.PlayerStatDown;
}

//Podremos tener funciones que recojan las otras 2 si queremos que se puedan activar y desactivar a nuestro antojo y añadirlas en Awake y en Destroy/Disable.

public void PlayerStatUp(TypeStat statAffected, float valueAffected){

statsPlayer[statAffected] += valueAffected;
      
} public void PlayerStatDown(TypeStat statAffected, float valueAffected){ statsPlayer[statAffected] -= valueAffected;
} } #Script Item public class Item : Monobehaviour{ public void Awake{ PlayerEvent.PlayerStatUP(TypeStat.AD, 8f); } public void Destroy{ PlayerEvent.PlayerStatDown(TypeStat.AD, 8f); } }

No tengo ahora manera de comprobar si es funcional y hasta después de unas horas no volveré a casa. A ver si hago algo con Unity3D para que podáis verlo.

Con estos ejemplos, hacemos que el personaje esté subscrito a esos eventos y que al añadir el script del item, este lance la función y se vea afectado el jugador, sin tener que mantener referencias de variables entre scripts dentro de un mismo objeto, además sin importar cuantos se añaden o que realicen.

La ventaja de usar Delegates es que tiene mil y un uso. La contrapartida es que son eventos que están en constante ejecución y hay que tener muy controlado el deshabilitarse cuando no van a ser usados.

Una utilidad para los delegates es activar y desactivar el movimiento del personaje desde una función. Alterar el como se mueve al estar confuso. El lanzar el evento y todos los objetos que estén subscritos se vean afectados por igual.

Imaginad el comecocos. Todos los fantasmas están subscritos al evento de que Pacman coge la cereza pero como este no está activo, actúan normalmente recorriendo el mapa. Entonces Pacman se come la cereza activando el evento del Delegate que hace que los fantasmas se tornen blancos, cambien su IA por una huidiza y que encima puedan ser comidos por Pacman. Además se activa un contador indicando con la música el tiempo restante. Una vez pasado el tiempo estipulado, se desactiva ese evento del Delegate y como se desactiva, vuelven los fantasmas a utilizar su IA normal y el destruir a Pacman. De esta manera, es el Actor el que tiene el peso del control de lo que ocurre, en vez de ser un Controlador el que maneje todo o teniendo que hacer referencia a elementos dentro de la pantalla y actuar sobre ellos.

1 1 respuesta
FireHermes

#1 si te preocupa el como(en terminos de espacio fisico), en unity hay triggers y colliders con diferentes formas geometricas, aparte de lo que ha dicho #4 que no se me habria ocurrido, podrias de forma mucho menos elegante mas o menos lo siguiente:

haces un script que tenga un cast a la clase jugador o que obtenga las referencias de los jugadores que esten en la zona del trigger a traves del onTriggerEnter/stay/exit...
-desde ahi modificas los parametros que quieras del personaje del cual tengas la referncia: por ej daño directo, llamas a la funcion de recibir daño o aplicas el daño etc
-si es un debuf o un boost en una coorrutina deberias encargarte de, en x segundos quitar el estado que le ha añadido tu jugador
(como mas de un jugador puede añadir estados, tendrias que apañartelas y tener la estructura para recibir diferentes estados de diferenes players, lo cual te biene bien igualmente por si quieres saber quien se lleva una kill con un prender o para saber quien tiene asistencia en una kill etc etc)

-si no quieres coorutinas porque a veces se quedan en memoria, te tocaria tener una libreria con timers para gestionarlo tu en el update o donde quieras

PD: no es tan elegante como usar un delegate, pero no sabia que existian en unity

1
KeTo

¿Cuando dices -1 al CD te refieres a un segundo menos?, porque yo ahí veo más un fallo de diseño que otra cosa xD

1 respuesta
B

#6 Cualquier cosa que se te pueda ocurrir como habilidad.

Ya sea reducir el cd 1 segundo. Como aumentar en 10% la velocidad o aumentar el AD durante 10 segundos y luego irse el buff

1 respuesta
KeTo

#7 Digo que me parece un fallo de diseño porque puede dar lugar a muchos casos extremos y casi siempre voy a preferir reducción de CDs por porcentajes que por segundos en sí.

Por ejemplo, si tienes una habilidad/objeto que tiene un CD de 2.5 seg, este objeto directamente va a romper esta habilidad.

Y luego como se comenta arriba, ¿qué pasa cuando sales del radio de acción?, ¿le sumas un segundo?, ¿lo dejas tal cual y luego no lo vuelves a reducir cuando vuelva a entra en la zona?, creo que es demasiado lioso para el usuario.

2 respuestas
FireHermes

#8 el tema de los segundos dependera de como tengas implementado el timer para el cool down, si fuera una cuenta regresiva en un float directamente podrias restarle los segundos al cooldown, o directamente bajar el tiempo de cooldown maximo:

se me ocurre por ejemplo:

prender CD de 45seg
una vez usado, empieza una cuenta en la que a una variable float con valor 0 le vas sumando el tiempo que va pasando, al llegar a 45, tienes disponible el hechizo otra vez.
o bien bajas el total de 45 seg, o al contador de tiempo pasado le sumas +X (donde ese x sea un porcentaje del cooldown total, 45 )

1
Fyn4r

Entiendo que hay que diferenciar entre un efecto en área instantáneo y uno en el tiempo.
Es decir, pongamos un efecto instantáneo como una habilidad que reduce el CD de las habilidades de los jugadores a su alrededor por ejemplo. En este caso habría que ver quién está dentro del área de efecto en el momento en el que se decide activar la habilidad. Que alguien ande entrando o saliendo es irrelevante porque el efecto ya ha pasado, si no estaba dentro de alcance pues mala suerte.

En el otro lado está la habilidad activa durante cierto tiempo, por ejemplo un área que cure a los personajes por una cantidad mientras se mantengan dentro. En este caso el hecho de que el jugador ande entrando y saliendo afecta puesto que puede "perderse" alguno de los ticks. En cualquier caso, una habilidad con efecto en tiempo se parece mucho a una habilidad instantánea que se repite durante un tiempo, no se si este planteamiento sería viable XD

Se me acaba de ocurrir mientras escribía que hay habilidades que dan efectos permanentes (áreas típicas de juegos online que te sube un stat mientras estés en el rango de acción).

Al final el problema que veo (sin contar el hecho de tener que llevar control del tiempo que tiene que estar un área "funcionando") es conocer en tiempo real quién está dentro del área, quién entra y quién sale de ella, con la carga que ello conlleva. Aquí se viene el tema de donde debería estar la lógica, es decir, que componente es el encargado de calcular y decidir a quién se le aplica el efecto del área o habilidad, puesto que es muy probable que haya más de una activa en un momento determinado.

He escrito cosas que se me vienen a la cabeza para entender un poco más el problema (no he resuelto nunca algo así) y creo que he escrito un montón para nada, a ver si a alguien el vale jajaja

P.D El tema de cómo aplicar los efectos al jugador, actualizar habilidades y stats creo que da para otro tema xD
P.D2 muy interesante el post y la pregunta, porque es válido para muchos juegos y situaciones.

1
B

#8 En el caso de un buff. Sería como este objeto:

Locket of the Iron Solari

Unique: Grants a decaying shield to nearby allied champions and yourself for up to 2.5 seconds, absorbing up to 60 - 434 (based on highest level between target and caster) (+ 2% - 36% (based on level) of caster's bonus health) damage (90 second cooldown) (600 range).

Si estás en rango, recibes el buff, si no lo estás, aunque ya esté activo, no lo obtienes.

Fyn4r lo ha explicado bastante en como sería el uso.

A una habilidad activada y permanente, tienes el Monsoon de Janna

Janna surrounds herself in a magical storm, throwing enemies back. After the storm has settled, soothing winds heal nearby allies while the ability is active.

Summons forth the might of the wind to knock surrounding enemies back and restores 100/150/200 (+50% Ability Power) Health to nearby allies each second for 3 seconds.

Para este caso, tienes un script que sea "Curar cada 3 segundos y de añadido la cantidad de curación.".
Hay varias formas de plantearlo, que sea la comprobación cada segundo o cada 3 secs.

Si el personaje está dentro, a su personaje, se le agrega el script de cura y si se sale durante esos 3 segundos, seguiría teniendo la cura. Pero al dar el tick 3 al no estar a rango, pierde la cura al irse de la zona. En este caso, hay que contar cuando da el tick de la cura. Si al segundo 0 y luego al 3 o esperar al 3. Esto daría juego a se activa la cura, me curo en el tick, salgo de la zona durante 2 secs, hago acciones y antes del 3 segundo, vuelvo a meterme para que me de el tick.

Otra opción es que el script se refresque en cada tick mientras esté en la zona por cada segundo activo, pero al salirse, lo pierde.

Y para mí la más viable para este caso. NO hay script para los personajes dentro y el que lo controla es el propio héroe que tiene el script de la habilidad activada. El script con una coroutine mantiene el conteo de 3 segundos. Al llegar a los 3 segundos, lanza un sphere cast y a todos los personajes dentro, les cura y a los que están fuera no.

1 1 respuesta
KeTo

#11 Vale, pensaba que era de otra forma, así tiene sentido, claro.

1 comentario moderado
DaRk-eXe

no me he leido todos los posts y no programo juegos xD pero daré mi opinión because potato, os pongo sobre aviso.

Usad observers, si son auras, cada aura debería tener un collider con su radio de acción y luego definís el enterCollider y el exitCollider (me estoy inventando completamente los nombres), cuando algo entre en el collider, añades ese objeto como observer de la cosa que entra en el collider, cuando salga lo sacas. Cuando quieras calcular los buffos de un muñeco en un momento determinado, miras todos los observers que estén en ese momento y hacer el calculo.

Creo que el detalle para que no sea muy costoso es simplemente añadir y quitar observers al entrar y salir de cada collideren lugar de estar calculando que tiene cerca en cada loop.

3 respuestas
B

#14 Los Delegates actúan como observers. El script donde ligas el delegate está a la escucha de ese evento y cuando se activa reacciona.

Son dos maneras de captar esto. Yo me decanto por los delegates porque tengo mejor uso de ellos, pero un Notification Center bien estructurado no tiene nada que envidiarles.

BLZKZ

#14 como se nota el HoN ahí

1
1 comentario moderado

Usuarios habituales