Google
 

miércoles, diciembre 13, 2006

La inutilidad de hacer un seleccionar todo + borrar

Lastima que no tuviera una cámara de fotos porque lo que he visto pasará a los anales de la historia. ¿Que necesitas borrar todos los emails de lista? Pues no problemo pones un peso encima de la tecla suprimir y santas pascuas.

Y sí, anales de la historia está escrito con doble intención.

C++: Namespaces sin nombre

Supongo que todo el mundo conoce para que sirve el namespace de C++. Lo que ya no es tan conocido es que se pueden crear espacios de nombres sin tener que asignarle un nombre, será entonces el compilador el que se encargue de darle un nombre único a voleo del que no tendremos conocimiento. ¿Y esto para que sirve? Pues los dos usos principales son:

1º Para poder abrir un espacio de nombres de forma local en lugar de en todo el archivo entero:
namespace MiSuperLibreria {
  class Torcuato {
    // …
  };
};

namespace {
  using namespace MiSuperLibreria;

  Torcuto variable1;
};

MiSuperLibreria::Torcuato variable2;
Torcuato variable3; // Aquí dará error el compilador
2º Uno de los usos de static es el indicar que una variable únicamente ha de poder verse en el archivo en el que se ha declarado. Pero con la llegada de C++ la forma correcta de hacer esto es usando los espacios de nombres sin nombre:

#include <iostream>

namespace {
  int variable;
};

int main() {
  std::cout << variable << std::endl;
}

Por último tened en cuenta que el compilador C++ de Microsoft no se lleva demasiado bien con los namespace sin nombre así que si lo usáis es mejor desactivar las cabeceras precompiladas.

lunes, diciembre 11, 2006

Paradigma de la programación

El paradigma de la programación es el estilo de la programación. Define la forma en que el programador “ve” el flujo del programa. En la wikipedia lo explican bastante mejor.

Hay tantos estilos de paradigmas de programación como lenguajes de programación. Los más ampliamente usados son el modo imperativo y el modo funcional:

- El modo imperativo son secuencias de comandos que son ejecutados por la máquina de forma secuencial. Lenguajes como C++, Basic, COBOL o Pascal usan este estilo.

- En el modo funcional el programa esta únicamente constituido por definiciones de funciones. No hay secuencia de comandos lineal, el flujo de ejecución esta definido por el orden en que se llaman las funciones lo que obliga en gran medida a tirar de funciones recursivas. Hay pocos lenguajes funcionales puros como Haskell o Miranda. Normalmente suelen incorporar algo del estilo imperativo como bucles. Entre los híbridos más conocidos tenemos Lisp, Scheme o OCaml.

Si llevas toda la vida programando en modo imperativo puede ser difícil cambiar al modo funcional. Es necesario volver a aprender a programar porque es una forma totalmente diferente de pensar, pero una vez lo has logrado te das cuenta del gran potencial que posee.

Históricamente los lenguajes imperativos han tenido mayor velocidad de ejecución que los funcionales pero poco a poco esto va cambiando. Lenguajes como OCaml ayudan a poner en duda la máxima de que lenguajes como C++ son de lo rápido del mercado.

jueves, noviembre 30, 2006

El tipo de letra más feo de la historia

Hay que tener cuidado a la hora de elegir la fuente en tu editor de textos porque al fin y al cabo acabas pasando bastantes horas al día delante del monitor. Muchos opinan que Comics Sans es el tipo de fuente horrible por excelencia pero creo que Oloron debería llevarse ese premio.

¿Te imaginas tener que programar ocho horas al día viendo esto?


Y tan solo vale 19.95$, chicas corred a comprarlo.

martes, noviembre 28, 2006

Metaprogramación por medio de templates en C++

La metaprogramación es una técnica que permite programar usando trozos de código como si de datos se trataran. En lenguajes como Lisp es realmente fácil de llevar a cabo pero en C++ sólo se puede hacer mediante templates.

Podemos ver el compilador como una máquina virtual que produce código ensamblador. Esto nos permite hacer cálculos complejos en tiempo de compilación en lugar de en tiempo de ejecución. Un ejemplo muy usado es calcular un número de la sucesión de Fibonacci en tiempo de compilación:

1 #include <iostream>
2
3 template <int N> struct fib {
4 static const int result = fib<N-1>::result + fib<N-2>::result;
5 };
6
7 template <>
8 struct fib<0> {
9 static const int result = 0;
10 };
11
12 template <>
13 struct fib<1> {
14 static const int result = 1;
15 };
16
17 int main() {
18 std::cout << "Fib(15) = " << fib<15>::result << std::endl;
19 return 0;
20 }

En la wikipedia podemos encontrar una descripción un poco más extensa.

jueves, noviembre 23, 2006

Rey de entre los reyes

Si sois aficionados a los TOP 10 de los mejores juegos de la historia os habréis dado cuenta de dos cosas. Los juegos de PC no suelen aparecer y el primer puesto se lo dan siempre, y digo siempre, a “The Legend of Zelda: The Ocarina of Time”.

Puede parecer que un juego en formato cartucho para la Nintendo 64, consola que tuvo escaso éxito comercial, no os parezca que pueda llegar a ser lo mejor de lo mejor. Pero hay un hecho innegable y es que tanto la N64, como “The Legend of Zelda: The Ocarina of Time” y su creador Shigeru Miyamoto marcaron un hito en la historia.

Shigeru Miyamoto, japonés creador de Mario, tiene entre otros muchos honores, el ser el creador del primer juego realmente 3D: Super Mario 64. Inventó un completo sistema de cámaras que fue copiado por multitud de compañias. Es un personaje de referencia en este mundillo, uno de los grandes pesos pesados.

Si os interesa aprender más sobre la historia de los videojuegos no dejéis de leer su biografía, sobre The Legend of Zelda: The Ocarina of Time ni de la Nintendo 64. Y por ultimo, bajad el DVD con la retrospectiva de Zelda que ha publicado Nintendo para celebrar su reciente Wii.

miércoles, noviembre 22, 2006

Calculadora científica de Windows

¿Cómo puede ser que la misma calculadora en modo estándard tenga un botón para calcular raíces cuadradas...?


¿...y en modo científico no?


Habrá que recurrir al x^y.

jueves, noviembre 09, 2006

Guiones para juegos

Considero que el mundo del cine y del videojuego tienen bastantes parecidos. La escritura de guiones para el cine creo que es aplicable también a videojuegos, sobretodo en aventuras gráficas o cualquier otro tipo de juego que tenga una historia un poco compleja.

Cletx es un programa libre bastante conocido que se usa para escribir guiones cinematográficos. No se limita simplemente al guión sino que también se encarga de llevar un control sobre la descripción de los personajes, los lugares, objetos, etc… Vale la pena usarlo si uno piensa escribir el guión de una aventura gráfica.

martes, noviembre 07, 2006

Ogre: Compositor

Compositor es una de las nuevas características que lleva Dagon; la última línea de desarrollo de Ogre. No son más que filtros a aplicar a toda la pantalla como motion blur o cambiar los colores a blanco y negro. Es bastante sencillo de usar, sobretodo si tirais de los ejemplos que Ogre trae. Aquí tenéis algunos. Únicamente debéis añadir el archivo .compositor, los shaders a los que el compositor hace referencia (en la web no salen, están en el directorio media del SDK) y finalmente para activarlos sólo tenéis que llamar a:
Ogre::CompositorManager::getSingleton().addCompositor(viewport, "Bloom", 0);
Ogre::CompositorManager::getSingleton().setCompositorEnabled(viewport, "Bloom", true);
El directorio de ejemplos de Ogre trae uno que ayuda a ver fácilmente como queda el efecto final, pero por lo que sea se han descuidado añadirlo al archivo principal .sln del Visual Studio. Pero tranquilos ya que tiene fácil solución, con que abráis manualmente el archivo del proyecto es suficiente: C:\OgreSDK\samples\scripts\Demo_Compositor_vc8.vcproj.

lunes, noviembre 06, 2006

Muertes estúpidas o Nethack YASD

