18 septiembre, 2006

Flexible complejidad

Ya he leído el documento de diseño, hablado con los diseñadores y discutido con ellos cómo debe funcionar el sistema de alerta de los enemigos. En principio, es algo tan sencillo como que si ven al jugador, su nivel de alerta cambia de "patrulla" a "persecución", te buscan durante un rato y si te encuentran, te atacan, mientras que si consigues despistarlos, vuelven al estado de patrulla al cabo de un tiempo. Suena bien, es lógico y razonable, y la mayoría de los juegos funcionan así. Satisfecho y confiado me dirijo a mi puesto a implementar la funcionalidad.

Lo primero que observo es que no está claro lo que significa "ver" al jugador. Me entran las dudas de si será necesario algún tipo de cono de visión. O si también se podrá detectar al jugador oyéndolo, sin verlo. Es muy probable que los diseñadores quieran controlar externamente cuándo un enemigo está autorizado a ver al jugador y cuándo no. Además por supuesto, se deben poder configurar parámetros como distancia de visión, máximo ángulo vertical y horizontal, etc. Se me ocurre que será interesante también tener en cuenta si algún elemento externo afecta la visión de los enemigos, como el jugador destruyendo bombillas o lanzando una bomba de humo. Lo mejor será preparar un componente de percepción sensorial, que los enemigos usen. Dicho componente tendrá una lista de servicios genéricos que se registrarán en el mundo lógico para recibir eventos. Los eventos atravesarán una serie de filtros abstractos que se podrán añadir para restringir rangos y ángulos, u otras circunstancias como oscuridad o paredes que obstruyen tanto vista como oído. Para conseguir la funcionalidad básica, empezaremos por implementar el servicio de visión, y aplicaremos un filtro para detectar si la visión está obstruida que consista en lanzar un rayo contra el posible objetivo.

Claro, pero aunque el objetivo normalmente sea el jugador, es posible que queramos que los enemigos luchen a veces entre si. Necesitaré organizar los personajes en bandos, en facciones. Asociar cada uno a un bando y un grupo, y luego definir en algún sitio centralizado las relaciones entre ellos, si son enemigos, aliados o neutrales. En cada actualización de la percepción, los personajes evaluarán su entorno usando el sistema de percepción y podrán clasificar a los otros personajes según su bando. Además, las relaciones entre ellos pueden cambiar fácilmente durante el juego, así que puede haber alianzas inesperadas, o nuevos enemigos o compañeros para el jugador. Suena bien, ahora a ver cómo deben reaccionar.

Una buena forma de estar preparado por si en el futuro es necesario incorporar nuevas o diferentes reacciones es implementar un traductor, que convierta los estímulos externos recibidos por la percepción en estados emocionales del personaje. Así, si ve a un enemigo, podemos incrementar su hostilidad, aunque si el enemigo viene muy bien preparado, también afectará a su miedo. Con este sistema, debería ser fácil poner a los enemigos a huir del jugador, si hiciera falta. La implementación la haré usando un mapa cognitivo. Definiremos una lista de estados emocionales, y cada posible evento percibido llevará asociado su efecto, positivo o negativo, sobre dichos estados. También podemos relacionar unos estados con otros, de modo que si la hostilidad sube por encima de cierto valor, el miedo baja o desaparece para simular que el personaje está tan furioso que no le importa ni su integridad física. Por supuesto, los estados emocionales no son un absoluto. Uno no se siente enfadado en general, lo está con algo. Los estados emocionales por tanto deberán ir asociados a los elementos percibidos en el mapa.

Y no basta con trabajar con los personajes que estan siendo percibiendo en un momento dado. Es necesario mantener algún tipo de memoria del pasado, para que aunque pierda a un enemigo de vista el personaje recuerde que debe seguir tratando de acabar con él. Así, los elementos emocionales, con sus estados asociados, perdurarán en el tiempo. Si no se refrescan siendo percibidos directamente, una rutina especial se encargará de afectar a los estados emocionales hasta que acaben olvidando haber percibido el elemento. Esto servirá para que pasado cierto tiempo de ver al jugador, si no le ven de nuevo, dejen de perseguirle.

Ya sólo falta alguna manera de conectar estados emocionales a comportamientos. Eso será fácil con una máquina de estados controlada por los valores emocionales. Según ciertos rangos de valores, el personaje pasará de un estado a otro. Habrá que incorporar cierta histéresis, de modo que si pasamos a un estado (atacando) porque la hostilidad es 20, no volvamos al estado anterior en cuanto la hostilidad descienda a 19, sino que esperemos a un valor algo menor. Así evitaremos oscilaciones de comportamiento. Pero además de la máquina de estados necesitaremos un mecanismo de arbitraje, ya que tendremos varias máquinas corriendo en paralelo, evaluándo como reaccionar a todos los elementos detectados en el mundo. Hará falta un sistema por encima que mediante reglas o puntuación elija uno de los elementos, y haga que el personaje ejecute las acciones correctas para tratar con ese elemento.

