Quantcast
Channel: Genbeta
Viewing all 92 articles
Browse latest View live

SFML 2, biblioteca para el desarrollo de videojuegos

$
0
0

Logo SFML

SFML es una biblioteca multiplataforma (Windows, Mac OS X y Linux) escrita en C++ y totalmente orientada a objetos para el desarrollo de aplicaciones multimedia enfocada en el desarrollo de videojuegos 2D.

La actual versión estable es la 1.6, pero en este artículo vamos a hablar de SFML 2 que se encuentra en Release Candidate desde el 15 de abril y desde entonces no se ha parado de pulir y mejorar. Nos encontramos en una versión bastante madura con muchas novedades.

Antes que nada vamos a hablar de la estructura de SFML y que es capaz de hacer. SFML se compone de cinco módulos: System, Window, Graphics, Audio y Network. Como vemos cada uno cubre una de las áreas que nos podemos encontrar en la programación de videojuegos.

Los cinco módulos de SFML 2

  • System. Este es el módulo básico de SFML, nos proporciona clases útiles para el desarrollo como manejo sencillo de hilos, control del tiempo, plantillas para manejar vectores, streams, cadenas, utf, etc.
  • Window. El módulo window nos sirve para el manejo de la ventana de nuestra aplicación usando este módulo se puede crear un contexto OpenGL en el que dibujar directamente desde OpenGL. Además se encarga de gestionar todos los eventos que recibe la ventana (cierre, maximizar, cambio de tamaño, etc.) así como el manejo de dispositivos de entrada (teclado, ratón, joystick).
  • Graphics. Nos proporciona un contexto especial sobre Window donde se puede “dibujar”. Además nos proporciona varias clases útiles para el manejo de imágenes, texturas (imágenes vivas en la tarjeta gráfica), colores, sprites, textos y figuras 2D como círculos, rectángulos y formas convexas.
  • Audio. Nos proporciona varias clases para trabajar con el audio, dos tipos de audio hay en SFML por defecto Sound que es un archivo de sonido corto que se carga en memoria y Music que son archivos largos de audio que se van reproduciendo en Stream. Soporte para sonido 3D.
  • Network. Colección de clases que nos facilita la creación de aplicaciones en red. Cuenta con clases para el manejo de http, ftp, packet, socket, etc.

Formatos soportados

SFML como buena biblioteca multimedia soporta muchísimos de los formatos más usados a continuación una lista de todo los soportados:

  • Formatos de imagen: bmp, dds, jpg, png, tga, psd.
  • Formatos de fuentes: ttf, cff, pcf, fnt, bdf, pfr, sfnt, type 1, type 42.
  • Formatos de audio: ogg, wav, flac, aiff, au, raw, paf, svx, nist, voc, ircam, w64, mat4, mat5 pvf, htk, sds, avr, sd2, caf, wve, mpc2k, rf64.

Como pega tiene el no soporte del formato de audio mp3 que es tan común, pero una conversión a ogg nos resuelve el problema.

Conclusiones

SFML 2 es una gran biblioteca totalmente orientada a objetos que nos facilita el manejo multimedia de la aplicación. Ojo SFML es una biblioteca multimedia no un motor para hacer videojuegos por lo que es solo la base sobre la que construir un videojuego o un motor.

Aunque aún no se ha anunciado versión oficial estable de SFML 2 esta ya supera en mucho a la actual versión 1.6 por lo que es recomendable su uso en detrimento de esta.

Más información | Sitio Oficial
Github | sfml-dev


Tiled Map Editor, el editor de mapas libre

$
0
0

Tiled Map Editor

Una de la problemáticas del desarrollo de videojuegos, sobre todo al principio cuando solo se quiere hacer un prototipo o juegos sencillo es tener un editor de mapas decentes. Hay dos opciones o crear tu propio set de edición o usar un editor de mapas de propósito general como Tiled Map editor.

El Tiled es un editor de mapas sencillo, pero potente se adapta a cualquier tipo de juego 2D tanto si tu juego es un RPG, un plataformas como un clon del Breakout con Tiled tienes todo lo que necesitas.

Características

Como hemos dicho Tiled es un editor de propósito general que se adapta a todo tipo de proyectos, cuenta con un formato basado en XML llamado TMX del que luego hablaremos.

Soporta mapas tanto Ortogonales como isométricos, soporte para objetos con precisión a nivel de pixel que hace que no sea necesario que tu mapa sea basado solamente en tiles.

Tiene soporte para copiar y pegar trozos de mapa de manera natural ideal para partes repetitivas y cuenta con deshacer y rehacer fácilmente.

Recarga automáticamente los tileset usados, ideal para cuando se está diseñando o modificando un tileset en un programa de edición de imágenes como Photoshop o GIMP al guardarlo automáticamente se actualiza en el Tiled.

Soporta plugins escritos con C++ y QT por lo que es ideal para adaptarlo a cualquier proyecto y generar ficheros de mapa acorde a cualquier tipo de engine.

En resumen sus características más destacables son las siguientes.

  • Mapas ortogonales e isométricos.
  • Formato de archivo XML.
  • Soporte para múltiples capas tanto de tiles como de objetos.
  • Soporte para múltiples tilesets.
  • Muy personalizable todas las propiedades de mapas con parejas de clave valor.
  • Soporte de color clave y transparencias.
  • Tamaño de los tiles y mapas totalmente personalizable.

Mapa isométrico tiled

Novedades de la versión 0.9

El pasado 28 de enero se estreno la esperada actualización de este proyecto que no para de crecer. Sus principales novedades fueron las herramientas de terreno que nos permite definir varios tiles como un terreno y nuestro mapa se dibuja y se adapta solo para hacer transiciones correctas entre los tipos de terreno.

Cuadro editor de terrenos

Otra de las novedades más interesantes es la inclusión de un minimapa ideal para navegar por mapas de tamaño considerable.

Por último destacar el panel de edición de objetos para navegar por todos los objetos de la escena y sus propiedades fácilmente.

Editor de objetos

El formato de mapa de Tiled Map Editor

El editor está muy bien diseñado y es perfecto para editores de niveles, pero como programadores lo que más nos interesa es el formato de mapa para saber como usar los archivos generados en tiled en nuestro proyecto.

Como hemos dicho el formato de Tiled es el TMX que no es más que un fichero XML. por tanto se puede editar fácilmente en un editor de texto. Vamos a ver un ejemplo.

<?xml version="1.0" encoding="UTF-8"?>
<map version="1.0" orientation="isometric" width="40" height="40" tilewidth="64" tileheight="32">
 <properties>
  <property name="prop_map" value="25"/>
 </properties>
 <tileset firstgid="1" name="tiled_dungeon" tilewidth="64" tileheight="128">
  <image source="../graphics/tilesets/tiled_dungeon.png" width="1024" height="1920"/>
 </tileset>
 <layer name="Capa de Patrones 1" width="40" height="40">
  <properties>
   <property name="tipo" value="base"/>
  </properties>
  <data encoding="base64" compression="gzip">
   H4sIAAAAAAAAA+3SUQoAIQgAUa9Q9z9sN0jMFJUZ8K9dHpYI/WopU8G0L1Pdp+03Yu8W36u9q8+69+z7ffF5zlub5vv9f+/gw2fxeb+f5ou+L3y57x0fPnz48PXwERERUUwH5o9HhQAZAAA=
  </data>
 </layer>
 <layer name="Capa de patrones 2" width="40" height="40" visible="0">
  <data encoding="base64" compression="gzip">
   H4sIAAAAAAAAA+2V3QrCMAyF8066+f+H3ggK3ikivv9jWGGwWGNYllS9OB8cKGvWnaVNSgQimCfVTPdG/NnqB76q5tvTDrGDwl4kKkNs/UFXZc6bc4u/Poyc7x+Tdo32fjtvbJW5JdnyvlHWetbPmNp/GSZNmIcDm+NaK2tqeyf9lxYv7ROP71M7Vn8Les0R10mIP3/ZnxXub0bt2bkIY+mZ1tMi/JWs/ej8RROdP+nMauL1X8qfN3+l68N7/rz+eH+/CeO8/rQ7WboPvP4i61Pq79Z+mxPpL+I85cCfD/jzAX8+4M+H1t+t6nIfAAAAAOA/eACNJESSABkAAA==
  </data>
 </layer>
 <layer name="Capa de patrones 3" width="40" height="40" visible="0">
  <data encoding="base64" compression="gzip">
   H4sIAAAAAAAAA+3QMQrAIAxGYa+cpeBcsL1TPZwdHBzE0KGJ4vvgB4cMD0PAbpJ3ALb1vMveEQCA5UnzPupGzrqZzd5nKXoHfHB5B3Ro/3ebVOAvop8AAACgUQAv8zdRABkAAA==
  </data>
 </layer>
 <objectgroup name="Capa de Objetos 1" width="40" height="40">
  <object type="start" x="556" y="650"/>
  <object x="266" y="692" width="149" height="135"/>
 </objectgroup>
 <objectgroup color="#ffaaff" name="Capa de Objetos 2" width="40" height="40">
  <object x="188" y="193" width="110" height="92"/>
 </objectgroup>
</map>

Como vemos es fácil de ver en el documento toda la información de nuestro mapa basta con usar la biblioteca XML de turno de nuestro lenguaje para extraer toda la información necesaria, quizás las parte más inquietante del mapa sea esta:

<layer name="Capa de patrones 3" width="40" height="40" visible="0">
  <data encoding="base64" compression="gzip">
   H4sIAAAAAAAAA+3QMQrAIAxGYa+cpeBcsL1TPZwdHBzE0KGJ4vvgB4cMD0PAbpJ3ALb1vMveEQCA5UnzPupGzrqZzd5nKXoHfHB5B3Ro/3ebVOAvop8AAACgUQAv8zdRABkAAA==
  </data>
 </layer>

Esta parte como vemos guarda la información de las capas de tiles, pero los datos vienen codificados en base64 y comprimidos con gzip (en las últimas versiones se utiliza por defecto zlib en lugar de gzip). ¿Por que esto? Por eficiencia, son la parte que más ocupa del archivo ya que contienen la lista de etiquetas de la grilla, algo como esto es lo que pone una vez descomprimido y decodificado.

<tile gid="17"/>
<tile gid="17"/>
<tile gid="5"/>
<tile gid="8"/>
<tile gid="0"/>

Contiene en orden de arriba a abajo y de izquierda a derecha el tile que tiene la capa empezando por el uno. Es decir si la capa en el primer tile arriba a la izquierda la segunda imagen del tileset entonces contendrá un 2. Es importante resaltar que se empieza a contar en 1 y se deja el 0 para los casillas que no tienen ningún tile.

Los mapas sin codificar y comprimir ocupan entre 40 y 50 veces más por lo que es más eficiente tenerlos en este formato y usar nuestro lenguaje para comprimir y decodificar la información.

Para un detalle a fondo del formato de mapa puedes revisar las especificaciones oficiales.

Implementeción en lenguajes y engines

Tiled es un editor de mapas muy popular y usado en muchos videojuegos 2D por lo que si usas un lenguaje o un engine popular es probable que ya exista un cargador de mapas para tu lenguaje o engine. Existen cargadores para C++, Python, C#, Flash, etc. y los engines más populares como Cocos2D, XNA o Game Maker.

Puedes encontrar una lista completa de todas las implementaciones del formato de mapa de tiled.

Conclusiones

Tiled Map Editor es ahora mismo el editor de mapas 2D de referencia usado en muchos proyectos comerciales y muy ligado a los juegos para móviles.

Más información | Sitio oficial
Wiki | Tiled Wiki
GitHub | Tiled Code

Cómo empezar a programar videojuegos

$
0
0

Partes programación de videojuegos

Últimamente ha surgido un boom de desarrollo de videojuegos indie, videojuegos hechos por pequeños estudios o grupos de personas sin los millonarios presupuestos de las grandes compañías, pero que a veces consiguen verdaderas obras maestras.