Si hay un juego que se lleva el premio a las muertes más estúpidas ese es sin lugar a dudas Nethack. A pesar de su aparente simpleza es uno de los juegos más extremadamente complejos a los que he tenido el placer de jugar. Llevo varios años jugando, jamás me he acercado ni por asomo al final y en ningún momento he pensado en dejar de jugar por ello. Aquí tenemos un ejemplo de una muerte realmente idiota, matado por un simple tritón:

miércoles, octubre 25, 2006

OCaml

OCaml es un extraño lenguaje que soporta tanto estilos de programación funcionales como imperativos u orientados a objetos.

Esta es una pequeña función de ejemplo que compara dos listas y retorna verdadero si son iguales:
1 let rec compara l1 l2 =
2 match l1, l2 with
3 | h1 :: t1, h2 :: t2 -> if h1 != h2 then false else compara t1 t2
4 | [], [] -> true
5 | _ -> false
Línea 1: Definimos la función compara con dos paremetros l1 y l2, además le indicamos que es una función recursiva (rec).
Línea 2: Match es algo parecido al switch del C pero mucho más potente, hacemos un match con las dos listas.
Línea 3: Comparamos si la cabecera de las dos listas (h1 y h2) son diferentes en cuyo caso devolvemos un falso. Si no, llamamos recursivamente la función con la cola de las dos listas (t1, t2).
Línea 4: Si las dos listas han llegado vacias hasta aquí es que son iguales.
Línea 5: Para el resto de casos (el tamaño de las listas es diferente) retornamos falso.

Espero que os haya entrado el gusanillo de mirar más cosas sobre OCaml.

lunes, octubre 23, 2006

2D vs 3D

Hay un interesante Top 10 del 2005 en la web de Game Tunnel (web sobre juegos indie). Lo que más me ha llamado la atención es que de los diez mejores juegos del año (según ellos claro) únicamente dos son 3D.

Creo que muchas veces la fiebre por lo 3D ciega a los diseñadores. Parece que sea obligado el hacerlo todo tridimensional. Hay muchas tipos de juegos que si se versionaran en 3D perderían mucha gracia. De igual forma, hay muchas juegos 3D que sería impensable pasarlos a 2D.

Hay que escoger lo mejor para la mecánica del juego y no basarse únicamente en la estética. Otra cosa a tener en cuenta es que bastante más facil el desarrollo de un juego 2D que uno 3D (salvo excepciones claro).

viernes, octubre 20, 2006

'Radiosity' en tiempo real

Es impresionante ver como han conseguido renderizar una escena calculando la luz en tiempo real. Ha sido posible gracias a siete años de investigaciones según dicen en la web. Hay disponible una pequeña entrevista que nos ofrece más detalles de esta pequeña maravilla tecnológica.

jueves, octubre 19, 2006

Cuidao que me espiño

Bil Gates no inventó internet ni Valve, con su Half-Life 2, la física en los juegos. Fun-motion nos trae una interesante lista de juegos en los que la física juega un papel importante. Crear corrientes de agua, construir puentes o apilar objetos son algunas de las cosas que tendrás que hacer. Para que luego digan que la originalidad en los videojuegos ha muerto.

miércoles, octubre 18, 2006

IA Básico: Ejemplo

He subido el código fuente de un ejemplo que resuelve un pequeño juego usando la técnica más básica.

Seguro que todos conoceréis el juego; en un tablero de tres por tres casillas tenemos desordenados ocho cubos numerados del 1 al 8, el objetivo consiste en ordenarlos de una forma predeterminada (la que más os guste) arrastrando un cubo al hueco que queda, pero ojo, únicamente podemos mover los cubos contiguos al agujero.

El funcionamiento del programa no debería daros demasiado problema. Lo único que hace es probar todas las combinaciones posibles y escoger la ganadora. Lo que realmente ocurre es que mediante algún algoritmo (Breadth First o Depth First) elegimos en el grafo (todos los movimientos posibles) un camino que nos lleve a la victoria.

Es un método bastante simple pero sólo sirve para usarlos en juegos en los que participe un único jugador, que tengamos toda la información del juego en todo momento y que el número de movimientos sea mínimo pues consume demasiada memoria y CPU.

Los Videojuegos en los hogares españoles

aDeSe ha publicado recientemente un estudio sobre los videojuegos en los hogares españoles. Nos ofrecen algunas conclusiones interesantes como que España representa el 2,6% del mercado mundial de videojuegos, que la gente no considera piratear un videojuego como delito con lo que la recuperar la inversión inicial no es fácil debido a que es la principal forma de acceso a los videojuegos o que, aproximadamente, el 5% de los juegos comercializados representan el 90% de las ventas. Es altamente recomendable echarle una ojeada al resumen del informe.

martes, octubre 17, 2006

I.A. Básica

La forma más básica de usar la inteligencia artificial en un juego se convierte en algo tan sencillo como probar todas las posibilidades y escoger la que te lleve al premio gordo. Es fácil de efectuar con una lista. Hay tres algoritmos básicos que te pueden ayudar:
- Breadth First
- Depth First
- A*
La diferencia entre esos algoritmos es mínima, únicamente cambia la posición en la que añadimos y quitamos cosas de la lista. El pseudo-código estándar podria ser así:
hacer
estado_actual = quitar_estado_de_la_lista
si estado_actual contiene la solucíon entonces final_de_juego
array_de_estados_hijos = expandir(estado_actual)
añadir_a_la_lista(array_de_estados_hijos)
mientras no_acabe_el_juego
La diferencia entre los diferentes algoritmos es:
- Breadth First: los elementos se añaden siempre en una cara de la lista y se quitan de la cara opuesta.

- Depth First: los elementos se añaden y se quitan de la misma cara de la lista.

- A*: los elementos se añaden a cualquier cara de la lista pero se eliminan primero los mejor puntuados según una función heurística (podríamos puntuar, por ejemplo, los que estén más cerca del objetivo). Reduce enormemente el número de elementos pero a base de usar un algoritmo más lento.

lunes, octubre 09, 2006

Como borrar elementos de una std::list

Borrar un elemento de una lista STL no es tan fácil como parece pues si hacemos:
1 for(std::list<int>::iterator i = lista.begin(); i != lista.end(); i++)
2 if((*i) < 0)
3 lista.erase(i);
Tendremos una bonita lista corrupta, pues no tenemos en cuenta que los elementos anterior y posterior del que estemos borrando continúan apuntando al elemento borrado. La función erase realmente arregla esos punteros anterior y posterior, pero el iterador que estamos usando actualmente mantiene guardados en memoria los punteros anteriores al borrado. La solución es mover el iterador antes de borrar:
 1 std::list<int>::iterator i = lista.begin();
2 while(i != lista.end())
3 if((*i) < 0) {
4 std::list<int>::iterator j = i;
5 i++; // Movemos antes de borrar nada
6 lista.erase(j); // Aqui borramos el elemento
7 } else
8 i++;
Otra opción es usar la función remove_if pues ya tiene todo esto en cuenta. Tened cuidado porque hay dos funciones remove_if; la del algoritmo general y una que pertenece a la clase std::list que es mucho más óptima que la general.

Actualización:
AK47 me comenta que la función erase ya se encarga de devolver un iterador válido. La función correcta quedaría entonces así:
1 std::list<int>::iterator it = lista.begin();
2 while(it != lista.end()) {
3 if(*it > 0)
4 it = lista.erase(it);
5 else
6 it++;
7 }

miércoles, octubre 04, 2006

C/C++: * vs /     Fight! And the winner is...

Existe una leyenda urbana programaril que dice que multiplicar floats es más rápido que dividirlos. Seguro que en más de una ocasión todos habremos visto cosas como:
float l = 1.0f / length();
x *= l;
y *= l;
z *= l;
He querido ponerlo a prueba pues siempre he pensado que aunque fuese cierto el compilador se encargaría siempre de optimizar o que la diferencía sería realmente mínima. Así que preparé dos programas:
div.c