Estoy empezando a pensar la lista de comportamientos (el de persecución lo pensaba hacer mediante filtros de partículas, que extiende una lista de puntitos por todo el mapa y hace que el personaje los vaya recogiendo, para hacerlo muy realista), cuando viene un diseñador y me pregunta cuándo podrá tener el sistema de alerta para probarlo.

- Pues dejame que mire, hmmm, dos semanas para el sistema de percepción, una para los bandos, tres para el mapa cognitivo, una la memoria, dos para la máquina de estados y una para integrar todo. Dos meses y medio. Eso sin contar el comportamiento de persecución, pero podrás ver que el enemigo está intentando perseguir al jugador porque aparecerá un texto encima de la cabeza.
- No lo entiendo. ¿Cómo puede tardar tanto? Parece algo sencillo.
- Si, pero no se trata de implementar simplemente lo que necesitais ahora. Voy a montar un sistema que facilitará cambios y nueva funcionalidad más adelante.
- Ya veo. ¿Y cuanto costaría hacer exactamente lo que necesitamos inmediatamente?

Pienso durante un rato. Habría que lanzar un rayo contra el jugador mientras el personaje está en el estado de patrulla. Si el rayo golpea al jugador, cambio el estado a persecucion y empiezo a ejecutar el comportamiento, mientras mantengo un contador cada vez que pierdo al jugador de vista. Si el contador llega a un límite, vuelvo a cambiar el estado, pero si tengo el jugador a tiro empiezo a atacarle.

- Una semana. Incluso daría tiempo de montar un sencillo comportamiento de persecución que se dirigiera a tu posición directamente.

El diseñador permanece en silencio durante un momento.
- Bueno, tú sabes lo que es mejor, pero dos meses y medio parece excesivo. - Se da la vuelta y vuelve a su sitio pensativo.

Y pensando también me quedo yo, cuestionando si realmente necesito toda la flexibilidad que había creído en un primer momento. Es fácil dejarse llevar por el ansia de cubrir todos los casos, de modelar las cosas siguiendo patrones de diseño y técnicas establecidas y generales. ¿Pero es esa realmente la mejor forma de desarrollar juegos? Antes de hallar una respuesta descubro que es la hora de ir a casa. Creo que lo consultaré con la almohada, y mañana veremos.


Mostrar/ocultar resto...

9 Comments:

At 18/9/06 12:20, Anonymous Anónimo said...

Uffff, leyendo esto me parece algo tan usual, y para mi es ciertamente preocupamente. He pasado por 2 empresas de videojuegos y siempre he visto el mismo problema, la gente tiene la mania de querer hacer el super sistema super flexible para algo q no sabes si vas a usar despues. Creo q hay un serio problema en la industria, hay demasiados teoricos q muchas veces se olvidan de lo q verdad necesitas y siempre plantean el sistema. Cuantas veces habre oido por parte de programadores, estoy montando el super sistema q va a hacer de todo y tal, y yo les pregunto puedo ver algo y siempre dicen es q todavia no esta listo, todavia me falta implementar tal componente :)

Yo siempre he ido por la parte mas pragmatica, hay q programar lo q de verdad vayas a usar, ademas de esta manera el disenyador tiene un feedback mas rapido y puedes probar antes si esa idea q a priori parece buena, de verdad lo es.

Mas programacion pragmatica, por favor

 
At 19/9/06 00:12, Anonymous Anónimo said...

Insisto en la frase: "ten en cuenta todo eso al escribir el código, e diseñalo de forma que puedas extender las cosas sin mucho problema, pero implementa solamente lo que sabes que necesitas ahora". Muchas veces esto se reduce a aislar esos "super-sistemas" en funciones sencillitas.

Por alguna razón me viene a la cabeza tambien la frase de Brian Reynolds (del Civi y el Rise of Nations): "tu implementacion de la IA siempre deberia ser tirar un random, ya le pondrás más cosas después."

 
At 19/9/06 09:36, Anonymous Anónimo said...

Interesante.

Bueno, si es cierto que eso ahi se puede dar, pero bueno, en otro tipo de desarrollos (por ejemplo Drivers) uno se acostumbra a meter solo lo justo e imprescindible.. y a como diria un antiguo compañero "mesurar cada punto y coma que se mete".