Muchos ven en esto un modelo de negocio y quieren saber cómo empezar a programar videojuegos. Otros simplemente por curiosidad, diversión o un poco de todo, en cualquier caso vamos a ver que necesitamos para empezar en este mundo de la programación de videojuegos.

¿De verdad lo que quieres es programar videojuegos?

Parece una tontería de pregunta, pero es clave. En el desarrollo de videojuegos existen varia áreas, estas son las cinco principales: Diseño, Programación, Gráficos, Audio, Distribución y Marketing. Vamos a pasar a analizarlas brevemente.

  • Diseño. La parte más importante de un videojuego. Historia, Guión, jugabilidad, reglas y demás conceptos que hacen a un juego ser lo que es.
  • Programación. Una vez elaborado un diseño es la parte donde se juntan gráficos, audios y reglas para dar vida a un mundo interactivo. Existen varias disciplinas a su vez dentro de ella como programación gráfica, gameplay o inteligencia artificial.
  • Gráficos. Interfaces, modelos 3D, animaciones y todo lo que “se ve” de el videojuego, existen varias disciplinas tanto en 2D como en 3D.
  • Audio. Efectos de sonido, música de fondo, diálogos. Muy importante para crear ambiente.
  • Distribución y marketing. El arte de publicar y promocionar un videojuego, responsable del éxito o no de muchos productos dependiendo de las estrategias que sigan.

Como vemos la programación solo es una parte del proceso y nunca un todo, si aún crees que la programación de videojuegos es tu área vamos a analizarla a fondo.

Programar videojuegos, sus áreas

Los grandes estudios tienes varias decenas de programadores, especializados en diferentes áreas. En programación de videojuego los principales equipos y/o disciplinas que puedes encontrar son los siguientes.

  • Programación del motor. Son los encargados de implementar la base sobre la que se sustenta el videojuego. Comunicación con el sistema operativo, gestión de memoria, gestión de cadenas, gestión de recursos, etc. Son necesarios grandes conocimiento de la plataforma para la que se programa, algoritmia y complejidad, opimización y gestión a bajo nivel.
  • Programación gráfica. Su misión es lidiar con las diferentes apis gráficas como DirectX y OpenGL. Conocimienos de dichas apis, y matemáticas sobre todo álgebra y geometría.
  • Programación de física. Se encarga de emular los comportamientos físicos del videojuego. Conocimientos de matemática vectorial y física dinámica y mecánica.
  • Programación de inteligencia artificial. Es la encargada de hacer nuestros enemigos (o nuestros aliados) inteligentes. Conocimientos de lenguajes de script, matemáticas y algoritmos de IA como pathfinding, máquinas de estados finitos o redes neuronales.
  • Programación de red. Se encarga de la parte multijugador, servidores y todo lo que sea conectar una máquina con otra.
  • Programaición de Gameplay. El equipo que se encarga de programar la lógica del juego, sus reglas. Conocimientos de lenguajes de script y uso de las partes desarrolladas por los otros equipos.

Estas son las principales áreas en las que se dividen los grandes estudios pudiendo variar en muchos de ellos, pero estas son las principales disciplinas de la programación de videojuegos. Vale, lo tengo claro, ¿Por cual empiezo? Por todas y por ninguna.

Cuando uno empieza ninguna de las áreas tiene un alto grado de complejidad y el programador indie debe aprender a lidiar con todas las áreas, las especializaciones es bueno tenerlas en cuenta de cara a un futuro, pero para empezar te tocará aprender un poco de todo.

Ahora sí, por donde empezar a programar videojuegos

Ahora que quedan claros los diferentes niveles que existen y el grado de complejidad que puede alcanzar cada área no te asustes, vamos a empezar por el principio.

Cuando uno se mete por primera vez en el desarrollo de videojuegos lo primero que le da por hacer es un clon de su Final Fantasy, GTA o MMORPG preferido y sucede lo que tiene que suceder: fracasa estrepitosamente. Todos los que un día empezamos pasamos por eso y es bueno para ver el nivel de complejidad de esos proyectos y lo que significa tal obra de la ingeniería de software.

Una vez superado este trauma inicial y con los pies en el suelo llega la primera regla: olvídate del 3D, al menos, de momento. Tus primeros juegos deben ser clones de los clásicos del 2D: Pong, Breakout, Pacman y cuando te sientas con confianza, incluso un juego de plataformas.

Aunque estoy suponiendo demasiadas cosas y una de ellas es que ya sabes programar así que vámonos más atrás aún.

Conocimientos básicos necesarios para programar videojuegos

  • Conocimintos de matemáticas. En principio no son muchos y dependerán básicamente del tipo de juego, pero suelen ser esenciales conocimientos básicos de trigonometría y geometría.
  • Conocimintos de física. Como las matemáticas depende del tipo de juego, para juegos de plataforma con conocimientos básicos de cinemática es suficiente.
  • Conocimintos de programación. Se debe saber programar y conocer bien un lenguaje de programación el lenguaje elegido es lo de menos siempre que sea popular y con una amplia comunidad y colección de bibliotecas.

Si se poseen estos conocimientos lo siguiente es buscar una biblioteca para el desarrollo de videojuegos de tu lenguaje. Aquí van algunas de las para los lenguajes más populares.

Como vemos el lenguaje es lo de menos en todos existen buenas bibliotecas 2D para empezar a desarrollar videojuegos. Lo importante es que aprendas las técnicas de la programación en tiempo real y eso es aplicable a cualquier lenguaje.

Lo más importante, haz juegos

Esto es lo más importante de este artículo, si quieres ser programador de videojuegos: haz juegos, muchos juegos. Escribir código y probar y probar es la única forma de aprender y mejorar lo demás vendrá poco a poco con paciencia y empeño.

Cocos2D-x haz juegos para todas las plataformas

$
0
0

Cocos2D-x logo

Al querer hacer un videojuego multiplataforma nos encontramos con la problemática de tener que implementar el mismo código en el lenguaje y SDK de turno. Que si Objetive-C para IOS y OSX, Java para Android, C# para Windows Phone, etc.

Duplicar trabajo y esfuerzos, lo ideal sería escribir código una vez independiente de la plataforma y poder compilar una versión para cada una de ellas sin tener que tocar el código. Esa es la idea de Cocos2D-x.

Cocos2D-x surgió de la idea de Cocos2D-iphone una biblioteca para crear juegos 2D de forma sencilla para IOS. A su vez Cocos2D-iphone había surgido de Cocos2D, la biblioteca original escrita en Python sobre Pyglet. Como vemos ha surgido toda una familia relacionadas a los conceptos de Cocos2D.

Familia Cocos2D

A nosotros nos interesa la versión Cocos2D-x puesto que esta versión es compatible con las plataformas más populares sin tener que tocar el código del juego que estará escrito en C++, el lenguaje universal. Estas son las plataformas soportadas por Cocos2D-x.

  • o“ para las que funcionan correctamente
  • i“ para las obsoletas o des actualizadas.
  • w“ para las versiones en progreso
Platforms C++ Lua Javascript
Mobile Platforms iOS o o o
Android o o o
WindowsPhone8 o
Bada i
BlackBerry o
MeeGo i
Marmalade o
Desktop Platforms win32 o o o
Linux o o
Win8 Metro o
Mac OS X o o

Para las versiones web hay que usar Cocos2D-html5 y cuenta con las siguientes versiones:

Browsers Canvas WebGL
Chrome o w
FireFox o w
IE 9 and above o
Other HTML5-ready Browsers o

Como vemos sin duplicar esfuerzos podemos tener nuestro juego para diversos sistemas operativos y con un poco más su versión web en HTML5. Biblioteca ideal para el que quiera realizar juegos para varias plataformas sin gastar dinero en licencias ya que Cocos2D-x es software libre.

Cocos2D-x | Sitio Oficial

Estructura del código fuente en C++

$
0
0

Carpeta código

Si bien un programa escrito en C++ se podría hacer en un único fichero de texto, cualquier proyecto serio requiere que el código fuente de un programa se divida en varios ficheros para que sea manejable, muchos principiantes no se dan cuenta de la importancia de esto, sobre todo porque mucho lo han intentado y les ha dado más problemas que soluciones. En este artículo vamos a explicar como definir la estructura del código fuente en C++.

Archivos de cabecera y archivos fuentes

En C++ existen principalmente dos tipos de ficheros, los de cabecera (normalmente .h o .hpp) y los ficheros fuentes (normalmente con extensión .cpp). La diferencia entre ellos es puramente conceptual, ambos son ficheros de texto plano el compilador no distingue entre uno y otro, esto lo hacemos los programadores.

¿Por qué dividir el código fuente?

Es la primera pregunta que se hacen los programadores principiantes, no entienden el sentido de tener un programa en varios archivos. Vamos a ver las ventajas de esto.

  • Compilación más eficiente. Si tu tienes un fichero fuente con 10.000 líneas de código y haces una pequeña modificación tendrás que recompilar las 10.000 líneas de código. Sin embargo, si tienes 10 ficheros de 1.000 líneas de código si haces una pequeña modificación en cualquiera de ellos solo deberás recompilar ese fichero ahorrando de recompilar 9.000 líneas de código fuente.
  • Organización. Imagina que estas creando un videojuego y tienes un solo archivo llamado juego.cpp con todas las clases, estructuras y variables, deberás buscar a lo largo de todo el archivo si decides que quieres modifica una parte. Ahora piensa que has dividido tu juego en los archivos main.cpp graficos.cpp audio.cpp y entrada.cpp si quieres modificar alo relacionado con los gráficos sabes que debes buscar en el fichero graficos.cpp reduciendo la búsqueda considerablemente.
  • Facilitar la reutilización. Imagina que estás trabajando con fracciones en tu programa y decides crear una clase Fracción para el manejo de fracciones, la pruebas la testeas y compruebas que funciona. Si en el futuro desarrollas una aplicación que necesite el uso de fracciones solo deberás recuperar tu fichero .h y .cpp asociado a la clase fracción para implementar la funcionalidad en tu nuevo proyecto.
  • Compartir código en varios proyectos. Este punto es similar al anterior, pero hay veces en las que más de un proyecto utilizan el mismo código, sería genial incluir el mismo el ambos proyectos y si se modifica este código automáticamente se modifique en ambos proyectos (es la esencia de las bibliotecas).
  • Dividir tareas entre programadores. Si tenemos un equipo de programadores y queremos que trabajen a la vez en un mismo proyecto solo debemos asignar un fichero a cada uno y luego juntarlos, sin que se pisen unos a otros (Este es el principio de la Programación Orientada a Objetos y la división en clases independientes unas de otras).

Espero haberte convencido de lo necesario que es dividir el código fuente en varios archivos, si es así, sigue leyendo y te daré algunas pautas de como hacerlo.

Cómo dividir el código fuente de un programa C++

La mayoría de las reglas a seguir son lógicas y un tanto arbitrarias, pero se presupone buen sentido al programador. Lo ideal es dividir por módulos según que hace que, si tienes un juego que tiene una clase encargada de la entrada de datos, otra de los gráficos y otra del audio lo lógico es hacer la división en estos módulos. Una buena idea es tener un par de ficheros por clase MiClase.h y MiClase.cpp

A veces existe la problemática de no saber en que fichero o módulo meter una parte del código (Un ingeniero de software probablemente te diría que es porque lo has diseñado mal).

Una vez hecha la división lógica por funcionalidad queda ver que va en el archivo de cabecera y que en el archivo de código fuente. Lo que suele estar en la parte superior es un serio candidato a estar en el archivo de cabecera, de ahí su nombre. Los archivos de cabecera suelen incluir todos o algunos de los siguientes elementos.

  • Definición de estructuras y clases.
  • Definición de tipos (typedef).
  • Prototipos de funciones.
  • Variables Globales (ver más adelante).
  • Constantes
  • Macros #define.
  • Directivas #pragma.