1 #include <stdio.h>
2
3 int main() {
4 int i;
5 for(i = 0; i < 1000000; i++) {
6 float i = 123.4f;
7 float j = 12.3f;
8 float k = i / k;
9 printf("%f\t", k);
10 }
11
12 return 0;
13 }
14
mult.c

1 #include <stdio.h>
2
3 int main() {
4 int i;
5 for(i = 0; i < 1000000; i++) {
6 float i = 123.4f;
7 float j = 12.3f;
8 float k = i * k;
9 printf("%f\t", k);
10 }
11
12 return 0;
13 }
14
Los dos están compilados con gcc programa.c -O2. Pongámoslos a prueba:
# time ./div > /dev/null
real 0m26.532s
user 0m26.061s
sys 0m0.374s

# time ./mult > /dev/null
real 0m1.218s
user 0m1.171s
sys 0m0.030s
Todas mis ilusiones puestas en que era realmente una leyenda urbana y vaya si que hay diferencía. Multiplicar es unas 20 veces más rápido que dividir.

Making off:
El printf en realidad es un poco chorra pero -O2 hace demasiado bien su trabajo y se da cuenta de que el bucle en realidad no sirve para nada más que para perder tiempo asi que lo elimina. De ahi a incluir el printf y redirigirlo luego a ninguna parte (/dev/null).

Visual Studio: Find and Replace

