miércoles, diciembre 13, 2006
La inutilidad de hacer un seleccionar todo + borrar
Y sí, anales de la historia está escrito con doble intención.
C++: Namespaces sin nombre
1º Para poder abrir un espacio de nombres de forma local en lugar de en todo el archivo entero:
namespace MiSuperLibreria {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:
class Torcuato {
// …
};
};
namespace {
using namespace MiSuperLibreria;
Torcuto variable1;
};
MiSuperLibreria::Torcuato variable2;
Torcuato variable3; // Aquí dará error el compilador
#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
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
¿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++
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
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
jueves, noviembre 09, 2006
Guiones para juegos
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
Ogre::CompositorManager::getSingleton().addCompositor(viewport, "Bloom", 0);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.
Ogre::CompositorManager::getSingleton().setCompositorEnabled(viewport, "Bloom", true);
lunes, noviembre 06, 2006
Muertes estúpidas o Nethack YASD
miércoles, octubre 25, 2006
OCaml
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 =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).
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 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
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
jueves, octubre 19, 2006
Cuidao que me espiño
miércoles, octubre 18, 2006
IA Básico: Ejemplo
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
martes, octubre 17, 2006
I.A. Básica
- Breadth FirstLa 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í:
- Depth First
- A*
hacerLa diferencia entre los diferentes algoritmos es:
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
- 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
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 for(std::list<int>::iterator i = lista.begin(); i != lista.end(); i++)
2 if((*i) < 0)
3 lista.erase(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.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++;
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...
float l = 1.0f / length();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:
x *= l;
y *= l;
z *= l;
div.cLos dos están compilados con gcc programa.c -O2. Pongámoslos a prueba: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
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
# time ./div > /dev/nullTodas 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.
real 0m26.532s
user 0m26.061s
sys 0m0.374s
# time ./mult > /dev/null
real 0m1.218s
user 0m1.171s
sys 0m0.030s
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
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\({.*}, \"{.*}\"\)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.
Replace with: SetText(\1, TT("\2"))
lunes, octubre 02, 2006
Dibujando (literalmente) en 3d
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
- 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
Cómo llevar un proyecto a buen puerto
viernes, septiembre 22, 2006
To kill or not to kill
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
“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
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
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
- 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++
lunes, septiembre 11, 2006
¿Porqué las macros de C apestan?
#define cuadrado(x) x*xPero 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;Porque se expande a ((x++)*(x++)). Finalmente intentamos usar otra solución:
cuadrado(x++)
int 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) (temp_var=(x); temp_var*temp_var)
#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) {Es literalmente imposible de llevar a cabo en C.
case “hola”: llamar_hola(); break;
case “adios”; llamar_adios(); break;
default: llamar_pordefecto(); break;
}
[Basado en http://lists.warhead.org.uk/pipermail/iwe/2005-July/000130.html]
jueves, septiembre 07, 2006
Tengo la idea... ¿y ahora qué?
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++?
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
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
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
viernes, agosto 11, 2006
Sergio Leone
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++
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
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
viernes, julio 28, 2006
Previsulización en Windows
Inicio/Ejecutar: regsvr32 /u shmedia.dll
WINE Version 0.9.18
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
1º 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.
2º 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
3º 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
martes, julio 25, 2006
Lo que será MIDP 3
Cache para compiladores
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
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++
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 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
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
1 Ray ray = mCamera->getCameraToViewportRay(mMousePos.x, mMousePos.y);Esta consulta sólo funciona con mallas, si además queremos también calcular colisiones contra objetos especiales como billboards habrá que añadir:
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
4 rsq->setQueryTypeMask(SceneManager::FX_TYPE_MASK);
martes, julio 04, 2006
¿Tabuladores sí o no?
lunes, julio 03, 2006
CMake
Este es el archivo CMakeLists.txt que tengo en uno de mis proyectos:
PROJECT(themines)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.
LINK_LIBRARIES(gc) (1)
AUX_SOURCE_DIRECTORY(src SRCS) (2)
ADD_EXECUTABLE(themines ${SRCS}) (3)
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
Bug en Visual Studio
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
Problemas con Windows Vista y WinFS
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:
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.
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)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
lunes, junio 19, 2006
Este mundo está loco
viernes, junio 16, 2006
Tutorial: generador aleatorio de niveles
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:
#·········································#
#·········································#
#·········································#
#·········································#
#·········································#
#·········································#
#·········································#
#·········································#
#·········································#
###########################################
###########################################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:
#·········································#
#·········································#
#·········································#
#·················X·······················#
#·········································#
#·········································#
#·········································#
#·········································#
#·········································#
###########################################
###########################################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);
}
}
}