En este caso uno se obliga a separar mucho las cosas en modulitos pequeños facilmente desconectables y muy concisos.. y claro esta, la funcionalidad justa :)

Eso si, sin embargo perdemos mas tiempo en repaso de codigo, pruebas de multicpu y demas (por si las BSOD's) ;)

Por mi parte creo que, si bien, programar es un arte creativo y hay que dejar que el programador diseñe su codigo (al menos , en estos tipos de programacion) la decision final sobre implementacion deberia de pasar por un encargado de proyecto que documento de requisitos en mano y resultados de los brainstorms sobre caracteristicas y necesidades de el ok (no me puedo creer que yo este diciendo esto) .. eso si, esa persona ha de tener buenos conocimientos tambien.. si te ponen para eso alguien que no sabe donde tiene la mano derecha... date por jodido.

Un Saludete, Astharoth / TLOTB

 
At 19/9/06 13:23, Blogger Jordiver said...

creo haber tenido un deja vu.....

Viendo como estan llendo las cosas por aqui no se que hubiera sido mas acertado... pero vamos, el tiempo lo dirá.

El tema creo que simplemente es tener la certeza de que realmente necesitas el supersistema complejo o por otro lado, si no lo tienes claro pues te dedicas a prototipar pequeñas funciones como decis y probar. Como diseñador digo lo que siempre, el programador sabe mas que yo, asi que decida el.

 
At 20/9/06 00:00, Anonymous Anónimo said...

Aunque estoy totalmente de acuerdo con el post, quiero destacar que un diseño e implementacion ajustados a un objetivo claro y bien definido no implica un codigo guarrillo y que funcione. El termino "que funcione" muchas veces es demasiado peligroso y se confunde con lo contrario a overengineering.

Creo que mucho de lo que ahora conocemos como industria de videojuegos dentro de unos años madurara de tal manera que nos hara gracia como se hacen los desarrollos actuales. Gran parte de la lucha contra los reiventos de rueda pasa por diseños robustos frente a la evolucion del tiempo.

Al fin y al cabo, el que hace un juego, quiere hacer el juego, no perder el tiempo en florituras.

Mi punto de discordia. ;)

 
At 20/9/06 00:30, Anonymous Anónimo said...

"tu implementacion de la IA siempre deberia ser tirar un random, ya le pondrás más cosas después."

AMEN!!! Ésta es mi forma de pensar y programar (aunque no de diseñar). Lo primero es tener ALGO funcionando lo antes posible, luego ya podemos complicarlo todo lo que queramos utilizando la primera implementación como referencia.

 
At 20/9/06 00:51, Anonymous Anónimo said...

Siempre nos cuesta mirar más allá, y nos cuesta todavía mucho más diseñar y encima acertar...

Realmente la situación ideal sería la de no tener que plantearse la cuestión de si implementar el sistema que cubra todos los casos o si implementar sólo lo que sabes que necesitas ahora...aunque en realidad ciertamente esa es la duda que siempre se plantea.

A donde deberiamos tender es a saber lo que vamos a necesitar en el sistema de IA en la totalidad del juego y a hacerlo de un tirón. ¿Cómo? Ahí justamente está el problema,..En todos los desarrollos que he vivido nadie ha sido capaz de llegar a "visionar" realmente las necesidades reales y precisas del sistema que se va a crear...Es así de triste y de real.
Es curioso, pero tenía un compañero tan sumamente preciso, concreto y con tal capacidad de análisis que decía que en una vida de un designer sólo te daba tiempo diseñar (y DISEÑAR bien) únicamente 3 juegos. Curioso pensé en su día, pero cada día que pasa creo que tenía bastante razón. En cualquier caso es una situación inviable e irreal: seguiremos planteandonos la cuestión que propone sgarces (maravilloso post por otra parte)

 
At 20/9/06 13:50, Anonymous Anónimo said...

Totalmente de acuerdo en tu comentario diegix. El diseño cerrado desde el primer momento es utópico. Pero eso no significa que constamente durante toda la vida de producción del juego se estén introducción cambios en conceptos básicos de jugabilidad o IA. En mi opinión hay que enfocar más las fuerzas al prototyping... Es una fase en la que no se invierte la atención, esfuerzo y tiempo que realmente necesita, y es en realidad el core del gameplay del juego.
No estaria mal un post sobre el arte prototipar distintas facetas de la IA o prototipar mecanismos de gameplay. Animo a que alguien abra alguno al respecto.

 
At 22/9/06 22:28, Blogger Isilion said...

Leyendo por ahí me he encontrado que otra forma de llamar a la sobreingeniería es "ingenieritis" :P

 

Publicar un comentario

<< Home