Las expresiones regulares integradas en el editor es una de esas características que siempre me preguntaba porque no las incluían en los editores de programación para Windows. Por suerte cuando Microsoft presentó los nuevos Visual Studio 2003 & 2005 las incluyó (aunque estaría mejor dicho las copió). Os presento un ejemplo rápido:
Imaginad que incluís en vuestro programa la función TT que coge un string y lo retorna en el idioma del sistema. Vemos entonces nuestra vieja función SetTextTraducido(CajaDeTexto, "Texto") se ha quedado obsoleta y debéis reemplazarla por la nueva SetText(CajaDeTexto, TT("Texto")). No podéis hacer un reemplazar texto de los de toda la vida pues el control CajaDeTexto va variando. Solución, activáis la opción de Find and Replace del Visual Studio, activáis las expresiones regulares y escribís:
Find what: SetTextTraducido\({.*}, \"{.*}\"\)
Replace with: SetText(\1, TT("\2"))
Lo que poneis entre las llaves { y } se guarda en un grupo que luego en el recuadro de reemplazar podeis cogerla llamando \1, \2, \x... donde x es el orden de aparición del grupo en la cadena de búsqueda.

lunes, octubre 02, 2006

Dibujando (literalmente) en 3d

¿Qué os parecería poder hacer modelos 3d haciendo un simple dibujo 2d? Lanzar cuatro líneas para dibujar un perro y que el programa automáticamente te creara un modelo 3d del boceto.

Pues ahora es posible a SmoothTeddy. Al principio cuesta un poco hacerse con el programa (no se parece a nada que hayas usado antes) pero cuando lo consigues convierte la tarea de crear modelos 3d en juego de niños.

No dejeis de ver el video ni de echarle un vistazo al paper. Simplemente genial.

viernes, septiembre 29, 2006

Pantallas de bienvenida

¿Os habéis fijado en que los programas usan cada vez menos esas odiosas pantallas de bienvenida (splash screen para los amigos)? Las únicas razones que veo para usarlas son:

- El programa tarda diez años en cargar y debes avisar al usuario de que tu programa no se ha muerto.

- Te fastidia sobremanera de que el usuario pueda hacer algo con el ordenador mientras tu programa carga (¡¡Te ha costado mucho hacerlo!!) así que has pensado en ponerle un parche encima de la pantalla.

- Como sabes que en el fondo la publicidad subliminal no funciona has decidido ponerle un cartelito al ordenador cada vez que arranca el programa.

- Como todos los programas las usan pues yo también las utilizo. Total, el ser humano no es más que un mono, para que pensar cuando puedes imitar.

- Has creído que sería buena idea porque si el programa se bloquea la ventana se quedará en pantalla hasta la eternidad.

- Quieres chulear ante los amigos que tu también sabes crear ventanas en modo top-most.

- Has hecho una apuesta en Internet sobre que tú puedes hacer una pantalla de bienvenida que dure más que la del Acrobat Reader sin morir en el intento (lo veo bastante difícil la verdad, corren rumores de que Nerón aún esta esperando que acabe de cargar para leer el periódico en PDF).

- Tu programa es bastante pequeño con lo que carga de forma casi instantánea, así que has decidido ponerle un pausa de diez segundos para que la gente pueda contemplar lo bien que se te da usar el diseño gráfico.

- Te levantaste con el pie izquierdo ésta mañana y pensaste "jodeos".

- Viste el asistente y te sentiste obligado a usarlo. No es cuestión ir discriminando opciones del programa no sea que luego se enfaden contigo y no quieran funcionar.

- Tu jefe (sí, ese mismo que te dijo que usar metodologías era de nenazas) ha pensado que el cliente os encargaría más programas si le obligábamos a ver el nombre de la empresa cada vez que arranque el programa.

- En el fondo sientes nostalgia por el Microsoft Word desde que te pasaste al OpenOffice y decidiste hacerle un homenaje.

- Te ha costado mucho programar esos plugins así que el usuario debe ¡qué digo debe! está OBLIGADO, ha leerlos todos y cada uno de ellos. Además has decido ponerle esos extraños nombres para asegurarte que entienda lo complicado que ha sido desarrollarlos.

- Sé que podría optimizar la carga del programa pero realmente paso de hacerlo.

- Has creído que sería la mejor forma de provocar una muerte lenta y silenciosa a tus usuarios.

- Nadie se acuerda del nombre de tu programa.

miércoles, septiembre 27, 2006

Cómo llevar un proyecto a buen puerto

Me ha ocurrido bastantes veces que he comenzado un proyecto y lo he acabado dejando a la mitad. No es debido a que me falten grafistas o más programadores, normalmente se debe a que como empieza siendo un proyecto un poco errático pues acaba perdido del todo. Pero hay una solución idónea para solucionarlo y es tan sencillo como usar una metodología. Hay cientos de tipos diferentes pero las que más de moda están son las metodologías ágiles. En el caso de los videojuegos hay una orientada específicamente para ellos: Scrum [GameDev] [Wikipedia].

Cómo llevar un proyecto a buen puerto

Me ha ocurrido bastantes veces que he comenzado un proyecto y lo he acabado dejando a la mitad. No es debido a que me falten grafistas o más programadores, normalmente se debe a que como empieza siendo un proyecto un poco errático pues acaba perdido del todo. Pero hay una solución idónea para solucionarlo y es tan sencillo como usar una metodología. Hay cientos de tipos diferentes pero las que más de moda están son las metodologías ágiles. En el caso de los videojuegos hay una orientada específicamente para ellos: Scrum [GameDev] [Wikipedia].

viernes, septiembre 22, 2006

To kill or not to kill

A veces usar algunas librerías es algo engorroso porque no está claro del todo quien es el encargado de destruir los objetos si tu o la librería. Así que al final queda un centenar de memory leaks por objetos que nadie ha borrado.

Según mi punto de vista quien lo crea lo borra. Si eres tu el que hace el new debes ser el encargado de borrar el objeto. Si en cambio es la librería la que crea el objeto (llamando por ejemplo una función CreaObjeto que retorna una instancia de un objeto) debe ser la librería la que lo borre.

miércoles, septiembre 20, 2006

Cuán equivocado estaba

Sabía que los bancos aún usaban COBOL en gran medida pero jamás me imaginé que tanto. Copio y pego de la wikipedia:
“Pese a que muchas personas creen que el lenguaje COBOL está en desuso, la realidad es que casi todos los sistemas que requieren gran capacidad de procesado batch, tanto de los bancos como en otras grandes empresas con sistemas mainframes, utilizan COBOL. Esto permite garantizar la compatibilidad de los sistemas antiguos con los más modernos, así como tener la seguridad de que el lenguaje es perfectamente estable y probado. Según un informe de Gartner Group de 2005, el 75% de los datos generados por negocios son procesados por programas creados en COBOL, y en otro informe de 1997 estima que el 80% de los 300.000 millones de líneas de código existentes están creados en COBOL, escribiéndose 5.000 millones de líneas nuevas de COBOL cada año. Con todo eso, hoy por hoy, la programación en COBOL es uno de los negocios más rentables del mundo de la informática.”
El 80% del código fuente del mundo mundial está escrito en Cobol. Creo que suena a negocio lucrativo. Tendré que comenzar a estudiarlo.

lunes, septiembre 18, 2006

Ogre3d 1.2.3 review

Me gustaría ofrecer una pequeña reseña aprovechando que ha aparecido una nueva versión de este motor gráfico.

Ogre3d es sin duda un conocido por todos los que se mueven en la programación 3d y si no… ¡A que esperáis! Se ha convertido ya en casi imprescindible. Si no existiera habría que inventarlo.

Es un motor de licencia libre, la LGPL concretamente. Esto quiere decir que podemos usarlos en proyectos comerciales, siempre y cuando lo usemos en formato de librería dinámica (DLL para los windowseros). Si hacemos uso de un linkaje estático deberemos liberarlo, obligatoriamente, bajo licencia LGPL.

A pesar que hay muchas librerías aparte de la principal, Ogre3d es únicamente un motor gráfico. Es decir, que no trae soporte para sonido, ni físicas ni ninguna otra cosa que se salga de mostrar gráficos en pantalla. Hay proyectos (OgreNewt, OgreODE, etc…) que están alojados en la misma página de Ogre3d pero deberéis bajároslos vosotros mismos del servidor CVS y compilároslo.

Como grandes bazas tenemos una gran calidad de render y un API que es de lo mejorcito que vais a ver en software libre. Si a eso le sumamos herramientas como oFusion que hacen que la exportación en 3ds max sea una autentica maravilla, tenemos como resultado un motor muy a tomarse en cuenta a la hora de desarrollar algún juego.

Como pegas hay el que no tenga los binarios ya compilados para los proyectos paralelos y que el sistema de organizar recursos no me acaba de gustar del todo (pero esto va según gustos de cada uno). También muchas veces hay que seguir la filosofía del guisárselo uno mismo, cosa que en la mayoría de motores comerciales no pasa. De todas formas cualquier pega que pueda tener deja de tener importancia gracias a la gran labor que hace la gente del foro.

viernes, septiembre 15, 2006

Quest3D 3.5 review

Quest3D es un motor para videojuegos bastante completo con la peculiaridad de que el lenguaje usado es un lenguaje gráfico basado en canales. Un canal es una caja con varias entradas y una salida, como si de una función en C se tratara. Para programar no hay más que ir uniendo los diferentes canales y definir sus propiedades. Al principio costó acostumbrarme, pues no se parecía a nada de lo que había usado antes, pero contra más lo usas más te va maravillando su sencillez para hacer cosas complicadas. Realmente vale la pena de aprender.

Por calidad de gráficos uno no se puede quejar ya que tiene todo lo que un gran motor gráfico ha de poseer, desde algo tan básico como pixels shaders hasta HDR. Usa el motor de físicas ODE y un sencillo motor de sonido 3d (tal vez su punto más flojo). Simulación de agua, ropas, plugin para la web, lightmaps, LUA, etc...

Como queja tenemos el precio, su licencia básica es de 999€ aunque se puede adquirir por 199$ aunque con bastantes limitaciones. El exportador para 3ds max se podría mejorar pero cumple su función perfectamente. Como dije antes el motor de sonido adolece de ser demasiado simple pero uno siempre puede agregar funcionalidades al lenguaje y crear un wrapper para OpenAL usando el SDK para C++.

Os podéis bajar la demo pero si lo que os interesa es ver como funciona os podeis bajar unos video tutoriales o incluso unos videos de demostración.

Solo añadir que es el motor que se está usando para el remake de la Abadia del Crimen.


martes, septiembre 12, 2006

Juegos originales

Recientemente me han llamado la atención algunos juegos por su originalidad, algo que, por desgracia, no está demasiado en boga últimamente.

- Rumble Box: todos los personajes están formados por cubos y esferas que se quedan por ahí una vez derrotados.

- Darwinia: me ha llamado mucho la atención la estética de los gráficos que me recuerda enormemente a la película Tron. La mecánica también es bastante original.

- Goblin Hack: estoy acostumbrado a juegos en modo texto (Nethack, DoomRL, etc...) pero nunca había visto llevar los gráficos ASCII hasta este punto.

NetBeans C/C++

Al igual que Eclipse, NetBeans también tiene un plugin para añadir soporte C/C++ al IDE. Aunque aún le faltan algunas cosas parece que tiene un futuro prometedor.

lunes, septiembre 11, 2006

¿Porqué las macros de C apestan?

Imaginad que queremos hacer una macro que nos devuelva el cuadrado de un número. En un primer intento escribiremos:
#define cuadrado(x) x*x
Pero sin darnos cuenta hemos caído en una trampa terrible. En un primer intento nos damos cuenta que 2/cuadrado(10) no funciona porque se expande a 2/10*10. Para intentar solucionarlo cambiamos la macro a:
#define cuadrado(x) (x*x)
Acto seguido vemos como no funciona al intentar hacer cuadrado(1+1) porque se expande a (1+1*1+1). Así que intentamos volver a arreglar la macro:
#define cuadrado(x) ((x)*(x))
Pero así y todo nos sigue sin funcionar de forma correcta al hacer:
int x = 2;
cuadrado(x++)
Porque se expande a ((x++)*(x++)). Finalmente intentamos usar otra solución:
int temp_var;
#define cuadrado(x) (temp_var=(x); temp_var*temp_var)
Pero sólo nos funciona con números enteros así que nunca más podrás hacer cuadrado(3.5). Para conseguir una versión de la macro que nos funcione de forma correcta deberemos tirar de extensiones del lenguaje no estándar:
#define cuadrado(x) ({typedef xtype = x; xtype xval = x; xval * xval; })
Una simple macro que en un principio parecía que no sería gran cosa nos ha obligado a usar extensiones del lenguaje que no nos ofrecen ninguna garantía de continuidad ya que no son parte del estándar. Pero es que el sistema de macros de C no acaba aquí, programar una macro que haga algo tan simple como:
strswitch(exp) {
case “hola”: llamar_hola(); break;
case “adios”; llamar_adios(); break;
default: llamar_pordefecto(); break;
}
Es literalmente imposible de llevar a cabo en C.

[Basado en http://lists.warhead.org.uk/pipermail/iwe/2005-July/000130.html]

jueves, septiembre 07, 2006

Tengo la idea... ¿y ahora qué?

Bien, tenemos una idea genial para un juego. Hemos escrito el guión, tenemos un breve diseño de lo que será juego pero ahora nos falta llevarlo a cabo.

Nos hace algún motor para juegos, no nos interesa programarlo nosotros mismos pues eso nos conllevaría un retraso de dos o tres años. Necesitamos echarle mano a uno de esos motores tan geniales que hay pululando por el mercado y por la red.

Hay motores que son únicamente 3d como Ogre, Irrlicht o Crystal Space. Pueden sernos de gran ayuda (no tendremos que programarlo todo de cero) pero si queréis realmente llevar a cabo un juego necesitareis más cosas como físicas, sonido, scripting… Deberemos usar un motor completo, y aunque la mayoría son de pago hay uno que recientemente me ha llamado la atención y esta bajo licencia LGPL Delta3d. Si en cambio queréis usar motores comerciales los más usados del mercado de juegos indi son el Torque Game Engine y TV3D.

Para una lista mucho más completa de estos motores pasaros por la web de devmaster.

lunes, septiembre 04, 2006

¿Porqué C++?

¿Porqué C++ es el lenguaje estrella a la hora de desarrollar juegos? Es anticuado, es a muy bajo nivel, carece de características avanzadas como funciones anónimas, su sistema de macros no pasa de ser un reemplazar texto pero aún así es el lenguaje estrella.

En el resto de aplicaciones comerciales, si excluimos los juegos, el papel de C++ ha ido cayendo en pro de lenguajes de la familia .Net o Java. Pero en el mundo de los juegos C++ sigue siendo imposible de vencer.

Mi opinión personal es que no es porque sea el lenguaje idoneo para desarrollar juegos o que sea el más rápido (excluyendo ensamblador). Es simplemente que para la mayoría de desarrolladores de videojuegos es el lenguaje con el que está más familiarizado y para que el que existen la mayoría de librerias (DirectX, OpenGL, Ogre, etc...).

Los desarrolladores se dan cuenta de esto e intentan evitar C++ cada vez más. Por ejemplo ya no se crea la lógica del juego directamente en C++, se suele incluir un lenguaje script para ello pues son conscientes de que C++ se queda corto. E incluso las herramientas como editores de niveles usan cada vez más C#.

No quiero decir ni mucho menos que C++ sea malo pero que a lo mejor, tal vez, deberíamos ser más conscientes a la hora de elegir el lenguaje de programación.

jueves, agosto 31, 2006

Microsoft XNA

¿Qué es Microsoft XNA?
Es un conjunto de utilidades y frameworks que permiten desarrollar fácilmente juegos en C# para las plataformas Windows XP, Windows Vista y XBox 360. Hace uso de la versión 2.0 del Microsoft .Net.

¿Puedo vender juegos hechos con XNA?
Con la versión express en las plataformas Windows sí pero en la XBox 360 sólo se pueden hacer juegos no comerciales.

¿Dónde me lo descargo?
La versión express se puede descargar de aquí. Es obligatorio tener instalado el Visual C# 2005 Express ya que la versión completa del Visual Studio no sirve.

Si está basado en C# será lento.
Mucha gente piensa erróneamente que C# es un lenguaje interpretado. C# usa la tecnología JIT por lo que es compilado antes de ejecutarse. Esto ayuda a aprovechar el máximo las características de la máquina.

martes, agosto 29, 2006

Declaración de derechos de los programadores

Traducción libre de Coding Horror.

1. Todo programador debería tener dos monitores

Con los precios actuales de las pantallas LCD y el soporte dual de las nuevas tarjetas gráficas, es una locura limitar a los programadores a un solo monitor. Los beneficios de productividad al doblar el escritorio están bien documentados. Si esperas maximizar la productividad de tu equipo de desarrollo, asegúrate que tus programadores tienen dos monitores.

2. Todo programador debería tener un PC rápido
Los programadores necesitan ejecutar una gran cantidad de programas para hacer su trabajo: entornos de desarrollo, motores de bases de datos, servidores web, maquinas virtuales, etc... Ejecutar todo ese software requiere un PC rápido con grandes cantidades de memoria. Contra más rápido sea el PC más rápidos serán los ciclos de depurar y compilar. Es absurdo pagar precios abusivos para llegar a tener la máxima potencia – pero asegúrate siempre de comprar lo más parecido. Equipa tus programadores con PCs rápidos y mucha memoria. El tiempo gastado contemplando como se mueve la barra de progreso es tiempo perdido.

3. Todo programador debería poder escoger su ratón y teclado
En la universidad tenía un negocio de pinturas. Cada pintor que contrataba tenía que comprar sus propios cepillos. Es lo primero que aprendí. Obligar a usar los cepillos estándar a los nuevos pintores no funcionaba. Los cepillos comunes se estropeaban y descuidaban rápidamente. Pero los pintores que compraban sus propios cepillos se hacían cargo de ellos. Los pintores que compraban sus propios cepillos apreciaban la diferencia entre el cepillo profesional de 20$ que poseían y el cepillo de a un dólar. Tener sus propios cepillos les hacia adquirir el sentido de responsabilidad. Los programadores deberían tener la misma relación con su ratón y teclado – son herramientas esenciales que usamos a diario para practicar nuestro arte y deberían ser tratados como tal.

4. Todo programador debería tener una silla cómoda
Afrontémoslo. Pasamos mucho tiempo de nuestra vida sentados sobre nuestros traseros ocho horas diarias. ¿Por qué no pasar esas ocho horas en una silla cómoda y bien diseñada? Dad a los programadores sillas que no solo hagan que pasen esas ocho horas de forma tolerable sino también agradable. Has contratado a tus programadores por sus cerebros pero no olvides los otros complementos.

5. Todo programador debería tener una conexión a Internet rápida
Los buenos programadores nunca escriben lo que pueden robar. Y Internet es la mejor fuente para adquirir material robado jamás inventada. Tengo todo tipo de libros pero es difícil imaginarse hacer cualquier trabajo de forma rápida sin una buena conexión a Internet.

6. Todo programador debería tener unas condiciones de trabajo tranquilas
Programar requiere concentración. Los programadores no pueden trabajar de forma efectiva en un entorno de trabajo con interrupciones constantes. Asegúrate de que el entorno de trabajo protege tu programador o perderán el tiempo con distracciones.

lunes, agosto 28, 2006

Lo que será el 3ds max 9

Autodesk ha mostrado en la última Siggraph lo que será el nuevo 3ds Max 9. Soporte para 64-bits, mayor facilidad de uso de Mental Ray y velocidad del programa optimizada son algunas de las características que tendrá esta aplicación que se ha hecho imprescindible para el mundillo de los videojuegos.

viernes, agosto 11, 2006

Sergio Leone

A veces uno se encuentra con auténticas joyas en la red. En esta ocasión es un artículo genial sobre el gran Sergio Leone. No os perdáis las localizaciones de los escenarios en España.

Igualmente os aconsejo que os hagáis con la música de Ennio Morricone, un inseparable de Sergio Leone.

martes, agosto 08, 2006

Recolector de basura para C++

¿Qué es?
Se encarga de liberar y reciclar toda la memoria que el programa ha dejado de utilizar.

Mitos
- Es más lento que la gestión manual de memoria.
- El recolector de basura causará pausas en mi programa.
- La gestón manual de memoria no crea pausas.
- Es incompatible con C y C++.

C++
Hay varios recolectores de basura para éste lenguaje. Recomiendo que useis libgc o su versión en desarrollo que soporta multiples hilos de ejecución.

A diferencia de lo que se cree usar un recolector de basura hará nuestro programa más rápido, sobretodo si hacemos un uso intensivo de la memoria como en el caso de sistemas de partículas, por ejemplo.

Recomiendo usarlo en todos vuestros proyectos. Con el estandard C++98 el lenguaje ha ido evolucionando cada vez más hacia los recolectores de basura pero las herramientas como auto_ptr que trae por defecto el lenguaje no son demasiado potentes e incluso a veces resultan un poco engorrosas.

viernes, agosto 04, 2006

Las diez mejores excusas

Todo programador que se precie las ha usado alguna que otra vez. Vía Geek 24, las diez mejores excusas dichas por programadores:

10. No he tocado ese módulo en semanas.
9. Debe ser un problema de hardware.
8. Alguien ha cambiado mi código.
7. ¿Has comprobado que no tengas virus?
6. Seguramente tengas una versión erronea.
5. Eso es muy extraño...
4. Debe de haber algo erroneo con tus datos.
3. No había hecho eso nunca.
2. Ayer funcionaba.
1. En mi máquina funciona bien.

Programación para una PSP

Para los que quieran iniciarse en el mundo de las videocónsolas y tienen una PSP pueden usar Lua. Es tan fácil como bajarse Lua Player (hay versiones tanto para Windows como para PSP) y ponerse a programar. No es muy potente pero es un paso hacia el desarrollo consolero.

viernes, julio 28, 2006

Previsulización en Windows

Esto se sale un poco de la tematica del blog pero no me gustaría que se perdiera. Una cosa que me disgusta especialmente de Windows es que aunque desactives la opcion de previsualización internamente sigue haciendola (al menos con los vídeos). Pues hay un truco para desactivarlo de forma definitiva:
Inicio/Ejecutar: regsvr32 /u shmedia.dll

WINE Version 0.9.18

Ayer apareció una nueva version de Wine con un soporte para juegos mejorado. Uno prueba estas cosas siempre con un poco de escepticismo, pero tengo que reconocer que me ha sorprendido gratamente.

Me acuerdo cuando hace bastantes años leí sobre el proyecto Wine. Me asustó el imaginarme la enorme complejidad del proyecto (implementar API por API, función por función todo Windows) e incluso dudé de que puediera llevarse a cabo. Una vez más me equivoqué.

jueves, julio 27, 2006

Generador aleatorio de terrenos

Este algoritmo no tiene mucho misterio y es ampliamente usado en juegos como el Age of Empires.

Nos creamos un mapa de alturas. Hay varios algoritmos para esto: el ruido de Perlin es el más usado pero el algoritmo de crear colinas da también resultados bastante interesantes. Imaginemos que el mapa de alturas va desde el 0 como la altura más baja hasta el 255 como la más alta.

A cada rango de alturas le asignamos un tipo diferente de terreno. Por ejemplo:
- De 0 a 20 el mar profundo
- De 21 a 30 la costa
- De 31 a 34 la playa
- De 35 a 90 el terreno llano
- De 91 a 140 las colinas
- De 140 a 230 las montañas
- De 231 a 255 los picos nevados de las montañas

Podemos añadir ríos para hacer los mapas más interesantes. Una forma de poder hacerlo sería escoger un punto aleatorio en el mapa e ir trazando el río por el camino que te lleve cada vez a una altura más baja. Si al final llegamos al mar hemos acabado y si no es que estamos en un punto en el cual se debería crear un lago.

Y listo, así de sencillo. Lo único que nos quedará es hacer la conversión a un tile-map o a una malla en 3d pues éste algoritmo sirve tanto para juegos en 2d como en 3d. Normalmente en este punto se añaden unos arbolitos o unos arbustos a voleo en el mapa.

miércoles, julio 26, 2006

De foto a imagen 3d

Photo Modeller es un programa que ayuda a convertir tus fotos en modelos 3d. No es un proceso perfecto y requiere bastante la intervención del usuario pero puede ser útil a la hora de generar modelos de edificios u objetos para juegos en 3d.

martes, julio 25, 2006

Lo que será MIDP 3

Poco a poco ya se comienza a perfilar lo que será la nueva versión de MIDP; la plataforma más usada para programar móviles. En la parte que más nos afecta, en los juegos, lo más llamativo es un mejor soporte de pantallas grandes, poder dibujar en las pantallas secundarias y optimización de la velocidad de los juegos.

Cache para compiladores

Visual C++ soporta el uso de cabeceras precompiladas que no es más que un caché para no tener que volver a parsear todos los archivos de cabeceras.

GCC lleva algo aún más interesante, gracias a ccache se evita que el compilador compile dos veces el mismo código, hayas borrado el código objeto o no. Si vuelves a compilar un archivo
con los mismos parámetros el código objeto resultante será el mismo así que es una tontería volver a compilarlo.

No es exactamente lo mismo que las cabeceras precompiladas de Microsoft pero ofrece un mayor rendimiento ya que además el caché puede ser compartido por todo el grupo de programadores. Si a eso le sumamos distcc para conseguir una compilación de forma distribuida podemos a obtener unas velocidades increíbles.

viernes, julio 21, 2006

La inteligencia artificial perfecta

Seguramente todos sabréis lo díficil que es copiar la inteligencia de los seres vivos para aplicarla en un juego. Pues alguien ha tenido la genial idea de resolverlo por la vía "fácil": meter directamente un ser vivo dentro del juego.

Véanlo ustedes mismos.

jueves, julio 20, 2006

Hello World!

Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook! Ook! Ook? Ook! Ook? Ook.
Ook! Ook. Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook?
Ook! Ook! Ook? Ook! Ook? Ook. Ook. Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook! Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook! Ook. Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook! Ook! Ook? Ook! Ook? Ook. Ook! Ook.
Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook? Ook! Ook! Ook? Ook! Ook? Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook.
Ook? Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook! Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook.
Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
Ook! Ook. Ook. Ook? Ook. Ook? Ook. Ook. Ook! Ook. Ook! Ook? Ook! Ook! Ook? Ook!
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.


¿No me entiendes? Hola Mundo!

PD: adivinad como se llama ese lenguaje ;-)

Desmitificando C++

Es un error común confundir el lenguaje C con el C++, la verdad es que son dos lenguajes muy diferentes. Como C es el lenguaje más rapido, con perdón del ensamblador, muchos piensan que por regla de tres C++ debe ser igual de rápido. C++ es rápido pero no tanto como C, características del lenguaje como funciones virtuales, RTTI, clases, herencia, etc... lo sobrecargan haciendolo un poquito más lento que el C.

La diferéncia es escasa pero suficiente para que lenguajes como OCaml se muestren más rapido que el C++ pero más lento que el C.

martes, julio 18, 2006

Ajustando la dificultad

Una de las partes que a menudo se olvida en un juego y que es una de las principales características es el grado de dificultad. Es difícil ajustarla porque es algo subjetivo, lo que para unos es fácil para otros puede resultar imposible.

Una posible solución es ajustar la dificultad de forma automática. Para conseguirlo necesitaremos poder calcular la habilidad del jugador y establecer la dificultad en su función. En un juego tipo Tetris se hace calculando el número de líneas completadas, en un RPG según la experiencia del personaje o en un FPS se podría obtener a partir de la vida actual o de la cantidad de bichos muertos.

Tampoco conviene ahogar al jugador en un juego muy difícil, lo ideal es ir variando la dificultad como si de una onda sinusoidal se tratara e ir aumentándola poco a poco de tamaño.

lunes, julio 17, 2006

Traducción libre de la noticia de Slashdot:
GameDaily ofrece una opinión bastante interesante sobre la forma en que la industria de los videojuegos se ha roto. El autor cita costes disparados, riesgos altos, reducción de la creatividad, y estancamiento del crecimiento como algunos signos de esa crisis.

Del artículo: “La siguiente generación de sistemas requieren editores para poder apostar grandes cantidades de dinero en cada titulo. Esto significará en que buscarán una bajada del riesgo y simplemente reproducirán de forma mecánica los mejores títulos. ¿Cuantos editores se arriesgarán con un juego original multiplataforma? Claramente no son buenas noticias para los consumidores en una industria en que la invocación ha sido el conductor desde el principio. La ironía es que esos sistemas de nueva generación espectaculares, de gran calidad y capacidad podrían perder lo que es más importante, el juego en si mismo. Reconciliar lo que quiere el equipo creativo con lo que el ejecutivo necesita en términos de beneficios será un reto en auge en muchas compañías."

jueves, julio 13, 2006

La evolución tambíen es para los enemigos

A veces se me va la pinza y se me ocurren ideas macabras (macabras para el programador que quiera implementarla por su complejidad).


La locura de hoy estriba en implementar una inteligencia artificial que se adapte al jugador. ¿Qué cómo que se puede hacer eso? Pues ni idea (es el problema de las ideas macabras). Tal vez usando algoritmos genéticos sobre unos cuantos parámetros que decidan el comportamiento del enemigo e ir creando los enemigos
nuevos usando los parámetros de los que más tiempo han sobrevivido, que serán en teoría los mejor adaptados al sistema, es decir, los que han costado más matar.

Creo que podría dar resultados bastante interesantes en juegos tipo Quake porque los enemigos se irían adaptando a la forma de jugar del jugador, lo que le obligaría a adaptarse él también.

lunes, julio 10, 2006

Ogre: Colisiones de rayos

Con el motor 3d Ogre podemos lanzar rayos contra el escenario para averiguar con que objetos colisionan. No nos sirve para colisionar contra el entorno pues los rayos solo "chocan" con la envoltura de los objetos (bounding-box en este caso) así que sólo es una colisión aproximada. Pero es útil, por ejemplo, para poder seleccionar objetos con el ratón:
 1 Ray ray = mCamera->getCameraToViewportRay(mMousePos.x, mMousePos.y);
2
3 RaySceneQuery* rsq = mSceneMgr->createRayQuery(ray);
5 rsq->execute();
6
7 float dist = 50000.0f;
8
9 MovableObject* o = NULL;
10 RaySceneQueryResult& result = rsq->getLastResults();
11 for(RaySceneQueryResult::iterator ri = result.begin(); ri != result.end(); ri++) {
12 RaySceneQueryResultEntry* e = &(*ri);
13
14 if(e->distance <>movable) {
15 dist = e->distance;
16 o = e->movable;
17 }
18 }
19
20 if(o != NULL) {
21 // TODO objeto seleccionado
22 }
23
Esta consulta sólo funciona con mallas, si además queremos también calcular colisiones contra objetos especiales como billboards habrá que añadir:
 4 rsq->setQueryTypeMask(SceneManager::FX_TYPE_MASK);

martes, julio 04, 2006

¿Tabuladores sí o no?

Siempre había tenido la idea (errónea, o al menos eso creo) de que el uso de tabuladores venía de antiguo, de cuando la memoria de las máquinas era escasa debido a su alto coste. Vía Joel on Software, todo un referente, he leído un pequeño articulo sobre si a día de hoy siguen siendo útiles los tabuladores. Viene a comentar muy diversos motivos pero hay uno en especial que ha hecho que me decante definitivamente por los tabuladores: el poder usar fuentes proporcionales.

lunes, julio 03, 2006

CMake

Aprovechando el reciente anuncio del equipo KDE de que se van a pasar a CMake os hago una rápida introducción para que vayáis abriendo boca.

Este es el archivo CMakeLists.txt que tengo en uno de mis proyectos:
PROJECT(themines)

LINK_LIBRARIES(gc) (1)
AUX_SOURCE_DIRECTORY(src SRCS) (2)
ADD_EXECUTABLE(themines ${SRCS}) (3)
Con estas cuatro linias CMake compila todo el código fuente que encuentre en el subdirectorio src (2), lo linka junto con la libreria gc (1) y me crea un ejecutable (3) llamado themines.

Para preparar la compilación lo único que hay que hacer es llamar al comando:
cmake .
Y CMake te crea un archivo Makefile para poder compilar tu proyecto. Él ya se encarga de las dependencias del código fuente, de la instalación del proyecto, de la detección del compilador, etc... y todo ello multiplataforma ya que soporta plataformas como Cygwin, Mingw y Visual C.

Para cambiar algunas propiedades del proyecto como los modo debug y release se puede usar cmake -i o ccmake.

Utilidades como Autogen y Automake son complicadas y difíciles de usar. Poco a poco muchos proyectos se están pasando a esta "nueva" utilidad.

jueves, junio 29, 2006

Esculturas friki

Todo friki que se precie debería tener al menos una de estas esculturas en su casa. Me gusta especialmente el fractal de Julia en 3d.

Bug en Visual Studio

Hay un bug en el Visual Studio 2005 que pone a Microsoft en evidencia. Fue descubierto a mediados de diciembre del 2005 y, a día de hoy (más de siete meses después), sigue sin estar solucionado. El problema real viene por la forma en que Microsoft decidió solucionarlo (enero del 2006): la única forma de arregarlo será adquiriendo la nueva versión del Visual Studio que se preveé que estará a la venta en el 2007.

Personalmente me hace bastante gracia cuando alguien dice que prefiere el software propietario por encima del software libre porque detrás siempre hay una empresa que te da soporte.

miércoles, junio 28, 2006

26 años para encontrar un truco en Pac-Man

Han pasado 26 años desde la aparición de Pac-Man sin que nadie se diera cuenta de que hay un truco para que los fantasmas no te vean.

Problemas con Windows Vista y WinFS

Microsoft ha anunciado recientemente que el proyecto WinFS (el sistema de archivos transaccional que prometía el oro y el moro) ha sido cancelado. Parece ser que es debido a la gran dificultad técnica que conllevaba un proyecto de tal magnitud.

No hay proyectos alternativos así que parece que en la siguiente versión de Windows habrá que seguir usando NTFS, un sistema de archivos que se ha quedado algo desfasado en comparación con sistemas como ReiserFS o XFS.

lunes, junio 26, 2006

Generador automático de mapas cavernosos

El algoritmo para crear este tipo de mapas es, ni más ni menos, que un simple autómata celular. No hace falta tirar de funciones recursivas ni complicados bucles. Una de sus grandes bazas es que tiene multitud de parámetros con los que trastear para amoldarlo a gusto del consumidor. Pero como todo también tiene sus inconvenientes y es que no se garantiza que se pueda tener acceso a todas las regiones del mapa.

Comenzaré explicando una primera versión más sencilla pero que crea demasiadas zonas independientes:

  1. Llenad el mapa de paredes a voleo. Lo que mejor me ha funcionado ha sido usar una probabilidad del 45% para que una celda se convierta en pared.

  2. Ahora toca pasar el autómata celular. Por cada celda del mapa si la celda es una pared permanece siéndolo si tiene cuatro paredes vecinas (a un punto de distancia) y si es suelo vacío se convierte en pared si tiene, al menos, cinco paredes vecinas.

    n = celda actual
    v(n) = numero total de paredes a una unidad de distancia de n
    mapa’[n] = pared si mapa[n] = pared y v(n) >= 5 o v(n) = 4 sino vacío (1)

  3. Repetir el paso dos unas cinco veces.

Una buena forma de intentar evitar que se creen zonas independientes es intentar no cerrar los pasillos en las zonas demasiado llenas. Para ello en las repeticiones cuatro y cinco podéis retocar un poco el autómata:

n = celda actual
v1(n) = número total de paredes a una unidad de distancia de n
v2(n) = número total de paredes a dos unidades de distancia de n

Para la cuarta repetición:

mapa’[n] = pared si (1) o v2(n) <= 2

Para la quinta repetición:

mapa’[n] = pared si (1) o v2(n) <= 1

Esto no evitará que se creen espacios inaccesibles pero si que los reducirá bastante. Podéis modificar el autómata a vuestro gusto para obtener todo tipo de mapas con las formas más diversas.

using System;

using System.Collections.Generic;
using System.Text;

namespace LevelGenerator
{
class Level2
{
private int width;
private int height;
private char[,] map;

public Level2(int width, int height)
{
map = new char[width, height];
this.width = width;
this.height = height;
}

public void print()
{
for (int y = 0; y < height; y++)
{
String s = "";

for (int x = 0; x < width; x++)
s += map[x, y];

Console.WriteLine(s);
}
}

public void generate(Random r, int p, int[][] values)
{
randomizeMap(r, p);

for(int i = 0; i < values.Length; i++)
automata(values[i][0], values[i][1], values[i][2]);
}

private void randomizeMap(Random r, int p)
{
for (int x = 0; x < width; x++)
{
map[x, 0] = '#';
map[x, height - 1] = '#';
}

for (int y = 0; y < height; y++)
{
map[0, y] = '#';
map[width - 1, y] = '#';
}

for(int y = 1; y < height - 1; y++)
for (int x = 1; x < width - 1; x++)
map[x, y] = (r.Next() % 100) > p ? '#' : '·';
}

private void automata(int param1, int param2, int param3)
{
char[,] new_map = new char[width, height];

for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
if (x > 0 && x < width - 1 && y > 0 && y < height - 1)
{
int neighbors1 = countNeighbors1(x, y);
int neighbors2 = countNeighbors2(x, y);

bool wall = (neighbors1 >= param1) || (neighbors2 <= param2)
|| (neighbors1 >= param3 && getCellValue(x, y) == 1);
new_map[x, y] = wall ? '#' : '·';
}
else
new_map[x, y] = map[x, y];

map = new_map;
}

private int countNeighbors1(int x, int y)
{
int count = 0;

for (int i = -1; i <= 1; i++)
for (int j = -1; j <= 1; j++)
if (i != 0 || j != 0)
count += getCellValue(x + i, y + j);

return count;
}

private int countNeighbors2(int x, int y)
{
int count = 0;

for (int i = -2; i <= 2; i++)
for (int j = -2; j <= 2; j++)
if (Math.Abs(i) == 2 || Math.Abs(j) == 2)
count += getCellValue(x + i, y + j);

return count;
}

private int getCellValue(int x, int y)
{
if (x < 0 || x >= width || y < 0 || y >= height)
return 0;

return map[x, y] == '#' ? 1 : 0;
}
}
}

Y para usarlo:

Level2 l = new Level2(Console.WindowWidth - 1, Console.WindowHeight - 1);
l.generate(new Random(), 55, new int[][] {
new int[] { 5, 0, 4 },
new int[] { 5, 0, 4 },
new int[] { 5, 0, 4 },
new int[] { 5, 2, 4 },
new int[] { 5, 1, 4 }
});
l.print();

jueves, junio 22, 2006

El juego de la vida con esteroides

Raro será que la mayoría de vosotros no conozcáis el juego de la vida. ¿Cuantos no habremos intentado implementarlo en todo tipo de lenguajes? Para los que como yo, nos quedamos en la parte más superficial os puedo asegurar que Golly os dará mas de una sorpresa; paletas que se pasan la bola, maquinas de Tuing, células que construyen balas e incluso texto.

Una de las grandes bazas de Golly es que en lugar de implementar el juego de la vida usando una matriz en 2d usa una tabla hash lo que le permite ahorrar mucho en memoria y calcular únicamente las células que han cambiado.

¡No os lo perdáis!

martes, junio 20, 2006

King Quest III

Los chicos de Infamous Adventures han acabado recientemente el remake del King Quest III. Todo un clasico que vale la pena jugar.

lunes, junio 19, 2006

Este mundo está loco

Cuando uno se aburre muchas veces hace alguna locura. El problema viene cuando un programador se aburre y hace alguna de las suyas. Aquí tenéis el programa 'Hello World!' escrito en cincuenta lenguajes diferentes, a cada cual mas inútil que el anterior. Me han llamado especialmente la atención Chef y Weird.

viernes, junio 16, 2006

Tutorial: generador aleatorio de niveles

Hoy os traigo algo un poco más jugoso que de costumbre, un sencillo tutorial sobre como crear un nivel de forma aleatoria (y no me refiero a típicos cutre-laberintos).

Una de las partes que más me gustan de juegos tipo Nethack o Angband es la capacidad que tienen de generar el mapeado de forma aleatoria y que además quede bien :). Este es un algoritmo sencillo pero efectivo que garantizará que se pueda tener acceso a todo el mapeado. Además es bastante fácil extrapolar las coordenadas de cada habitación, así que no tendreis ningún problema para evitar los pasillos a la hora de generar los objetos y enemigos.

