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...

15 Comments:

At 18/9/06 12:20, Anonymous garrofi 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 18/9/06 13:24, Blogger Saül said...

"¿Es esta realmente la mejor forma de desarrollar juegos?" Yo creo que no (y deduzco que tu tampoco). 1 voto por el pragmatismo que cita garrofi.

En Novarama ha sucedido algunas veces que los programadores, llevados por un exceso de entusiasmo y/o por falta/exceso de especificaciones en la documentación de diseño, han programado más de lo que toca "enredando" el desarrollo de esa parte y la posterior implementación.

Por suerte, también en Novarama, la comunicación entre equipos es excelente y, cada vez más, tendemos al estilo de programación (¡y diseño!) pragmático. Cuando algo se considera que no está suficientemente bien especificado en la documentación se consulta a diseño para que se corrija esa falta/exceso de información para pensar y discutir sobre ello.

El "Bueno, tú sabes lo que es mejor" puede suponer horas de trabajo mal gastadas tanto del programador, que verá que los diseñadores no utilizan algunas de sus implementaciones, como del diseñador, que se deberá pelear con opciones que no necesita o no entiende bien para qué sirven.

¡Pragmatismo y comunicación!

 
At 18/9/06 15:28, Blogger Diegix said...

La sobreingeniera es un tema siempre controvertido. Es como la 2, todo el mundo dice que la ve, pero tiene los peores indices de audiencia. En todas las discusiones sobre este tema de las que he sido testigo nunca he encontrado a nadie que la defienda, sin embargo todos sabemos que está presente en muchas partes y nos come como una plaga hasta el punto de destruir por dentro muchos proyectos de igual forma que alien el octavo pasajero. Todos tenemos claro que es "evil", sin embargo ¿por que se sigue haciendo?. Lo peor de todo es que seguro que a los sobreingenieros les molesta cuando luego tienen que cambiar su mapa cognitivo por una lista de ifs cuando los requirimientos hagan que el mapa cognitivo no pueda resolverlos (no creo que haya sistema capaz de absorber todos los posibles cambios) o cuando el diseñador no emplea toda la "potencia" que le pone a su alcance, pero ¿quien se la ha pedido?
En cualquier caso, también tenemos el caso contrario del diseñador que al cabo de una semana de tener el comportamiento de perseguir quiere que los enemigos oigan, y eso requiere refactorización del código, implementación de nuevas rutinas de generación de sonidos lógicos, interfaces con sonido, etc... y se molestan de que no vaya a estar disponible para mañana.
Desde mi punto de vista, si viera la tele diría que veo la 2, siempre he odiado la sobreingeniera, y creo que la mejor solución es hacer el código minimo necesario para implementar la funcionalidad que se necesita en este momento, pero teniendo en cuenta lo que se puede necesitar en el futuro para hacerlo más o menos flexible, para eso hay que tener un plan/documento de diseño con algo más de lo que se quiere hoy, que aunque sorprenda hay sitios donde no lo hay...
Todas las nuevas tendencias de desarrollo agil, scrum, TDD apuntan en esa dirección, implementar sólo lo que se necesita ahora.

 
At 19/9/06 00:12, Anonymous Jare 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 el nano 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 ent 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 imagame 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 10:24, Blogger Diegix said...

Desde mi punto de vista el tener un diseño del juego cerrado desde el primer momento es una utopía. El objetivo de un juego es que sea divertido y lamentablemente es dificil, si no imposible, saber si algo será divertido hasta que lo pruebas. En la conferencia del Civ IV de esta GDC el lead designer decia algo así como "Start playing as soon as possible and as much as possible" queriendo decir que cuanto antes puedas probar el juego antes podras cambiar las cosas no divertidas y evitar perder el tiempo en ellas, o probar 30 cosas distintas que cuestan 1h cada una en lugar de 2 que cuestan 15h cada una.

 
At 20/9/06 13:50, Anonymous imagame 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 20/9/06 20:07, Blogger JuanD said...

*suspiro*

Pese a que estemos hablando de sobreingeniería, yo me quedo con el tema original de Sergio: comportamientos / percepción. Creo que es un ejemplo cojonudo. ¿Por qué?

Porque si está mal / incompletamente especificado, el diseñador tostará a programación con cincuenta mil cambios, muchas veces contradictorios, a lo largo del desarrollo del proyecto. Así que cualquier programador curtido tenderá a prever cambios: generalizar los estímulos y respuestas.

La única solución es tener una mejor especificación y, por desgracia, ésta es una de las partes más difíciles de especificar a priori (hasta que no tienes el juego más o menos funcionando, no puedes estar seguro de qué comportamiento / reacciones necesitas).

Así que voy a llevar la contraria un poco: en mi experiencia lo ideal es tener un crack de programador y que me haga un poquito de sobreingeneria. Y cuanto antes en el ciclo de desarrollo tratar de cerrar este tema (porque un problema de los cambios en este sistema es que tiende a descuageringar el contenido / misiones / etc).

Sergio me valdría.

Probablemente ;-P

 
At 20/9/06 20:12, Blogger JuanD said...

¡Ah! Por cierto, gracias por introducirme un concepto nuevo: histéresis. Antes no sabía yo cómo llamar a esto. Es un concepto que cualquier diseñador o programador debería conocer (¡y tener muy en cuenta!).

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

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

 
At 23/9/06 19:08, Blogger Saül said...

Artículo bastante relacionado en gamasutra: http://gamasutra.com/features/20060921/campbell_01.shtml

 

Publicar un comentario en la entrada

Links to this post:

Crear un enlace

<< Home