Además se deben incluir plantillas y funciones en línea, como veremos más adelante en este artículo.

Por ejemplo, mira cualquier biblioteca estándar que venga con el compilador que uses. stdlib.h es un buen ejemplo. Te darás cuenta que tiene algunas de las cosas mencionadas en la lista de antes. Del mismo modo puedes ver que las variables globales están precedidas por el modificador extern, esto es importante, pero lo veremos más adelante.

En general todo lo que son las definiciones deben de ir en los ficheros de cabecera y todo lo que son las implementaciones en los ficheros de código fuente, así que lo normal es tener un archivo .h y otro .cpp con nombres iguales por ejemplo Sprite.h y Sprite.cpp en uno se definen Clases, funciones, estructuras, etc. y en el otro se implementan.

Recuerda que ambos son ficheros de texto en el fondo podrías meter lo que quisieras en cada uno, la organización y lógica corresponde al programador.

Problemas al dividir el código

No todo podía ser maravilloso, al separar el código en varios archivos surgen algunas complicaciones que suelen tirar para atrás a los programadores novatos, vamos a repasarlas y buscarles solución.

  • Un archivo fuente de repente sin tocarlo deja de compilar porque no encuentra la definición de una clase o función. (Esto a menudo se manifiesta en forma de algo parecido a un “error C2065: ‘MyStruct’: identificador no declarado”. En Visual C++, aunque esto puede producir cualquier número de mensajes de error diferentes en función de exactamente lo que están tratando de referencia.
  • Dependencias cíclicas. El archivo de cabecera ClaseA depende de ClaseB y a su vez ClaseB depende de ClaseA, incluyas cual incluyas primero el otro aún no se ha definido.
  • Duplicar las definiciones. Imagina que tienes un archivo llamado map.h que incluye los archivos sprite.h y hero.h, a su vez hero.h incluye sprite.h por lo que sprite.h se incluiría dos veces en map.h, provocando un error.
  • Duplicado las instancias de los objetos dentro del código que compila bien. Este es un error vincular, a menudo difícil de entender.

Bueno una vez definidos de manera general vamos a definirlos algo mejor y buscarles una solucion.

Eliminación accidental de cabeceras

Imagina que tenemos los siguientes archivos con el siguiente código:

/* Header1.h */
#include "header2.h"
class ClaseA { ... };
/* ---------- */
/* Header2.h */
class ClaseB { ... };
/* File1.cpp */
/* ---------- */
#include "Header1.h"
ClaseA miClaseA_instance;
ClaseB miClaseB_instance;

File1.cpp incluye a Header1.h que a su vez incluye a Header2.h por lo que en File1.cpp quedan definidas ambas cabeceras y puedes crear objetos definidos en ambos ficheros. De repente un día decides que Header1.h ya no necesita incluir a Header2.h cuando trates de compilar File1.cpp solo incluirá a Header1.h porque Header2.h ya no está incluido en este produciendo error al no encontrar los elementos definidos en Header2.h

La solución a este problema es muy simple, incluir siempre explícitamente todas las cabeceras necesarias en cada archivo y no suponer que ya otro fichero la incluye. Así en File1.cpp debemos incluir tanto Header1.h y Header2.h así si por alguna razón Header1.h deja de incluir a Header2.h no afectará ha File1.cpp

Dependencias cíclicas

El problema común de archivos que depende de entre sí, veamos el siguiente código.

/* Parent.h */
#include "Child.h"
class Parent
{
    Child* elChild;
};
/* ---------- */
/* Child.h */
#include "Parent.h"
class Child
{
    Parent* elParent;
};

Para definir la clase Parent se necesita tener primero definida la clase Child y para poder definir la clase Child se necesita tener definida previamente la clase Parent por lo que nos encontramos en un bonito bucle. La solución son las declaraciones adelantadas (forward declaration).

Como tanto la clase Parent como la clase Child lo único que tienen de la otra clase es un puntero de referencia y los punteros no necesitas saber toda la estructura de la clase con hacer lo siguiente vale.

/* Parent.h */
class Child; /* Forward declaration de Child; */
class Parent
{
    Child* elChild;
};

Como vemos sustituimos el #include “Child.h” por su declaración adelantada, esto es válido ya que el compilador solo necesita saber en la definición que hay un tipo llamado Child ya que solo va a almacenar una dirección de memoria no necesita saber como es el tipo.

Por supuesto, en los archivos fuentes, habrá funciones que se aplican a Parent que modifican también a Child. Por lo tanto es probable que haya que incluir Parent.h y Child.h tanto en Parent.cpp como en Child.cpp.

Dependencia cíclicas

Duplicado de definiciones

Duplicar definiciones en tiempo de compilación significa que un archivo de cabecera terminó incluyéndose más de una vez en un archivo en particular. Esto lleva a que una clase o estructura se define más de una vez, causando error. Lo primero que debes hacer es asegurarte de que se incluyen solo los archivos necesarios para ese código fuente en particular y quitar todo lo que no utilice.

Lamentablemente, esto rara vez es suficiente, ya que algunas cabeceras se incluyen otras cabeceras. Vamos a revisar un ejemplo.

/* Header1.h */
#include "header3.h"
class ClassOne { ... };
/* ---------- */
/* Header2.h */
#include "header3.h"
class ClassTwo { ... };
/* ---------- */
/* File1.cpp */
#include "Header1.h"
#include "Header2.h"
ClassOne myClassOne_instance;
ClassTwo myClassTwo_instance;

Por alguna razón tanto Header1.h como Header2.h incluyen a Header3.h, quizás ClassOne y ClassTwo se compenen con funciones de Header3.h. La razón no es importante, pero muchas veces sucede casos como estos en los que al final en File1.cpp se acaba incluyendo dos veces el mismo archivo incluso sin haber una #include a él en el mismo archivo, recuerda que la directiva #include lo que hace es, antes de compilar, copiar todo el contenido del archivo incluido en el archivo actual. así que File1.cpp quedaría de la siguiente manera.

Archivos de inclusión

A los efectos de la compilación, File1.cpp termina con copias de Header1.h y Header2.h, los cuales incluyen sus propias copias de Header3.h. El archivo resultante, con todas las cabeceras de expansión en línea en su archivo original, se conoce como una unidad de traducción. Debido a esta expansión en línea, todo lo declarado en Header3.h va a aparecer dos veces en esta unidad de traducción, causando un error.

Así que, ¿qué hacer? No se puede hacer sin Header1.h o Header2.h, ya que se necesita para acceder a las estructuras declaradas en su interior lo que necesita alguna forma de asegurar que, no importa qué, Header3.h no va a aparecer dos veces en el File1.cpp cuando se compila.

Si se miraba stdlib.h antes, te habrás dado cuenta las líneas en la parte superior similar a lo siguiente:

#ifndef _INC_STDLIB
#define _INC_STDLIB

Y en la parte inferior del archivo, algo así como:

#endif  /* _INC_STDLIB */

Esto es lo que se conoce como un “inclusion guard”. Viene a decir que si no está definido _INC_STDLIB defínelo, sino, ve a #endif sería similar al código cuando quieres que algo se ejecute solo una vez.

Durante la compilación de File1.cpp, la primera vez que pides que se incluya el archivo stdlib.h, llega a la línea #ifndef y continúa porque “_INC_STDLIB” aún no está definida. La siguiente línea define ese símbolo y lleva a cabo la lectura en stdlib.h. Si hay otra “#include” durante la compilación de File1.cpp, leerá el cheque #ifndef y luego salta a #endif al final del archivo. Esto se debe a todo lo que entre el #ifndef y #endif se ejecuta sólo si “_INC_STDLIB” no está definido, y que se definió la primera vez que lo incluye. De esta manera, se garantiza que las definiciones en stdlib.h sólo son cada vez incluye una vez al ponerlos dentro de #ifndef / #endif.

Esto es trivial para aplicar en tus propios proyectos. Al comienzo de cada archivo de cabecera escribe lo siguiente:

#ifndef INC_FILENAME_H
#define INC_FILENAME_H

Ten en cuenta que el símbolo (en este caso, “INC_FILENAME_H”) tiene que ser único en todo el proyecto. Es por esto que es una buena idea de incorporar el nombre del archivo en el símbolo. No agregue un guión bajo al principio como stdlib.h tiene como identificadores precedidos por un guión bajo se supone que son reservados para “la aplicación” (es decir, el compilador, las librerías estándar, y así sucesivamente). Luego se agrega el #endif / * INC_FILENAME_H * / al final del archivo. El comentario no es necesario, pero te ayudará a recordar a que pertenece ese #endif.

Duplicado de objetos o tipos globales

Cuando el enlazador trata de crear un archivo ejecutable (o biblioteca) de tu código lo que hace es meterlo todo en un archivo objeto (.obj o .o), uno por cada archivo de código fuente y los une. El trabajo principal del enlazador es resolver los identificadores (básicamente, las variables o los nombres de funciones) y convertirlas en direcciones máquina en el archivo final. El problema surge cuando el enlazador encuentra dos instancias o más de ese identificador en los archivos objetos, entonces no se puede determinar cual es el “correcto” para su uso. El identificador debe ser único para evitar cualquier ambigüedad, Así que ¿Cómo es que el compilador no ve que hay un identificador duplicado y el enlazador si lo ve?

Imagina el siguiente código:

/* Header.h */
#ifndef INC_HEADER_H
#define INC_HEADER_H
int my_global;
#endif /* INC_HEADER_H */
/* ---------- */
/* code1.cpp */
#include "header1.h"
void DoSomething()
{
    ++my_global;
}
/* ---------- */
/* code2.cpp */
#include "header1.h"
void DoSomethingElse()
{
    --my_global;
}

La primera se compila en dos archivos objeto, probablemente llamado code1.obj y code2.obj. Recuerde que una unidad de traducción contiene una copia completa de todos los encabezados incluidos en el archivo que está compilando. Finalmente, los archivos de objetos se combinan para producir el archivo final.

Aquí hay una representación visual de la forma en que estos archivos (y su contenido) se combinan:

Linker

Nota que hay dos copias de “my_global” en ese bloque final. Aunque “my_global” fue único para cada unidad de traducción (esto sería garantizada por el uso de los guardias de inclusión), que combina los archivos objeto generados por cada unidad de traducción se traduciría en que haya más de una instancia de my_global en el archivo. Esto se marca como un error, ya que el enlazador no tiene manera de saber si estos dos identificadores son en realidad una misma, o si uno de ellos era mal llamado justa y que se supone en realidad que es de 2 variables independientes. Así que hay que arreglarlo.

La respuesta no consiste en definir las variables o funciones en los archivos de cabecera en lugar de los archivos fuentes, donde estás seguro que se compilan solo una vez. Esto trae un nuevo problema, ¿Cómo hacer las funciones y variables visibles globalmente si no se encuentra en un archivo de cabecera? ¿De qué manera la pueden “ver” otros archivos? La respuesta es declararlas en los archivos de cabecera, pero no definirlas. Esto permite al compilador saber que la función o variable existe, pero delega el acto de de asignarle una dirección al enlazador.

Para hacer esto para una variable, se añade extern la palabra clave antes de su nombre:

extern int my_global;

El especificador extern es como decirle al compilador que esperar hasta el tiempo de enlace para resolver la “conexión”. Y para una función, simplemente hay que poner el prototipo de función:

int SomeFunction(int parameter);

Esto es así porque por defecto todas las funciones se consideran extern.

Por supuesto, estas son sólo las declaraciones de que my_global y SomeFunction existen en alguna parte. En realidad, no los creas. Todavía tiene que hacer esto en uno de los archivos de origen, de lo contrario aparecerá un error de vinculador nuevo cuando se descubre que no puede resolver uno de los identificadores a una dirección real. Así que para este ejemplo, tendría que añadir “int my_global” a cualquiera de Code1.cpp o Code2.cpp, y todo debería funcionar bien. Si se trata de una función, deberá añadir la función como su cuerpo (es decir, el código de la función) en uno de los archivos de origen.

La regla aquí es recordar que los archivos de cabecera define una interfaz, no una implementación. En ellas se indica que las funciones, variables y objetos existen, pero no se hace responsable de su creación. Ellos pueden decir lo que una estructura o clase debe contener, pero en realidad no debe crear instancias de esa estructura o clase. Se puede especificar los parámetros de una función y se lo devuelve, pero no cómo se obtiene el resultado. Y así sucesivamente. Esta es la razón por la que la lista de lo que puede entrar en un fichero de cabecera al principio de este artículo es importante.

Hay dos notables excepciones a no incluir el cuerpo de las funciones en los archivos de cabecera:

La primera excepción es la de funciones de la plantilla. La mayoría de los compiladores y enlazadores no puede manejar plantillas se definen en archivos diferentes a la que se utilizan en, por lo que las plantillas casi siempre es necesario definir en un encabezado para que la definición se puede incluir en todos los archivos que necesita para usarlo. Debido a la forma de plantillas se crea una instancia en el código, esto no conduce a los mismos errores que se podrían obtener mediante la definición de una función normal en un encabezado. Esto es así porque las plantillas no se compilan en el lugar de la definición, sino que se compilan, ya que son utilizados por el código en otros lugares.

La segunda excepción es las funciones en línea, mencionó brevemente antes. Una función en línea se ha compilado en el código, en vez de llamadas de la forma habitual. Esto significa que cualquier unidad de traducción que ‘llama “el código de una función en línea tiene que ser capaz de ver el funcionamiento interno (es decir, la puesta en práctica) de esa función con el fin de insertar el código de esa función directamente. Esto significa que un prototipo de función sencilla no es suficiente para llamar a la función en línea, lo que significa que donde quiera que normalmente sólo tiene que utilizar un prototipo de función, se necesita el cuerpo de la función general de una función en línea. Al igual que con las plantillas, esto no causa errores de vinculador como la función en línea no es realmente compilado en el lugar de la definición, pero se inserta en el lugar de la llamada.

Otras consideraciones

Por lo tanto, el código está muy bien dividido en varios archivos, que le da todos los beneficios mencionados en el inicio como la velocidad de compilación de una mayor y mejor organización. ¿Hay algo más que necesitas saber?

En primer lugar, si estás usando la biblioteca estándar de C++ (STL) o cualquier otra biblioteca que utilice espacios de nombres, no utilice la se sentencia using en los archivos de cabecera pues reducirás la utilidad de los espacios de nombres casi por completo, es mejor usar el identificador del espacio de nombre en cada función u objeto que uses, así tienes la ventaja de saber a que biblioteca pertenece ese código y no tendrás problemas de superponer nombres de funciones propias que te hagan rectificar el código.

En segundo lugar, el uso de macros debe ser cuidadosamente controlado. Los programadores de C tienen que confiar en las macros de un montón de funcionalidades, pero los programadores de C++ debe evitarlos siempre que sea posible. Si desea una constante en C++, utilice la palabra clave const. Si desea una función en línea en C++, utilice la palabra clave ‘inline’. Si desea una función que opera sobre los diferentes tipos utilice plantillas o sobrecarga. Pero si necesitas utilizar una macro, por alguna razón, y lo coloca en un fichero de cabecera, trate de no escribir macros que podrían interferir con el código en los archivos que se incluyen. Cuando se producen errores extraños de compilación, no quiero tener que buscar a través de todos los archivos de cabecera para ver si alguien utiliza un #define para cambiar inadvertidamente su función o sus parámetros a otra cosa. Así que siempre que sea posible, mantener las macros de cabeceras a menos que usted puede estar seguro de que no les importa que afecta a todo en el proyecto.

SFML 2: Crear un entorno de trabajo

$
0
0

SFML Logo

Vamos a empezar una serie de artículos dedicados a SFML 2 y como trabajar con esta biblioteca multimedia dedicada al desarrollo de videojuegos 2D. En esta primera entrega vamos a crear un entorno de trabajo con el que poder seguir el resto de los tutoriales y como punto de partida para proyectos propios, ya que se puede ver cómo configurar la biblioteca para su uso.

Trabajando en Windows

Como la versión actual de SFML 2 está en desarrollo el único medio de obtener la última versión es bajando el código fuente y compilandolo para nuestro sistema operativo. El código fuente se puede obtener en el repositorio de GitHub.

Para los usuarios de Windows y Visual Studio 2010 (se puede obtener una versión gratuita de Visual C++ 2010 Express). He creado un proyecto en el que ya se incluye una versión compilada de SFML 2 y un proyecto creado para poder empezar a trabajar.

Simplemente descomprimir la carpeta del archivo y abrir GameSolution2010.sln y veréis el proyecto en el que tenéis un archivo main.cpp con código de prueba. Pulsar F5 para probar que todo funciona correctamente, se debería abrir una ventana de consola y otra ventana con un fondo celeste.

Otros entornos y sistemas operativos

Me es imposible proporcionar un entorno de prueba para cada IDE y sistema, lo mejor es acceder a la página de tutoriales y revisar como obtener un proyecto con Cmake e instalar las bibliotecas en tu sistema.

Si necesitas ayuda con algo en concreto me lo puedes comentar en los comentarios y entre los tutoriales y todos conseguiremos hacerlo funcionar en tu sistema.

En el próximo artículo empezaremos con las nociones básicas de la biblioteca. La idea es escribir una serie de artículos dedicados al manejo básico de SFML 2 y luego realizar un pequeño juego paso a paso usando las funcionalidades de la biblioteca.

En Genbeta Dev | SFML 2, biblioteca para el desarrollo de videojuegos

SFML 2: Crear una ventana

$
0
0

SFML Logo

Una vez preparado el entorno de trabajo como vimos en el artículo anterior es hora de ponerse manos a la obra. Vamos a empezar borrando el código fuente que hay en el archivo main.cpp para empezar de 0 e ir paso a paso. En este artículo el objetivo es obtener el código que se dio de prueba y crear una ventana.

SFML 2, una estructura modular

Una de las claves de SFML es su estructura modular que nos permite usar solo las partes de la biblioteca que nos interesa sin tener que depender de todos los módulos. Veamos la relación que existe entre los módulos de SFML.

Módulo Depende de
System -
Window System
Graphics System, Window
Audio System
Network System

Como vemos todas dependen de System (menos la propia System) y Graphics además depende de Window. Lo bueno que tiene es que al incluir una que depende de otra, ya la otra queda automáticamente incluida, así, al incluir el módulo Graphics este incluye automáticamente a Window y System.

Así que si lo que queremos es trabajar con el modo gráfico de SFML solo debemos hacer lo siguiente.

#include <SFML/Graphics.hpp>

Con solo esta línea ya podemos usar los módulos System, Window y Graphics de SFML. Para usar además el de Audio y Network habría que incluir las siguientes líneas

#include <SFML/Audio.hpp>
#include <SFML/Network.hpp>

Con esas tres líneas tendríamos acceso a toda las funcionalidades que nos aporta SFML.

Creando un programa base

Bien una vez conocida la relación de los módulos de SFML por ahora nos vamos a centrar en el módulo Window que es el encargado de crear la ventana de la aplicación y manejar todos los eventos que suceden en ella. Así que empezamos con el siguiente código.

#include <SFML/Window.hpp>

 int main()
 {
     // Crea una ventana de 640×480×32 con el título SFML window
     sf::Window window(sf::VideoMode(640, 480, 32), “SFML window”);

     // Activa la sincronización vertical (60 fps)
     window.setVerticalSyncEnabled(true);

     // Game Loop mientras la ventana esté abierta
     while (window.isOpen())
     {
         // Creamos un objeto evento
         sf::Event event;
		 // Procesamos la pila de eventos
         while (window.pollEvent(event))
         {
             // Si el evento es de tipo Closed cerramos la ventana
             if (event.type == sf::Event::Closed)
                 window.close();
         }

         // Actualizamos la ventana
         window.display();
     }

     return 0;
 }

Después de incluir el módulo Window (Recuerda ahora podemos usar Window y System) procedemos a crear nuestra función main que ejecuta el programa.

El espacio de nombre sf

Una cosa importante es que todas las clases, funciones y tipos de datos de SFML están incluidos dentro del espacio de nombre sf, así para acceder a cualquier elemento que proporciona SFML hay que anteponer sf::.

Recomiendo encarecidamente no usar la famosa sentencia using namespace sf porque sería reducir a casi nula la funcionalidad de los espacios de nombre que nos permite con un simple vistazo ver a que colección pertenece nuestro elemento y evitar sobreescribirlo con un elemento propio.

Creando una ventana

Una vez aclarado esto lo primero que hay que hacer en un programa SFML es crear una ventana, esto se hace creando un objeto sf::Window como vemos que recibe como parámetros un objeto sf::VideoMode que no es más que una clase que almacena las dimensiones y la profundidad de color de un modo de vídeo, una cadena de caracteres que representa al título de la ventana y como parámetro opcional aceptaría el estilo de la ventana que define si es a pantalla completa, si se puede redimensionar o cerrar. Para ver todos los constructores y funciones que admite un objeto sf::Window lo mejor es irse a la documentación oficial de la clase.

Después de crear la ventana de la aplicación lo siguiente que hacemos es activar la sincronización vertical mediante el método setVerticalSyncEnabled esto lo que hace es que la aplicación vaya a 60 frames por segundo que es la taza de sincronización de los monitores evitando un molesto parpadeo. Si se quiere otra taza de fps se puede usar el método setFramerateLimit, todo está muy bien documentado.

A continuación viene la parte más importante de todo videojuego, el Game Loop es el bucle que se repite constantemente durante la ejecución del juego y que nos permite actualizar y dibujar los elementos del mismo.

En nuestro caso la estructura es un while que se repite mientras la ventana de la aplicación esté abierta, esto se comprueba llamando al método isOpen de la ventana.

Ya dentro del bucle es donde debemos meter todo lo que queremos que se ejecute en cada ciclo del juego. Lo primero que debemos hacer es procesar los eventos.

Controlando los eventos

En SFML los eventos que llegan a la ventana se van almacenando en una pila de la que se van extrayendo para procesarlos mediante el método pollEvent. Este método lo que hace es extraer un evento de la pila y almacenarlo en el objeto sf::Event pasado como parámetro. El método devuelve true si hay elementos en la pila y false en caso contrario. Así para procesar todos los eventos de la pila debería llamarse al método desde un bucle.

 sf::Event event;
 while (window.pollEvent(event))
 {
    // procesar evento...
 }

Como vemos lo que hacemos es crear un objeto sf::Event llamado event al que vamos a ir asignando los objetos que sacamos de la pila en el bucle. Dentro del bucle procesamos el evento.

Tipos de eventos

SFML maneja los siguientes tipos de eventos, casa uno con sus propiedades y parámetros distintos.

  • Closed – Se cierra la ventana.
  • Resized – Se redimensiona la ventana
  • LostFocus – La ventana pierde el foco
  • GainedFocus – La ventana obtiene el foco
  • TextEntered – Entrada de texto.
  • KeyPressed – Tecla pulsada.
  • KeyReleased – Tecla liberada.
  • MouseWheelMoved – Se mueve la rueda del ratón.
  • MouseButtonPressed – Se pulsa botón del ratón.
  • MouseButtonReleased – Se libera botón del ratón
  • MouseMoved – Se mueve el ratón.
  • MouseEntered – El ratón entra en la ventana
  • MouseLeft – El ratón sale de la ventana
  • JoystickButtonPressed – Se presiona un botón del joystick
  • JoystickButtonReleased – Se libera un botón del joystick
  • JoystickMoved – Se mueve el joystick
  • JoystickConnected – Se conecta el joystick
  • JoystickDisconnected – Se desconecta el joystick

Como vemos cada uno tiene unas propiedades diferentes según el tipo de uno puede que tenga sentido que nos de sus coordenadas x, y como la de la posición del ratón y otro la tecla pulsada como KeyPressed. En la documentación de la clase se puede ver con que parámetros cuenta cada evento.

Ahora volvemos a nuestro código en el que comprenderemos el código que viene a continuación, nuestro procesador de eventos. En este caso solo queremos procesar si hay evento de cierre por lo que comprobamos la pila de eventos y si alguno es te cierre mandamos la orden de cerrar la ventana.

Por último en la ultima sentencia llamamos al método display que actualiza la ventana, esto será necesario cuando dibujemos en nuestra ventana ya que es la sentencia que refresca lo dibujado mostrando una nueva imagen.

Conclusión

Ya debemos entender los conceptos básicos de SFML 2, como crear una ventana y cómo procesar eventos. Puedes hacer pruebas comprobando otro tipo de eventos e imprimiendo sus valores por consola para ver como trabajan.

En el próximo artículo veremos como controlar el tiempo en SFML 2, una parte fundamental del desarrollo de videojuegos.

En Genbeta Dev | SFML 2, biblioteca para el desarrollo de videojuegos
En Genbeta Dev | SFML 2: Crear un entorno de trabajo

SFML 2: Controlar el tiempo

$
0
0

SFML Logo

A diferencia de otras bibliotecas en las que el tiempo se representa con un entero en milisegundos o un numero de como flotante en segundos SFML no impone ninguna regla sobre esto y todos los tiempos que maneja la biblioteca los hace a través de la clase sf::Time que nos permite obtener el tiempo en segundos, milisegundos o microsegundos que nos da un gran juego para controlar el tiempo.

Hay que tener en cuenta que sf::Time maneja unidades de tiempo, pero relativamente, no es para obtener fechas es para almacenar una cantidad de tiempo que según el contexto puede significar una cosa u otra.

Creando objetos de Tiempo

Los objetos sf::Time se pueden construir con segundos, milisegundos o microsegundos usando las funciones definidas (no miembros).

// Creamos tres tiempos diferentes
sf::Time t1 = sf::seconds(0.1f);
sf::Time t2 = sf::milliseconds(100);
sf::Time t3 = sf::microseconds(100000);
// Los mostramos en pantalla como segundos
std::cout << t1.asSeconds() << std::endl;
std::cout << t2.asSeconds() << std::endl;
std::cout << t3.asSeconds() << std::endl;

Que tiene como salida por pantalla lo siguiente:

0.1
0.1
0.1

Como vemos da igual como almacenemos el tiempo luego lo podemos recuperar como segundos, milisegundos o microsegundos usando los métodos asSeconds, asMilliseconds y asMicroseconds. Internamente se almacena como un entero de 64 bits por si alguien tiene curiosidad.

Lo bueno de esta clase es que podemos hacer cosas como la siguiente.

sf::Time t2 = t1 * 2;
sf::Time t3 = t1 + t2;
sf::Time t4 = -t3;
bool b1 = (t1 == t2);
bool b2 = (t3 > t4);

Lo que nos da un gran juego para operar y comparar tiempos independientemente de si los tratamos como segundos, milisegundos o microsegundos.

Relojes, midiendo el tiempo

Ahora que tenemos la manera de almacenar y representar el tiempo necesitamos algo para medirlo, de eso se encarga sf::Clock. Esta clase solo tiene dos métodos: getElapsedTime, que devuelve el tiempo pasado desde que se creo o se reinició el reloj, y restart que reinicia el reloj.

sf::Clock clock; // inicia el reloj
/* otro código ... */
sf::Time elapsed1 = clock.getElapsedTime();
std::cout << elapsed1.asSeconds() << std::endl;
clock.restart();
/* otro código ... */
sf::Time elapsed2 = clock.getElapsedTime();
std::cout << elapsed2.asSeconds() << std::endl;

Una operación típica es obtener el tiempo pasado y acto seguido reiniciar el reloj que lo haríamos así:

sf::Clock clock
/* otro código ... */
sf::Time t = clock.getElapsedTime();
clock.restart();

Pero la función restart devuelve también el tiempo pasado antes de reiniciar así que se podría hacer lo siguiente.

sf::Clock clock
/* otro código ... */
sf::Time t = clock.restart();

Conclusiones

Con esto ya sabemos como Almacenar y contar el tiempo en nuestras aplicaciones SFML, en el próximo artículo ya empezaremos a tocar el apartado gráfico de SFML.

En Genbeta Dev | SFML 2, biblioteca para el desarrollo de videojuegos
En Genbeta Dev | SFML 2: Crear un entorno de trabajo
En Genbeta Dev | SFML 2: Crear una ventana


¿Qué es la inteligencia artificial?

$
0
0

SFML Logo

Todos hemos oido hablar de la inteligencia artificial y hemos visto películas como Terminator, Matrix o Yo, Robot en la que la inteligencia artificial se revela contra la humanidad, pero realmente sabemos ¿Qué es la inteligencia artificial?

La respuesta es que no está tan claro como definir la inteligencia artificial. A continuación aparece una tabla con ocho definiciones de inteligencia artificial extraídas de ocho libros de texto diferentes.

Sistemas que piensan como humanos Sistemas que piensan racionalmente
El nuevo y excitante esfuerzo de hacer que los ordenadores piensen… máquinas con mentes, en el más amplio sentido literal. (Haugeland, 1985). El estudio de las facultades mentales mediante el uso de modelos computacionales. (Charniak y McDermotL 1985).
La automatización de actividades que vinculamos con procesos de pensamiento humano, actividades como la toma de decisiones, resolución de problemas, aprendizaje… (Bellman, 1918). El estudio de los cálculos que hacen posible percibir, razonar y actuar. (Winston. 1992).
Sistemas que actúan como humanos Sistemas que actúan racionalmente
El arte de desarrollar máquinas con capacidad para realizar funciones que cuando son realizadas por personas requieren de inteligencia. (Kurzweil, 1990). La Inteligencia Computacional es el estudio del diseño de agentes inteligentes. (Poolc el al.. 1998).
El estudio de cómo lograr que los computadores realicen tareas que, por el momento, los humanos hacen mejor. (Rich y Knight, 1991). IA… está relacionada con conductas inteligentes en artefactos. (Nilsson, 1998).

Las que aparecen en la parte superior se refieren a procesos mentales y al razonamiento, mientras que las de la parte inferior aluden a la conducta. Las definiciones de la izquierda miden el éxito en términos de la fidelidad en la forma de actuar de los humanos, mientras que las de la derecha toman como referencia un concepto ideal de inteligencia, que llamaremos racionalidad. Un sistema es racional si hace “lo correcto”, en función de su conocimiento.

A lo largo de la historia se han seguido los cuatro enfoques mencionados. Como es de esperar, existe un enfrentamiento entre los enfoques centrados en los humanos y los centrados en torno a la racionalidad. El enfoque centrado en el comportamiento humano debe ser una ciencia empírica, que incluya hipótesis y confirmaciones mediante experimentos. El enfoque racional implica una combinación de matemáticas e ingeniería. Cada grupo al mismo tiempo ha ignorado y ha ayudado al otro.

Bueno, y para vosotros ¿Qué es la intelgencia artificial?

SFML 2: Sprites y texturas

$
0
0

SFML Logo

LLegamos al artículo de la serie sobre la biblioteca SFML 2 que muchos estaban esperando, es la hora de abordar el manejo de gráfico de la biblioteca empezando por los sprites y texturas. Veremos las diferencias entre un sprite y una textura y como manejarlos correctamente.

Preparando SFML para usar el modo gráfico

En esta ocasión vamos a empezar usando una ventana de RenderWindow en lugar de una Window es una clase que hereda de esta última y está preparada para dibujar.

Como vemos lo primero es usar el modulo Graphics de SFML a continuación todos es igual con la salvedad que las RenderWindow tienen algunas opciones más que las Window como por ejemplo el método clear que limpia la ventana con el color pasado por parámetro.

Set de recursos

Antes de seguir vamos a descargar la carpeta data y la vamos a descomprimir en la carpeta bin de nuestro proyecto (en la que se encuentra el ejecutable). Esta carpeta contiene un pack de recursos libres para hacer nuestras pruebas.

Texturas y Sprites

En SFML el concepto de textura y Sprite está muy bien hecho y se separa en dos objetos una textura no es más que una imagen cargada en la tarjeta gráfica del ordenador y un sprite usa esta textura pero no es exclusiva de él. Puede haber muchos sprites que usan una misma textura ahorrando tener que cargar varias imagenes desperdiciando memoria. Veamos un ejemplo de como cargar una textura, crear un sprite y manipularlo.

Como vemos solo cargamos una textura, pero creamos dos sprites con ellas. Podemos coger dos trozos diferentes y manipularlas como queramos como podemos ver en el ejemplo.

En Genbeta Dev | Artíclos anteriores de SFML 2
Más información | Documentación de SFML 2

Crear un adivinador

$
0
0

Animales

Seguro que todos hemos vistos muchas aplicaciones y juegos de adivinación las típicas que te dicen que pienses en un objeto o en una persona y en pocas preguntas te adivinan en qué estás pensando y se nos queda una cara de tontos que no es normal. En este artículo vamos a ver por encima como crear un adivinador simple.

Estos juegos se basan en una gran base de datos de la que mediante preguntas que se hacen al jugador se van eliminando opciones hasta que queda una. Suelen tener grandes algoritmos heurísticos para poder acertar incluso con datos erróneos por parte del usuario.

Nosotros vamos a crear un sistema adivinador de animales al que hay que “enseñarle” en un principio nuestro adivinador solo conocerá un animal y preguntará si ese es el animal que estás pensando, si no es así te preguntará en que animal pensabas y en que lo diferencia del que nos ha propuesto, así irá creando un árbol de animales que al cabo de unos cuantos miles de partidas podrá adivinar cualquier animal.

Usaremos el lenguaje de programación Python por su sencillez y usaremos una estructura de árboles para almacenar nuestro adivinador. Empecemos.

Árboles

Un árbol es una estructura de datos compuesta de nodos. Cada nodo está compuesto por una carga y enlaces a sus nodos hijos.

Arbol

Como vemos en este árbol las cargas son números enteros como 65, 58, 79, etc. Los nodos tienen hijos a los que apuntan, por ejemplo, el nodo 65 tiene 2 nodos hijos 58 y 79. 58 a su vez tiene 3 nodos hijos 25, 96 y 12.

Los árboles se componen de un nodo que no tienen padre en nuestro caso es 65 que se llama tronco (por el símil con el árbol). Los nodos hijos que van saliendo de él se llaman ramas hasta llegar a nodos que no tienen nodos hijos que se llaman nodos hojas.

Arboles binarios

Es este artículo vamos a centrarnos en los árboles binarios. Son árboles que tienen solo dos nodos hijos. Se va bifurcando de dos en dos, por los que se les llama binario.

Arbol binario

Como podemos ver en el árbol superior hay dos nodos hijos en cada rama, por tanto es un árbol binario.

Árboles binarios en Python

Vamos a crear este tipo de dato en en lenguaje python:

Podemos ver que recibe tres valores, la carga y dos referencias a sus nodos hijos, en un principio todo definido con None. Para crear un arbol podríamos hacer lo siguiente.

Esto nos crearía el siguiente esquema.

Arbol sin hijos

Podríamos añadirle dos nodos hijos:

Arbol con hijos

Lo anterior se puede resumir en una sola línea de código.

El resultado es el mismo. Como vemos cada llamada a la clase Arbol() solo nos genera un nodo del árbol. Hemos redefinido el método str que sustituye el valor de la sentencia print aplicado al objeto, mostrandonos solo la carga.

Lo más importante para un tipo de dato es tener alguna manera de poder recorrerlo, vamos a implementar una función que recorra todos los nodos del árbol y nos devuelva la suma de las cargas de cada nodo.

Nuestra función suma recibe el nodo padre como parámetro. Tiene la condición base de que si el árbol vale None (esta vacío) pues su valor es 0 si no se cumple se llama recursivamente para calcular el valor de sus hijos más la carga cuando alcanza un nodo que vale None retornará 0 y saldrá de la recursividad.

Los árboles binarios sirven para ir eligiendo entre opciones que se van ramificando. Es útil para comportamientos y toma decisiones de una posible I.A.

Adivinador de animales

Ahora que sabemos como crear estructura de árboles con Python vamos a crear una Inteligencia Artificial que tratará de adivinar en que animal estamos pensando. El programa irá aprendiendo a medida que jugamos e irá conociendo más animales. Lo que vamos a hacer es crear una estructura árbol que se vaya ramificando poco a poco.

Veamos un ejemplo ejecución del programa para que veáis como funciona.

Estas pensando en un animal? s
Es un pajaro? n
Qué animal era? Raton
Qué diferencia a un pajaro de un Raton? vuela
Si el animal fuera un pajaro cual seria la respuesta? s
Estas pensando en un animal? s
vuela? s
Es un pajaro? n
Qué animal era? Aguila
Qué diferencia a un pajaro de un Aguila? caza
Si el animal fuera un pajaro cual seria la respuesta? n
Estas pensando en un animal? s
vuela? n
Es un Raton? n
Qué animal era? Perro
Qué diferencia a un Raton de un Perro? Ladra
Si el animal fuera un Raton cual seria la respuesta? n
Estas pensando en un animal? s
vuela? n
Ladra? n
Es un Raton? n
Qué animal era? Rinoceronte
Qué diferencia a un Raton de un Rinoceronte? tiene colmillos
Si el animal fuera un Raton cual seria la respuesta? n
Estas pensando en un animal? s
vuela? s
caza? n
Es un pajaro? n
Qué animal era? Buitre
Qué diferencia a un pajaro de un Buitre? come carroña
Si el animal fuera un pajaro cual seria la respuesta? n
Estas pensando en un animal? s
vuela? n
Ladra? n
tiene colmillos? s
Es un Rinoceronte? n
Qué animal era? Elefante
Qué diferencia a un Rinoceronte de un Elefante? tienen trompa
Si el animal fuera un Rinoceronte cual seria la respuesta? n
Estas pensando en un animal? s
vuela? n
Ladra? n
tiene colmillos? s
tienen trompa? n
Es un Rinoceronte? n
Qué animal era? Jabali
Qué diferencia a un Rinoceronte de un Jabali? vive en el agua
Si el animal fuera un Rinoceronte cual seria la respuesta? s
Estas pensando en un animal? s
vuela? n
Ladra? n
tiene colmillos? n
Es un Raton? n
Qué animal era? Jirafa
Qué diferencia a un Raton de un Jirafa? tiene el cuello largo
Si el animal fuera un Raton cual seria la respuesta? n
Estas pensando en un animal? s
vuela? s
caza? n
come carroña? n
Es un pajaro? n
Qué animal era? Mosca
Qué diferencia a un pajaro de un Mosca? es un insecto
Si el animal fuera un pajaro cual seria la respuesta? n
Estas pensando en un animal? s
vuela? n
Ladra? n
tiene colmillos? s
tienen trompa? n
vive en el agua? s
Es un Rinoceronte? s
Soy el más grande!

Como se puede ver inicialmente el programa no “sabe” nada, solo sabe el nodo inicial que aleatoriamente hemos puesto que sea pájaro y pregunta si el animas es un pájaro. Si se le dice que no preguntará en que animal se estaba pensando y te pedirá una diferencia con el que te ha sugerido él para almacenarlo en su árbol. Así ha medida que se va jugando e introduciendo animales el programa va conociendo más ya que su árbol se va ramificando y creciendo en animales.

Veamos otra ejecución del programa más corta para poder analizarla.

Está pensando en un animal? s
Es un pájaro? n
Cómo se llama el animal? perro
Qué pregunta distinguiría a un perro de un pájaro? Puede volar
Si el animal fuera un perro, cuál sería la respuesta? n
Estás pensando en un animal? s
Puede volar? n
Es un perro? n
Cómo se llama el animal? gato
Qué pregunta distinguiría a un gato de un perro? Ladra
Si el animal fuera un gato, cuál sería la respuesta? n
Estás pensando en un animal? s
Puede volar? n
Ladra? s
Es un perro? s
Soy el más grande!

Generaría el siguiente árbol binario.

diagrama

Como podermos ver según la información que le damos va generando un árbol. Al principio de cada juego se empieza en lo más alto del árbol (tronco) y se hace la pregunta que contiene el nodo, cuando llega a un nodo hoja trata de adivinar. Hay que darse cuenta que todos los animales son nodos hojas y todas las preguntas son nodos ramas obvio porque solo las preguntas tienen o una respuesta u otra pregunta y los animales son estados finales. Si falla pide el a usuario el nombre del nuevo animal y una diferencia con el intento fallido, entonces añade el nuevo animal y la nueva pregunta al árbol.

Aquí está el código completo del programa.

La función si() devuelve verdadero si se ha escrito S o s y falso si se ha escrito otra cosa. Es esta función la que interactúa con el usuario.

La condición del bucle externo es 1, por lo que seguirá hasta que se encuentre la sentencia break cuando el usuario decida que ya ni piensa en ningún animal. El bucle while interno recorre el árbol guiado por las respuestas que va dando el usuario. Cuando se añade un nuevo nodo al árbol, la pregunta sustituye a la carga y los dos hijos son el nuevo animal y la carga original del nodo que ahora es la pregunta.

Una carencia de esta implementación es que, al salir, ¡olvida todo lo que le habías enseñado con tanto cuidado! Esto se podría soluciona almacenando la estructura del árbol en algún archivo o base de datos.

Este código tan simple si se le implementa una base de datos online que vaya haciendo crecer el árbol en cada juego de los usuarios. Al cabo de un tiempo, después de muchos juegos sería capaz de adivinar cualquier animal. Sería necesario añadir algo de heurística para evitar animales repetidos y demás, pero se sale de la intención del artículo.

Artículo basado en el capítulo de Árboles del libro Aprende a Pensar como un Programador con Python [pdf].

Breve repaso de la Programación Orientada a Objetos con C++

$
0
0

Lenguajes de Programación

En este artículo vamos a hacer un breve repaso de la programación orientada a objetos con C++, pero que es aplicable a cualquier lenguaje orientado a objetos. Muchos desarrollamos software orientado a objetos, pero muchas veces olvidamos los principios básicos de este metodología de programación y viene bien recordarlos para hacer un buen diseño de software.

Clases y Objetos

Una clase es un conjunto de atributos (datos) y comportamientos (métodos) que juntas forman un todo útil y significativo. Una clase es una especificación que describe cómo las instancias individuales de la clase, conocidos como objetos, se debe construir. Por ejemplo, su mascota Rover es una instancia de la clase “perro”. Así, hay una relación de uno a varios entre una clase y sus instancias.

Encapsulación

Encapsulación significa que un objeto presenta sólo una interfaz limitada con el mundo exterior, los detalles del estado interno del objeto y la implementación quedan ocultos. Esto es bueno para el usuario de la clase que solo debe conocer esta interfaz para usarla y no como está implementada dicha clase. También permite al programador que escribió la clase asegurar que las instancias de la misma tengan un estado consistencia lógica

Herencia

La herencia permite que una nueva clase se defina como una extensión de una clase preexistente. La nueva clase modifica o extiende los datos, la interfaz y/o el comportamiento de una clase existente. Si la clase Hija extiende de la clase Padre se dice que Hija hereda o deriva de la clase Padre. En esta relación a la clase Padre se le conoce como clase base o superclase, y la clase Hija como clase derivada o subclase. Evidentemente la herencia conduce a la jerarquía (forma de árbol) con varias relaciones entre clases.

Herencia crea una “es-un” entre clases. Por ejemplo, un círculo es un tipo de Figura. Así que si estuviera escribiendo una aplicación de dibujo 2D, probablemente tendría sentido para derivar la clase Circulo de una clase base llamada Figura.

Podemos dibujar diagramas de jerarquías de clases utilizando las convenciones definidas en el Lenguaje de Modelado Unificado (UML). En esta notación, un rectángulo representa una clase, y una flecha con una cabeza triangular hueco representa la herencia. La flecha va desde las clases hija a la clase Padre. Véase la figura para ver un ejemplo de una jerarquía de clases sencilla representado como un diagrama de clases UML estático.

Herencia

Herencia múltiple

Algunos lenguajes de programación sportan herencia múltiple (HM), lo que significa que una clase puede tener más de una clase padre. En teoría la HM puede ser muy elegante, pero en la práctica este tipo de diseño por lo general da lugar a una gran confusión y dificultades técnicas (véase Herencia múltiple). Esto es debido a que la herencia múltiple transforma un simple árbos de clases en un grafo potencialmente complejo. Un grafo de clases será de todo menos un simple árbol, por ejemplo, un diamante mortal (http://es.wikipedia.org/wiki/Problema_del_diamante), en el que la clase derivada termina con dos copias de la clase base abuelo (ver figura). En C++, la herencia virtual permite evitar la duplicación de los datos de los abuelos.

Herencia Múltiple

La mayoría de desarrolladores de C++ evita la herencia múltiple por completo o solo la permiten de una forma muy limitada. Una regla común es solo permitir clases simple, sin padres, que se multipliquen en la clase heredera, dicho de otra manera solo una jerarquía de herencia con añadido de clases simples. Estas clases simples se denominan mix-in ya que pueden ser utilizadas para añadir una funcionalidad en un punto arbitrário del árbol de clase. Ver la figura para ver como la clase Animator solo es un añadido a la jerarquía establecida.

Herencia mix

Polimorfismo

El poliformismo es una característica que permite que una colección de objetos diferentes puedan ser manipulados a través de una interfaz común única. La interfaz común hace que una colección hetereogénea de objetos pueda parecer homogénea, desde el punto de vista del código mediante el uso de una interfaz.

Por ejemplo, un programa de dibujo 2D se podría dar una lista de varias formas para dibujar en la pantalla. Una forma de sacar esta colección heterogénea de formas es utilizar una sentencia switch para realizar comandos diferentes de dibujo para cada tipo distinto de la forma.

El problema de este método es que drawShape() necesita saber como dibujar cada tipo de figura. Si añadiéramos un tipo de figura nueva tendríamos que modificar este método para que aceptara el nuevo tipo de figura.

La solución es el polimorfismo, consiste en declarar, dentro de la clase Shape una función virtual pura, así cada clase derivada tiene que encargarse de implementar la función Draw() con este método al llamar al método Draw() de Shape lo que hace es llamar a la implementación de la clase hija, y esta no necesita conocer como está implementada dicha función.

Estas son las características más destacadas de la programación orientada a objetos y siempre viene bien recordarlas porque muchas veces las olvidamos a la hora de diseñar software complicandonos mucho la vida.

10 sitios que todo programador de videojuegos debería conocer

$
0
0

Cabecera

Todo programador en general y el programador de videojuegos en particular deberías estar siempre al tanto de las últimas novedades. Si te dedicas a programar videojuegos o tienes intención de hacerlo corre a añadir estos 10 sitios que todo programador de videojuegos debería conocer a tus favoritos.

GameDev

GameDev

La web por excelencia que todo desarrollador de videojuegos debería visitar diariamente. Sus artículos en los que hay casi de todo y su foro donde puedes encontrar a gente con muchísima experiencia son su mayor fuerte.

Enlacehttp://www.gamedev.net

Gamasutra

Otra de las más grandes con artículos escritos por verdaderos profesionales de la industria y que no solo se centra en la programación si no en todas las áreas del desarrollo de videojuegs: Diseño, programación, audio, arte, producción y marketing.

Gamasutra

Enlacehttp://www.gamasutra.com

GPwiki

GPwiki

Una gran idea eso es GPwiki, una wiki donde se pueden encontrar artículos relacionados con la programación de videojuegos todos muy bien ordenados y categorizados. Además han añadido un gran foro que le da mucho más valor a la comunidad.

Enlace | http://www.gpwiki.org/

#AltDevBlogADay

AltDevBlogDay

Un blog que es un tesoro. Es un blog comunitario en el que escribe mucha gente muchos de ellos verdaderos genios de grandes compañías. Puedes encontrar de todo desde reflexiones acerca de la industria en general hasta artículos técnicos específicos de alguna tecnología.

Enlacehttp://www.altdevblogaday.com/

Stratos

Stratos

La única de la lista en español, pero que se encuentra aquí por méritos propios. Como dice su eslogan “Punto de encuentro para desarrolladores” en la comunidad puedes ver las últimas ofertas de trabajo en la industria, proyectos amateurs en los que colaborar y un foro repleto de expertos donde puedes exponer tus dudas que te ayudaran o presentar tus proyectos.

Enlace | http://stratos-ad.com/

Gamedev tuts+

Gamedev tuts

Perteneciente a la saga de blogs tuts+ podemos encontrar muy buenos artículos sobre desarrollo de videojuegos muchos de ellos vienen explicados gráficamente con ejemplos en javascript que se pueden ver en la propia web.

Enlace | http://gamedev.tutsplus.com/

DevMag

DevMag

Desarrollado como rama independiente de una revista sobre desarrollo de videojuegos sudafricana este blog cuenta con muy buenos artículos sobre el desarrollo de videojuegos en general no solo centrado en la programación.

Enlacehttp://devmag.org.za/

AiGameDev

AiGameDev

El blog por excelencia de la desarrolladores de inteligencia artificial. Podemos encontrar artículos de todos los tipos y niveles explcando técnicas de IA modernas. Lo parte negativa es que la mayor parte del contenido es de pago, pero aún así tienen muchos artículos gratuitos de primera calidad.

Enlacehttp://aigamedev.com/

OpenGameArt

OpenGameArt

Una gran idea eso es OpenGameArt, una comunidad de recursos para videojuegos libres. Puedes encontrar tanto gráficos 2D como modelos 3D, sonidos, música, etc. y todo ello con licencias libres para que los uses en tus proyectos ¿no suena genial? Pues mejor es ver que hay recursos de primerísima calidad y totalmente disponibles.

Enlacehttp://opengameart.org/

2D Game Art For Programmers

2D Game Art For Programmers

La web SOS para los programadores negados para los gráficos como yo. Por medio del programa inkscape te enseñas a hacer gráficos sencillos para prototipos rápidos. Completar con este artículo de SionDream se pueden conseguir resultados espectaculares sin tener mucha idea de graficos.

Enlacehttp://2dgameartforprogrammers.blogspot.co.uk/

Seguro que conoces alguna más que no está en la lista y que merecería estarlo, ¡Compártela con nosotros en los comentarios!

10 tips de programación de videojuegos

$
0
0

Esquema

Son muchas las cosas que debemos tener en cuenta los programadores para hacer buen código y aplicaciones eficientes, los programadores de videojuegos y aplicaciones en tiempo real debemos hacer todas ellas y alguna más ya que siempre debemos pensar en optimizar lo máximo posible. Aquí os traigo una lista de 10 tipis de programación de videojuegos que considero importante tener en cuenta.

1. Programa limpio

Es la regla de oro de todo programador, y es la más importante, también para el programador de videojuegos. Usar nombres de variables descriptivos siempre. Evitar cosas como:

int pxj1;
int pxj2;
int pye1;

y usar en su lugar variables más claras:

int jugador1_pos_x;
int jugador2_pos_x
int enemigo1_pos_y;

Cuando lleves más de diez mil líneas de código o lleves unas semanas sin tocar esa parte del código agradecerás haber sido claro.

2. No optimices hasta el final

Espera, ¿Este artículo no trataba de reglas para optimizar? Sí, pero optimiza solo cuando funcione y este terminado, si empiezas a optimizar antes de acabar tendrás un código criptográfico que será muy difícil de corregir o modificar. Así que solo optimiza cuando ya esté todo listo y probado.

3. Ten en cuenta la arquitectura RISC

Lo más probable es que el procesador de tu ordenador esté hecho con arquitectura RISC (Reduced Instruction Set Computer). Esto es que a tu ordenador le gustas las instrucciones simples y no las complejas. Así que por escribir algo como esto:

if( mapa[ jugador.x >> 5 + 4 ][ jugador.y >> 2 + 6 ] == '0' ){ ... }

significa que el procesador vaya a ejecutarlo en un solo ciclo al contrario como el procesador solo recibe instrucciones cortas y simples al compilador le costará más trabajo simplificarlo para dárselo listo al procesador, produciendo un código binario más lento.

4. Usa algoritmos eficientes

Si usas un algoritmo lento tu programa será lento, ya puedes implementarlo entero en lenguaje ensamblador que si tienes un algoritmo de orden O(n2) tu programa irá lento. Así que más importante que el lenguaje es que uses el algoritmo correcto siempre. Hay muy buenos libros de análisis algoritmos ¡úsalos!.

5. Cuidado con C++ y el Polimorfismo

C++ es un gran lenguaje para programar videojuegos te da toda la potencia del bajo nivel de C para optimizar y la programación orientada a objetos que encaja a la perfección con los videojuegos, pero si estas usando C++ y empiezas a usar herencia y polimorfismo con funciones virtuales de forma indiscriminada la velocidad puede caer en picado ya que son procesos que se realizan en tiempo de ejecución, el polimorfismo es un gran invento, pero úsalo adecuadamente. A veces problemas polimórficos se resuelven con uso de templates (plantillas) mejorando mucho la velocidad.

6. Usa funciones inline con cuidado

Cuando usas funciones inline lo que estás haciendo es decirle al compilador que analice dicha función y si es posible sustituya el contenido en donde se hizo la llamada evitando el salto a la función, pero una cosa es que tú lo digas y otra que el compilador lo haga aunque declares todas las funciones de esta manera el compilador analizará cual son las idóneas para ello. Así que no te fíes de que la funciones inline lo serán.

7. Funciones trigonométricas

A menudo en los juegos tendrás que usar cálculos trigonométricos para muchas cosas, pero esto consume una cantidad ingente de tiempo que nos es muy preciado. Es muy ineficiente calcular algo como esto:

x = cos(m)*fuerza;

Así que una solución es al iniciar precalcular los valores trigonométricos que vayas a usar durante el mismo o al menos los más usados y guardarlos en un array y luego simplemente usar esos valores cuando sea necesario:

x = coseno[m]*fuerza;

8. Usa los operadores de bits

Operar con los operadores de multiplicación y división (*, /) consume muchos ciclos de procesador, aunque en los procesadores modernos ya no tanto, pero en las operaciones muy críticas es conveniente evitarlas así que usa solución es usar los operadores de bits de recorrido que mueven a la derecha o a la izquierda (>>, <<) los cuales dividen y multiplican por potencias de 2, es decir:

y = ( y << 3 ); // es lo mismo que y = y*2^3 o en otras palabras y=y*8
y = ( y >> 3 ); // es lo mismo que y = y/2^3 o en otras palabras y=y/8

Si necesitas hacer una división o multiplicación por un número que no es una potencia de 2 puedes dividirlo en dos partes y sumarlos, por ejemplo, si quieres multiplicar por 74:

y = (y << 6) + (y << 3); // osea y = y*26 + y*23 -> y = y*64 + y*8 -> y = y*72

Sí, esto sigue siendo más eficiente que poner directamente y *= 64 ¿mola no?.

9. Evita hacer casts entre int y float

Convertir entre números enteros y de como flotante es una práctica muy habitual, pero para representar el punto flotante en binario se interpreta como una descripción de la forma -25×10^-2 que en binario se ve:

1-1111110-001000000000000000000000000000000000000000000000000000000

donde el primer bit es el signo, los 7 siguientes el exponente y los 56 bits restantes es la mantisa que es el número en sí. ¿Cuántos ciclos crees que le llevará a tu procesador convertir eso en un entero? pues sí, muchos. Así que trata de evitar las conversiones de tipo entre int y float siempre que sea posible.

10. Recorre los arrays grandes con punteros

Muchas veces dentro del desarrollo de videojuegos se tienen que recorrer grandes arrays de datos para procesarlos. Una forma de optimizar esto es usando punteros en lugar de índices, por ejemplo, en un sistema de partículas si tuviéramos:

Particula mi_array[ 21000 ];
for(int i = 0; i < 21000; i++){
mi_array[i].x = .... //cualquier cosa
mi_array[i].y = ....// cualquier cosa
...
}

Se estarían haciendo muchos cálculos para acceder a posiciones específicas dentro del array. Una forma más eficiente sería usando aritmética de punteros:

Particula mi_array[ 21000 ];
Partícula *particula_actual = mi_array;
Particula *fin = &mi_array[ 21000 ];
while(particula_actual++ < fin ){
particula_actual->x = ....// cualquier cosa
particula_actual->y = ....// cualquier cosa
....
}

La informática, expulsada del ámbito de las ingenierías

$
0
0

Anteproyecto

Se ha publicado borrador bastante definitivo del Anteproyecto de Ley de Servicios Profesionales que trae noticias muy malas para los ingenieros informáticos: no se nos incluye en el marco legal de las ingenierías. El efecto que esto podría causar es la total desaparición de la ingeniería informática dado que sus profesionales no podrían ejercer legalmente ninguna actividad, quedando excluidos del marco jurídico establecido.

También los estudios de grado y máster en informática quedarían excluidos del marco normativo de los ingenierías lo que llevaría a su posible desaparición ya que serían los únicos estudios de ingeniería que no tendrían una profesión.

Lo más grave es que los ingenieros técnicos en informática españoles no podremos solicitar la tarjeta profesional de la Unión Europea, al no estar reconocidas en España nuestras cualificaciones, lo que impedirá nuestra movilidad profesional en el territorio europeo.

vía | José Antonio Serrano en Linkedin
Más información | Ante proyecto de ley de servicios profesionales


No habrá colegio de informáticos, ¿Como afectará a la carrera de informática?

$
0
0

Titulo

Ayer dábamos la noticia de un borrador de ley de los servicios profesionales confirma que finalmente no habrá colegio de informáticos. La idea es ir haciendo desaparecer paulatinamente todos los colegios para adaptarse al marco europeo e igualar las competencias.

El problema está que mientras se niega colegiar la informática apenas se hace nada para disminuir el poder de otros colegios dejándonos a los informáticos en una clara desventaja al no poder nosotros firmar proyectos que contengan otras disciplinas, pero permitiendo que cualquier pueda firmar un proyecto en el que haya ingeniería informática de por medio.

Hasta ahora no ha habido colegio de informáticos, pero se luchaba por la esperanza de conseguir uno con la nueva reforma que se viene parece que es ya definitivo que no tendremos, esto puede hacer que futuros estudiantes se planteen sin estudiar informática o una carrera en el que su título sea reconocido y necesario para la sociedad.

Puede que muchos opten por hacer telecomunicaciones o entrar en un módulo ya que aparte de los conocimientos extras que se pueden conseguir en cursar la ingeniería no merecerán la pena para muchos ya que cualquiera con una formación diferente podrá desarrollar su trabajo.

La opinión de muchos es que no importa el título sino que sepas hacer las cosas para que te contraten, estamos de acuerdo, pero entonces ¿Por qué los informáticos necesitamos la firma para hacer proyectos de otras carreras colegiadas?

SFML 2: Fuentes y textos

$
0
0

SFML Logo

En este artículo veremos como tratar fuentes y textos en SFML 2. Las fuentes y los textos funcionan de forma parecida a las texturas y los sprites que vimos en el artículo anterior.

Las fuentes tipográfica es el recurso que se debe cargar desde una memoria secundaria (Disco duro, pendrive, etc.) y el texto es un objeto que tiene la propiedad de ser dibujable y transformable al igual que los sprites. Aparte de las características de estos los textos añaden otras como el tamaño de los caracteres, uso de strings y más añadidos propios de un texto. Lo mejor siempre es ver un ejemplo de código comentado paso a paso.

Como vemos cargamos una sola fuente y creamos dos textos diferentes con ella. Atención especial a como podemos construirlo de varias maneras en el primero creamos el objeto texto y luego le asignamos a través de sus métodos una cadena, un tamaño y una fuente. Por el contrario en el segundo aprovechamos el constructor para definir estos parámetros.

Este sería el resultado de nuestro programa:

SFML Texto

En Genbeta Dev | Artíclos anteriores de SFML 2
Más información | Documentación de SFML 2

Punteros y referencias

$
0
0

RAM

Muchos novatos o gente que viene de lenguajes de alto nivel no quieren ni oír hablar de C/C++. se oyen cosas como que es muy complicado porque hay que manejar la memoria a bajo nivel, no tiene recolector de basura, etc. Vamos a intentar explicar claramente el manejo de memoria en C/C++ y los temidos punteros y referencias.

La memoria principal

Como sabemos cuando declaramos una variable lo que estamos haciendo es reservar una zona de memoria, imaginemos que la memoria es una tabla con muchas celdas:

Memoria

Imaginemos que cada celda de memoria puede ser ocupada por un dato. En realidad según el tipo dato se precisan más o menos casillas, pero para entender el concepto imaginemos que en cada casilla se puede almacenar un dato. Nuestra memoria ficticia está compuesta de mil casillas numeradas desde el 0 al 999. Ésta es la dirección de memoria de cada casilla, su identificador único, como pudieran ser en la vida real una dirección de una calle, un número y un piso.

Si nosotros hacemos algo como esto en nuestro código:

int x = 4;

Lo que estamos haciendo es reservar una zona de memoria para una variable x y dándole el valor 4. Imaginemos que en nuestra memoria ficticia se almacena en la posición 998, se vería así.

Variable

Bien, en C++ hay un operador para obtener la dirección de memoria de una variable y es & si hiciéramos algo como imprimir el valor de la dirección de memoria:

int x = 4;
cout << "Valor de x: " << x << endl;
cout << "Direccion de x: " << &x << endl;

Nos saldría por pantalla:

Valor de x: 4
Direccion de x: 998

Realmente no te saldrá un número entero sino que será un número hexadecimal bastante más largo, pero hay que tener bastante claro que ni el número hexadecimal que te de el compilador ni el 998 son números enteros es una dirección de memoria un tipo de dato como cualquier otro.

Punteros

Como es un tipo de dato las direcciones de memoria se pueden almacenar en variables. Variables del tipo que almacenan direcciones de memoria que son los llamados punteros. Por tanto un puntero es tan solo un tipo de datos como int almacena enteros, char caracteres o float almacena números de como flotante pues los punteros almacenan direcciones de memoria.

La clave está en que no hay una palabra clave, valga la redundancia, para definir a los tipos punteros sino que según sea el tipo de dirección que va a almacenar ese sera el tipo añadiendo el operador *.

Por ejemplo si yo quiero crear un puntero que almacene una dirección de memoria de un entero debo hacer lo siguiente:

int *px;

con esto estoy declarando una variable que puede almacenar la dirección de una variable de tipo int. Podriamos entonces asignarle una dirección de memoria mediante el operador que vimos antes.

int x = 4;
int *px = &x;

Imaginemos que la variable px se crea en la posición 2 de nuestra memoria ficticia. Nos quedaría el siguiente esquema:

Puntero

Como vemos ocupa una posición como una variable normal, pero en lugar de contener un entero como 4 contiene una dirección de memoria (998). Como posición de memoria que ocupa también tiene su dirección de memoria que se puede obtener con el operador & es lo que se llama un puntero de puntero.

Estos se declaran igual que los demás anteponiendo el operador *:

int x = 4;
int *px = &x;
int **ppx = &px // es un puntero de puntero por lo que solo puede guardar direcciones de punteros de tipo int

Siguiendo con el ejemplo de nuestra memoria ficticia supongamos que la nueva variable ppx se crea en la posición 997. Este sería el equema resultante.

Puntero de puntero

El operador de indirección

Bien, ya sabemos almacenar en memoria direcciones de memoria de otras variables ya sean variables enteras o de cualquier tipo como punteros de las mismas (punteros de punteros). Ahora necesitamos algo para poder usar esas direcciones de memoria, poder usarlas para acceder al contenido original, esto se hace a través del operador de indirección * y no, no me he equivocado es el mismo que se usa para declarar los punteros es lo que tiene C y por tanto C++ que reutiliza mucho los operadores y basa su significado en el contexto. Veamos un ejemplo de uso.

int x = 4;
int *px = &x;
cout << "Valor de x: " << x << endl;
cout << "Valor de x: " *px << endl;

Esto mostrará el siguiente resultado:

Valor de x: 4
Valor de x: 4

Como vemos anteponer * a una variable puntero nos devuelve el contenido de la dirección que contiene. Lo mismo sucede con los punteros de punteros:

int x = 4;
int *px = &x;
int **ppx = &px;
cout << "Valor de x: " << x << endl;
cout << "Valor de x: " << *px << endl;
cout << "Valor de x: " << **ppx << endl;

La última sentencia el asterisco más pegado a la variable devuelve la que contiene la dirección de ppx que es px y el siguiente asterisco devuelve lo que contiene la dirección de px que es x y por tanto 4.

Valor de x: 4
Valor de x: 4
Valor de x: 4

Paso por valor o por referencia

Una de las cosas más útiles de los punteros es el paso por referencia en lugar del paso por valor. Vamos a explicar como funciona esto en C/C++.

Ámbito de variables

Lo primero es entender bien lo que es el ámbito de las variables, una variable solo existe en la función en la que se crea y nada más, a través de los parámetros podemos pasar el valor de una determinada variable a otra función, pero lo que pasamos es el valor y no la variable en sí. Veamos un ejemplo.

#include <iostream>
void sumar(int);
int main()
{
	int n = 4;
	sumar(n);
	std::cout << n << std::endl;
	system("pause");
	return 0;
}
void sumar(int x)
{
	x++;
	std::cout << x << std::endl;
}

Tenemos la función main que contiene la variable n, esta variable solo existe en este contexto al llamar a la función sumar que tiene como parámetro la variable x lo que hacemos es darle a ese valor del parámetro x el valor de n, pero es la única relación que hay entre n y x por tanto la salida de nuestro programa es la siguiente:

5
4

La variable x que valía 4 por el valor que le dio n es aumentada en una unidad y se muestra en pantalla, se sale de la función sumar y se vuelve a la función main que muestra el valor de n que sigue siendo 4.

Paso por referencia

Hay veces que nos interesa que una función modifique una variable que no pertenece a ella, es decir, fuera de su ámbito. En C/C++ es imposible pasar una variable por referencia como en otros lenguajes y hay que hacerlo a través de punteros (o referencias como veremos más adelantes). La idea es que como solo se puede pasar el valor de una variable a una función lo que hacemos es pasar la dirección de una variable a través de un parámetro de puntero y luego con el operador de indirección podemos acceder al contenido de la variable original.

#include <iostream>
void sumar(int *);
int main()
{
	int n = 4;
	sumar(&n);
	std::cout << n << std::endl;
	system("pause");
	return 0;
}
void sumar(int *x)
{
	*x = *x + 1;
	std::cout << *x << std::endl;
}

Como vemos el parámetro de sumar ahora es un puntero que recibe una dirección de memoria en este caso le pasamos la dirección de la variable x y dentro de la función con operador de indirección podemos acceder al contenido de la variable n.

Referencias

Además de los punteros heredados de C el lenguaje C++ añadió una nueva característica que son las referencias, una referencia es por así decirlo un alias o etiqueta de una variable.

Las referencias se deben inicializar al declararse ya que no son en sí una variable sino una etiqueta de otra y se declaran poniendo el operador & después del tipo de dato.

int n = 4;
int &ref_n = n;
std::cout << ref_n << std::endl;

A efectos prácticos n y ref_n se refieren a la misma variable de hecho si con el operador & obtenemos la dirección de memoria de n y ref_n obtendríamos la misma en ambos casos. En nuestra memoria ficticia se vería así:

Referencia

Paso por referencia… con referencias

Las referencias son una buena forma de pasar un valor a otra función sin ser por valor, veamos el ejemplo anterior de paso por punteros, pero está vez usando referencias.

#include <iostream>
void sumar(int &);
int main()
{
	int n = 4;
	sumar(n);
	std::cout << n << std::endl;
	system("pause");
	return 0;
}
void sumar(int &x)
{
	x = x + 1;
	std::cout << x << std::endl;
}

Espero que este artículo ayude a todos los que tengas dudas aún sobre como usar los punteros y las referencias de C/C++.

SFML 2: Sonidos y música

$
0
0

SFML Logo

En esta ocasión vamos a repasarlos conceptos de la biblioteca SFML 2 dedicados al menejo del sonido y de la música. Trataremos las diferencias entre uno y otro y como se manejas desde SFML 2.

Sonidos

Un sonido SFML lo interpreta como algo que “suena” en un determinado momento como un choque, el disparo de un proyectil o unos pasos. Los sonidos funcionan de la misma manera que las texturas y los sprites y las fuentes y los textos. Se carga un recurso en este caso un SoundBuffer y se crea un objeto que los usa un Sound, pueden existir muchos Sound que usen el mismo SoundBuffer a diferente volumen, frecuencia etc.

Música

Lá música al contrario de los sonidos están pensada para sonar a lo largo del tiempo y no como algo puntal, al contrario que los sonidos no se carga en memoria sino que se reproduce a través de stream desde el fichero en la memoria secundaria.

Como siempre lo mejor es ver un ejemplo de uso.

Al ejecutar empezará a sonar la música y al pulsar la barra espaciadora sonará nuestro sonido.

Con esto terminamos el repaso general al SFML 2, en el próximo artículo crearemos el juego del Pong entero para poner en práctica lo visto en estos artículos.

En Genbeta Dev | Artículos anteriores de SFML 2
Más información | Documentación de SFML 2

Curso expertos en desarrollo de videojuegos

$
0
0

Curso Expertos Desarrollo de Videojuegos

La Universidad de Castilla la Mancha ha liberado el material de su Curso Universitario de Experto en Desarrollo de Videojuegos. Cuatro libros completos y todo el código fuente de los mismos en el que se explican los fundamentos del desarrollo de videojuegos.

La parte práctica del curso se plantea como una aproximación multiplataforma al desarrollo de videojuegos, empleando herramientas y estándares libres multiplataforma.

El material consta de 4 libros/módulos:

  1. Arquitectura del Motor. Se estudian los aspectos básicos del diseño de un motor de videojuegos, técnicas básicas de programación y patrones de diseño orientado a videojuegos.
  2. Programación Gráfica. Este segundo módulo se centra en algoritmos gráficos y técnicas de dibujado eficiente.
  3. Técnicas Avanzadas. En este libro se recogen técnicas más avanzadas como estructuras de datos específicas, pruebas de validación y física.
  4. Desarrollo de Componentes. El último módulo está dedicado a componentes específicos de los videojuegos, como la Inteligencia Artificial, Networking, Sonido, multimedia y técnicas avanzadas de Interacción.

Todos se pueden descargar libremente desde la página del curso.

Más información | Expertos en Desarrollo de Videojuegos

Viewing all 92 articles
Browse latest View live