Estaré encantado de intentar aclararos cualquier duda (usad los comentarios para ello).

Pues allá vamos:

1. Genera una habitación vacía que ocupe todo el nivel completo:
###########################################
#·········································#
#·········································#
#·········································#
#·········································#
#·········································#
#·········································#
#·········································#
#·········································#
#·········································#
###########################################
2.
Escoge un punto a voleo en la habitacón (que no esté muy cerca de las paredes). Para la primera habitación será todo el mapa para las demás únicamente el espacio que estas ocupen:
###########################################
#·········································#
#·········································#
#·········································#
#·················X·······················#
#·········································#
#·········································#
#·········································#
#·········································#
#·········································#
###########################################
3.
Genera un pasillo horizontal o vertical (escógelo de forma aleatoria). Ten cuidado de quitar el trozo de pared de los dos extremos del pasillo. erá igual de largo (o de ancho) que la habitación que estemos usando actualmente:
###########################################
#·········································#
#·········································#
###########################################
···········································
###########################################
#·········································#
#·········································#
#·········································#
#·········································#
###########################################
4.
Como puedes ver ahora tienes dos habitaciones separadas pero falta permitir que se pueda acceder a ellas de alguna forma. Para ello crea en las dos paredes del pasillo dos puertas, una por pared, en un punto aleatorio:
###########################################
#·········································#
#·········································#
######+####################################
···········································
############################+##############
#·········································#
#·········································#
#·········································#
#·········································#
###########################################
5.
Repite los pasos del 2 al 4 en las dos nuevas habitaciones que se han generado. Tantas veces como complejo quieres que sea el nivel, pero ten cuidado que haya aún espacio libre (fácilmente comprobable porque sabemos el tamaño y las coordenadas de las dos nuevas habitaciones).

Al final os quedará una cosa parecida a esta:
######·####################################
#····+·+··································#
#····#·#··································#
######+####################################
···········································
#################·##########+##############
#···············#·#·······················#
#···············+·+·······················#
#···············#·#·······················#
#···············#·#·······················#
#################·#########################
Por último sólo comentar que si queréis guardar el nivel no hace falta que lo guardéis entero, con que uséis la misma semilla para el generador de números aleatorio será suficiente.

Actualización: Como no he encontrado mi viejo código me he puesto esta mañana y he hecho uno nuevo en C#.

/*
Level
Copyright (C) 2006 samsaga2 (samsaga2@gmail.com)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
using System;

namespace LevelGenerator
{
class Level
{
private int width;
private int height;
private char[,] map;

public Level(int width, int height)
{
map = new char[width, height];
this.width = width;
this.height = height;
}

public void print()
{
for (int y = 0; y < height; y++)
{
String s = "";

for (int x = 0; x < width; x++)
s += map[x, y];

Console.WriteLine(s);
}
}

public void generate(Random r)
{
clear();
createPassage(r, 1, 1, width - 3, height - 3);
}

private void clear()
{
for (int x = 0; x < width; x++)
{
map[x, 0] = '#';
map[x, height - 1] = '#';
}

for (int y = 0; y < height; y++)
{
map[0, y] = '#';
map[width - 1, y] = '#';
}

for (int x = 1; x < width - 1; x++)
for (int y = 1; y < height - 1; y++)
map[x, y] = '·';
}

private void createPassage(Random r, int room_x, int room_y, int room_w, int room_h)
{
// Comprobar que haya suficiente espacio
if(room_w <= 6 || room_h <= 6)
return;

// Coordenadas del nuevo pasillo
int x = 2 + r.Next() % (room_w - 4);
int y = 2 + r.Next() % (room_h - 4);
int dir = r.Next() % 2;

int room1_x, room1_y, room1_w, room1_h;
int room2_x, room2_y, room2_w, room2_h;
if (dir == 0)
{
// Dibujar el pasillo horizontal
for (int i = 0; i <= room_w; i++)
{
map[room_x + i, room_y + y - 1] = '#';
map[room_x + i, room_y + y + 1] = '#';
}
map[room_x - 1, room_y + y] = '·';
map[room_x + room_w + 1, room_y + y] = '·';

// Crear una puerta en cada pared del pasillo
int door1_x = 1 + r.Next() % (room_w - 1);
int door2_x = 1 + r.Next() % (room_w - 1);

map[room_x + door1_x, room_y + y - 1] = '+';
map[room_x + door2_x, room_y + y + 1] = '+';

// Coordenadas de las dos nuevas habitaciones
room1_x = room_x;
room1_y = room_y;
room1_w = room_w;
room1_h = y - 2;

room2_x = room_x;
room2_y = room_y + y + 2;
room2_w = room_w;
room2_h = room_h - y - 2;
}
else
{
// Dibujar el pasillo vertical
for (int i = 0; i <= room_h; i++)
{
map[room_x + x - 1, room_y + i] = '#';
map[room_x + x + 1, room_y + i] = '#';
}
map[room_x + x, room_y - 1] = '·';
map[room_x + x, room_y + room_h + 1] = '·';

// Crear una puerta en cada pared del pasillo
int door1_y = 1 + r.Next() % (room_h - 2);
int door2_y = 1 + r.Next() % (room_h - 2);

map[room_x + x - 1, room_y + door1_y] = '+';
map[room_x + x + 1, room_y + door2_y] = '+';

// Coordenadas de las dos nuevas habitaciones
room1_x = room_x;
room1_y = room_y;
room1_w = x - 2;
room1_h = room_h;

room2_x = room_x + x + 2;
room2_y = room_y;
room2_w = room_w - x - 2;
room2_h = room_h;
}

createPassage(r, room1_x, room1_y, room1_w, room1_h);
createPassage(r, room2_x, room2_y, room2_w, room2_h);
}
}
}