Tabla de contenidos

Prólogo: La historia de fondo

Hace poco, un amigo y mentor y yo estábamos hablando de los tutoriales que escribo para este sitio. Yo estaba hablando de lo que siento que debería escribir un tutorial que más ofertas con secuencias de comandos y menos con los componentes; y cómo no podía decidir cuál sería el concepto para el tutorial. Fuimos ida y vuelta con ideas para unas horas con los pros y los contras de cada uno. A continuación, me recordó un momento en que él me instruyó en un motor de Pavimentación y estaba explicando cómo los algoritmos no eran tan difícil como pensaba.
De repente, me golpeó como una estampida. Debería hacer un tutorial en la fabricación de un sistema de baldosa de mapa de trabajo al explicar los conceptos de edición, las ideas detrás del código y formas en las que podría extenderse durante un juego de aventura de arriba hacia abajo.
No se pierda! extremos de la oferta en

  • Accede a los más de 200 cursos
  • Nuevos cursos añadió mensual
  • Cancelar en cualquier momento
  • Los certificados de terminación

    ACCESO NOWDon’t pierda! extremos de la oferta en

    • Accede a los más de 200 cursos
    • Nuevos cursos añadió mensual
    • Cancelar en cualquier momento
    • Los certificados de terminación

      ACCESO ahora

      Pre-Order The Complete Realidad Virtual desarrollo del juego con Unity y aprender a crear juegos y experiencias de inmersión mediante la construcción de 10 juegos de realidad virtual. El curso asume ninguna experiencia previa Unidad o VR – Te enseñaremos C #, la Unidad y la programación 3D de la tierra-para arriba

      comprobarlo en Zenva Academia y obtener acceso temprano!
      código fuente tutorial

      Enlace de descarga para tutorial
      Enlace para Visio Alternativas aquí.
      Sección 1: La Teoría Boring sección

      Si usted es un principiante, es posible que nunca han oído de un mapa de baldosas. Un mapa es un mapa de baldosas nivel de procedimiento generado que se crea dentro de un determinado conjunto de reglas. Esto en sí mismo puede ser confuso y puede dibujar preguntas, por lo que debemos romper ese abajo.
      ¿Cuáles son las reglas de generación mapa?
      mapa es un
      5 x 5 cuadrícula
      Si una baldosa está marcado Desactivar, Establecer una baldosa en ese punto
      Si una baldosa iría fuera del tamaño de la cuadrícula, no establezca un azulejo.
      Estas son las reglas básicas que usted podría emplear para la fabricación de un sistema de mapas de baldosas.

      ¿Qué palabras clave de programación o bucles que se utilice para el sistema de mapas de baldosas?
      Enumeración, bucles for anidados, foreach, constructores, sobrecarga, y si las declaraciones

      ¿Puede este sistema de mapas puede modificar para isométrico, hexagonal o Diametric?
      Con esta pregunta, voy a decir esto. Si usted puede entender las reglas, las matemáticas fundamental detrás de cada estilo y lo que se hizo a la ejecución, que podría convertirla en una rejilla o bien isométrica, Diametric o hexagonal. Para ser completamente honesto, no he sido capaz de hacerlo con éxito isométrica, Diametric o hexagonal mapas hasta la fecha.

      ¿Puede este mapa sistema se utilice en un juego de estrategia en tiempo real?
      Si usted es un principiante, no recomiendo que empiece con un juego de estrategia en tiempo real. Son extremadamente difíciles de prototipos y aplicar plenamente. Si usted tiene un mentor o está remedando un equipo de desarrolladores, yo sugeriría que hable con ellos y ver lo que piensan.
      Una vez dicho esto, muchos de los juegos de estrategia en tiempo real clásicos utilizaron un sistema de recubrimiento similar a este. Por lo tanto, se puede utilizar absolutamente.
      Ahora que estas preguntas se hacen, creo que en realidad deberíamos hablar de las matemáticas involucradas. Las matemáticas involucradas no es tan complicado como se podría pensar, si usted es como yo y están muertos de miedo de las matemáticas.

      ¿Qué información necesitamos matemáticamente?
      Tamaño de las imágenes, el tamaño de la red que desea visualizar, y el tamaño de la pantalla. Usted también debe tener una buena comprensión de algunos cálculos, la multiplicación, y contando el número lineal positiva / negativa.

      creo que esto casi cubre toda la teoría detrás de hacer un sistema de mapas de baldosas. Me he mantenido agnóstico idioma por lo que esta parte del tutorial se puede aplicar a cualquier idioma que está aprendiendo. Lo que quiero decir con el lenguaje es agnóstico esta sección se puede aplicar a cualquier lenguaje de programación. Si decide aprender de imprenta y Phaser, BabylonJs, LimeJS, Quinto, Unreal Engine, Construct2, HTML5, o cualquier otro lenguaje de programación / motor de juego; Usted podría utilizar esta parte del tutorial y funcionará!
      Sección 2: Planificación del Código

      En esta sección, se ocupa de la planificación de nuestro código fuente y luego escribirlo. Lo que quiero decir con esto es, primero crear el diagrama UML de cómo el código debe ser estructurada y luego en realidad escribir el código. Es importante que hago hincapié en la necesidad imperiosa de que es para planificar su código en primer lugar, y esto es cierto independientemente de si el diseño de juegos, aplicaciones en cualquier plataforma, y ​​sitios web. una planificación adecuada y medios de documentación que va a pasar menos tiempo tratando de averiguar lo que está tratando de implementar y cómo desea implementarlo. También significa que usted puede añadir mucho más fácilmente nuevas características al código y saber exactamente dónde va, lo que es heredera, y si entra en conflicto con otra porción de su código. Para la planificación de esto, estoy usando Microsoft Visio 2015. Si usted no tiene Visio o los fondos para comprarlo, puede utilizar Gráfico lúcido o Oficina Libra Dibuje en su lugar.
      Básico UML TME
      Como se puede ver, nos separamos todo de acuerdo a lo que debería ser de clase. Nos hizo la enumeración sea en su propia clase llamada Azulejos, clase TileSprite está marcado serializable y no tiene la herencia, y hereda TilingEngine de MonoBehaviour. También se escribió a cabo nuestras propiedades y métodos. Nos separamos las propiedades de los métodos y se aseguró de añadir los paréntesis para designar métodos.
      Sección 2.5: escribir el código

      En esta sección, se van a escribir el código real. Yo sé que ustedes están eufórico de ver por fin algo de código y cómo podemos aplicarlo a Unity3D. Puesto que esto es con el principiante en mente, voy a estar explicando el código a través de código auto documentar y con los comentarios después de cada segmento de código con respecto a ella. En primer lugar, es la clase de azulejos.
      utilizando UnityEngine; utilizando System.Collections; public enum {Azulejos No establecido, hierba, agua, ladrillo, piedra arenisca} 1234567891011usingUnityEngine; usingSystem.Collections; publicenumTiles {definida, hierba, agua, ladrillo, piedra arenisca}

      Aquí, estamos haciendo uso de la palabra clave enum. Si no está familiarizado con las palabras clave .Net, sugeriría que deje de leer este tutorial y leer esta guía en todos los C # palabras clave en .NET Framework. En el código anterior, el compilador vería esto como Desestablecer = 0, Hierba = 1, y así sucesivamente. Se podría establecer explícitamente esto, sin embargo, no es necesario que en la mayoría de los casos.

      El siguiente, es la clase TileSprite.
      using System; usando UnityEngine; usando System.Collections; [Serializable] public class TileSprite {public string Nombre; Sprite pública tileimage; Azulejos pública TileType; TileSprite pública () {Name = «Desactivar»; tileimage = new Sprite (); TileType = Tiles.Unset;} pública TileSprite (string name, imagen Sprite, azulejo azulejos) {name = nombre; tileimage = imagen; TileType = baldosas;}} 12345678910111213141516171819202122232425usingSystem; usingUnityEngine; usingSystem.Collections; [Serializable] publicclassTileSprite {publicstringName; publicSprite tileimage; publicTiles TileType; publicTileSprite () {Name = «Desactivar»; tileimage = newSprite (); TileType = Tiles.Unset;} publicTileSprite (nombreDeCadena, imagen Sprite, baldosas Azulejos) {nombre = nombre; tileimage = imagen; TileType = baldosas;} }

      declara una cadena como nombre, sprite imagen de mosaico, azulejos como el tipo de baldosas como. En el constructor por defecto, nos fijamos el nombre definido, la imagen de la baldosa como nuevo sprite, y el tipo de baldosas como tiles.unset. Lo que estamos haciendo aquí en el constructor por defecto es dar un valor de cadena para asociarse con el sprite y el valor de enumeración de desarmado. El siguiente, sobrecargamos el constructor por defecto con una firma del método del nombre de la cadena, sprite, y azulejo azulejos. Esto se hace así más adelante podemos modificar el nombre, la imagen y el tipo de loseta más tarde

      Última en la lista es nuestra clase TilingEngine. Voy a dividirlo en secciones más pequeñas antes de revelar el código completo.
      utilizando UnityEngine; usando System.Collections; usando System.Collections.Generic; usando magra; TilingEngine clase pública: MonoBehaviour {public List TileSprites; Vector2 MapSize pública; Sprite DefaultImage pública; GameObject TileContainerPrefab pública; GameObject TilePrefab pública; Vector2 CurrentPosition pública; Vector2 ViewPortSize pública; TileSprite privada [,] _MAP; controlador GameObject privado; _tileContainer GameObject privado; Lista privada _tiles = new List (); privada TileSprite FindTile (azulejo azulejos) {foreach (TileSprite tileSprite en TileSprites) {if (tileSprite.TileType == teja) volver tileSprite; } Nula regresar; }} 1234567891011121314151617181920212223242526272829usingUnityEngine; usingSystem.Collections; usingSystem.Collections.Generic; usingLean; publicclassTilingEngine: MonoBehaviour {publicList TileSprites; publicVector2 MapSize; publicSprite DefaultImage; publicGameObject TileContainerPrefab; publicGameObject TilePrefab; publicVector2 CurrentPosition; publicVector2 ViewPortSize; privateTileSprite [,] _ mapa; privateGameObject controlador; privateGameObject _tileContainer; privateList _tiles = newList (); privateTileSprite FindTile (azulejo azulejos) {foreach (TileSprite tileSprite inTileSprites) {if (tileSprite.TileType == teja) returntileSprite;} returnnull;}} < p> de acuerdo, las propiedades de éste es bastante fornido. Estoy seguro de que ha notado una nueva instrucción using de Lean. Esto se debe a que estamos usando un activo denominado LeanPool. Es un activo libre en la tienda de activos y que está diseñado para ser utilizado para la agrupación de objetos. la agrupación de objetos es el reciclaje de objetos en lugar de simplemente eliminarlos. Utiliza menos memoria a largo plazo y funciona fantásticamente para los objetos que serán reutilizados muy a menudo. Tenemos una lista que tiene un parámetro de sprites baldosas llamada baldosas sprite, un Vector 2 de MapSize, Sprite imagen predeterminada de, dos gameobjects para contenedores baldosas prefabricadas y teja prefabricada, un vector 2 de la posición actual, el vector 2 para el tamaño de ventana de observación. Para las propiedades privadas que tenemos; Teja de sprites como una matriz llamada mapa, lista con un parámetro de azulejos GameObject llamado, y 2 GameObject respectivamente llamado controlador y el controlador de baldosas.
      Cada una de estas propiedades se discutirán a medida que a ellos en el guión. En ese sentido, nuestro primer método es encontrar azulejo con un parámetro de baldosas. En el interior tenemos un bucle foreach que pasa por cada sprite baldosas y si es igual al azulejo, devuelve una baldosa. Si no es igual baldosas, se devuelve un valor nulo. error básico de manipulación aquí.
      En la siguiente sección del código. Prometo que no será tan grande como la primera.
      DefaultTiles private void () {for (var y = 0; y TileSprites.Count – 1) index = 0; }}} 123456789101112131415161718192021222324privatevoidDefaultTiles () {for (variar = 0; y TileSprites.Count-1) índice ( = 0;}}}

      Nuestro primer método es de baldosas predeterminado. Aquí vemos un bucle anidado para, o un bucle dentro de otro bucle. Aquí vemos Mapa tamaño X e Y en los bucles, así como la palabra clave var. El medio de palabras clave var que crea un tipo genérico que los elige compilador el tipo específico es en tiempo de compilación. Lo que la de los bucles están diciendo es bastante simple, sin embargo, creo que siempre es mejor ponerlo en la llanura Inglés para la comprensión completa.
      Aquí es una variable de yy es igual a 0. Si y es menor que y del tamaño del mapa menos 1, añadir a y. Aquí es una variable de xy es igual a 0. Si es menor que x del tamaño del mapa menos 1, añadir a x. Cada vez que se realiza un bucle a través, matriz del mapa con un valor X e Y se le da un nuevo sprite del azulejo y del nombre debe ser desarmado, con la imagen predeterminada, y el valor de la enumeración de Desestablecer.
      El siguiente es el conjunto de azulejos método. Declaramos un índice que es igual a 0. El bucle for que dice aquí es una variable de yy es igual a 0. Si y es menor que y del tamaño del mapa menos 1, añadir a y. Aquí es una variable de xy es igual a 0. Si es menor que x del tamaño del mapa menos 1, añadir a x. Cada vez que se realiza un bucle a través, matriz del mapa con un valor X e Y se le da un nuevo sprite Azulejo con los parámetros de sprites azulejo con una matriz de índice que tiene un nombre, sprites azulejo con una matriz de índice de imagen de mosaico de, y Azulejos Sprites con una matriz de índice de tipo baldosa. añadir al índice para cada bucle. Por último, tenemos una sentencia if. Si el índice es mayor que el recuento de baldosas sprites’ menos 1, el índice es igual a 0. La sentencia if es un poco de manejo de errores rociada sobre.
      AddTilesToWorld private void () {foreach (GameObject o en _tiles) {LeanPool.Despawn (o); } _Tiles.Clear (); LeanPool.Despawn (_tileContainer); _tileContainer = LeanPool.Spawn (TileContainerPrefab); var tileSize = .64f; var viewOffsetX = ViewPortSize.x / 2f; var viewOffsetY = ViewPortSize.y / 2f; for (var y = -viewOffsetY; y MapSize.x – 2) continúan; si (Iy> MapSize.y – 2) continuar; var t = LeanPool.Spawn (TilePrefab); t.transform.position = nuevo Vector3 (Tx, Ty, 0); t.transform.SetParent (_tileContainer.transform); var renderer = t.GetComponent (); renderer.sprite = _MAP [(int) x + (int) CurrentPosition.x, (int) y + (int) CurrentPosition.y] .TileImage; _tiles.Add (t); }}} 123456789101112131415161718192021222324252627282930313233343536privatevoidAddTilesToWorld () {foreach (GameObjectoin_tiles) {LeanPool.Despawn (o);} _ tiles.Clear (); LeanPool.Despawn (_tileContainer); _ tileContainer = LeanPool.Spawn (TileContainerPrefab); vartileSize = .64f; varviewOffsetX = ViewPortSize .x / 2f; varviewOffsetY = ViewPortSize.y / 2f; for (variar = -viewOffsetY; y MapSize.x-2) siguen; si (iy > MapSize.y-2) continuar; vart = LeanPool.Spawn (TilePrefab); t.transform.position = newVector3 (Tx, ty, 0); t.transform.SetParent (_tileContainer.transform); varrenderer = t.GetComponent < SpriteRenderer> (); renderer.sprite = _MAP [(int) x + (int) CurrentPosition.x, (int) y + (int) CurrentPosition.y] .TileImage; _tiles.Add (t);}}}

      Ahora tenemos los azulejos Añadir a método mundo. Éste es el corazón de la mayor parte del código. Tenemos un bucle foreach con el parámetro del objeto del juego desde el interior de las baldosas. Con cada bucle, piscina magra es hacer desaparecer un objeto. piscina magra se desaparecerán el contenedor de azulejos y el contenedor baldosas se colocan a la piscina magra para desovar el contenedor prefabricado del azulejo.
      El tamaño de la baldosa variable llamada se establece en 0,64 flotador.
      El desplazamiento variable llamada x vista se establece en el tamaño del puerto vista de x dividido por 2 flotador.
      La variable llamada vista del eje y se ajusta al tamaño de ventana de observación de y dividido por 2 flotador.
      El anidado de bucle comienza con Y en lugar de x este momento. La razón es porque es considerada estándar de la industria para hacerlo. La variable de y es igual a la vista desplazamiento negativo de y. si y es menor que el desplazamiento de la vista y, añadir a y. La variable de x es igual a la vista desplazamiento negativo de x. si x es menor que la distancia al eje de x, añadir a x.
      Creamos una variable llamada tX y es igual a x multiplicado por el azulejo size.we crear una variable llamada tY y es igual a Y multiplica por el tamaño de la baldosa.
      Creamos una variable llamada iX y es igual a x más la posición actual de x.
      Creamos una variable llamada iY y es igual a y más la posición actual de y.
      Ahora, para las sentencias if. Si iX es menor que 0, continúe. iY si es inferior a 0, continúe. Si iX es mayor que x del tamaño del mapa menos 2, seguir adelante. iY si es mayor que y del tamaño del mapa menos 2, seguir adelante.
      Creamos una variable llamada ty Se fija para la freza de la piscina de Lean para la baldosa prefabricada. La posición de transformar t es igual a un nuevo vector 3 de tx, ty, y 0. Se establece que el padre de la t de transformar al contenedor de Azulejos para transformar. Creamos una variable llamada procesador y decimos t para obtener el componente de Sprite Procesador. entonces nosotros decimos azulejos añadir t.
      public void start () {controller = GameObject.Find ( «Controller»); _MAP = new TileSprite [(int) MapSize.x, (int) MapSize.y]; DefaultTiles (); SetTiles (); } Privada actualización void () {AddTilesToWorld (); } 12345678910111213publicvoidStart () {controller = GameObject.Find ( «Controller»); _ MAP = newTileSprite [(int) MapSize.x, (int) MapSize.y]; DefaultTiles (); SetTiles ();} privatevoidUpdate () {AddTilesToWorld ( );}

      Última métodos 2 y luego de pasar a la siguiente sección. Tenemos el método de inicio. Creamos una instancia del objeto controlador por tener método de búsqueda del objeto del juego para encontrar un objeto llamado controlador. Ahora nos fijamos el mapa para ser un nuevo sprite Tile, los parámetros de la misma son explícitamente x e y para valores enteros de tamaño del mapa convertida para coincidir con la forma de la matriz está configurado. Ahora llamamos el método azulejos predeterminada y establecer método de azulejos. El método de actualización es ahora. Dentro de actualización simplemente llamamos los azulejos Añadir a método mundo.
      Por último, echemos un vistazo a la clase TilingEngine en su conjunto.
      utilizando UnityEngine; usando System.Collections; usando System.Collections.Generic; usando magra; TilingEngine clase pública: MonoBehaviour {public List TileSprites; Vector2 MapSize pública; Sprite DefaultImage pública; GameObject TileContainerPrefab pública; GameObject TilePrefab pública; Vector2 CurrentPosition pública; Vector2 ViewPortSize pública; TileSprite privada [,] _MAP; controlador GameObject privado; _tileContainer GameObject privado; Lista privada _tiles = new List (); public void start () {controller = GameObject.Find ( «Controller»); _MAP = new TileSprite [(int) MapSize.x, (int) MapSize.y]; DefaultTiles (); SetTiles (); } DefaultTiles private void () {for (var y = 0; y TileSprites.Count – 1) index = 0; }}} Private void Update () {AddTilesToWorld (); AddTilesToWorld} private void () {foreach (GameObject o en _tiles) {LeanPool.Despawn (o); } _Tiles.Clear (); LeanPool.Despawn (_tileContainer); _tileContainer = LeanPool.Spawn (TileContainerPrefab); var tileSize = .64f; var viewOffsetX = ViewPortSize.x / 2f; var viewOffsetY = ViewPortSize.y / 2f; for (var y = -viewOffsetY; y MapSize.x – 2) continúan; si (Iy> MapSize.y – 2) continuar; var t = LeanPool.Spawn (TilePrefab); t.transform.position = nuevo Vector3 (Tx, Ty, 0); t.transform.SetParent (_tileContainer.transform); var renderer = t.GetComponent (); renderer.sprite = _MAP [(int) x + (int) CurrentPosition.x, (int) y + (int) CurrentPosition.y] .TileImage; _tiles.Add (t); }}} Privada TileSprite FindTile (baldosas Azulejos) {foreach (TileSprite tileSprite en TileSprites) {if (tileSprite.TileType == teja) volver tileSprite; } Nula regresar; }} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105usingUnityEngine; usingSystem.Collections; usingSystem.Collections.Generic; usingLean; publicclassTilingEngine: MonoBehaviour {publicList TileSprites; publicVector2 MapSize; publicSprite DefaultImage; publicGameObject TileContainerPrefab; publicGameObject TilePrefab; publicVector2 CurrentPosition; publicVector2 ViewPortSize; privateTileSprite [,] _ mapa; controlador privateGameObject; privateGameObject _tileContainer; privateList _tiles = newList (); publicvoidStart () {controller = GameObject.Find ( «Controller»); _ mAP = newTileSprite [(int) MapSize.x, (int) MapSize. Y]; DefaultTiles (); SetTiles ();} privatevoidDefaultTiles () {for (varían = 0; y TileSprites.Count-1) index = 0;}}} privatevoidUpdate () {AddTilesToWorld ();} privatevoidAddTilesToWorld () {foreach (GameObjectoin_tiles) {LeanPool.Despawn ( o);} _ tiles.Clear (); LeanPool.Despawn (_tileContainer); _ tileContainer = LeanPool.Spawn (TileContainerPrefab); vartileSize = .64f; varviewOffsetX = ViewPortSize.x / 2f; varviewOffsetY = ViewPortSize.y / 2f; for (variar = -viewOffsetY; y MapSize.x-2) continuar; si (iy> MapSize.y-2) siguen; vart = LeanPool.Spawn (TilePrefab ); t.transform.position = newVector3 (Tx, ty, 0); t.transform.SetParent (_tileContainer.transform); varrenderer = t.GetComponent (); renderer.sprite = _MAP [(int) x + ( int) CurrentPosition.x, (int) y + (int) CurrentPosit ion.y] .TileImage; _tiles.Add (t);}}} privateTileSprite FindTile (azulejos Azulejos) {foreach (TileSprite tileSprite inTileSprites) {if (tileSprite.TileType == teja) returntileSprite;} returnnull;}}

      ahora que hemos creado nuestros scripts, ahora podemos entrar en la Unidad y preparar todo para verlo en acción.

      Sección 3: Aumento de la escena en Unity3D

      Abrir un nuevo proyecto en la Unidad. Establece que sea un proyecto 2D ya que este fue diseñado con 2D en mente. En la carpeta de activos, crear 2 carpetas; Imágenes uno es y el otro es guiones.
      22.01.2016 (2)
      El siguiente, añadir las secuencias de comandos a la carpeta scripts o crear secuencias de comandos 3 con los mismos nombres que las clases que se habla en la Sección 2. Cualquiera de escribir el código a mano si no lo ha hecho ya, o copiar y pegar el código en si lo escribió mientras la lectura de la sección 2.
      22.01.2016 (3)

      El uso de Microsoft Paint o cualquier imagen de edición / creación de programa make 5 imágenes por separado con los colores (blanco, gris, verde, beige y azul) y añadirlos a la carpeta Imágenes.
      22.01.2016 (4)

      Abrir el almacén de activos y la búsqueda de “magra piscina”, descarga e importarlo en el proyecto.
      01/22/2016 (5)

      Añadir 3 objetos del juego vacías. Nombre un controlador, otra TileContainer, y en tercer lugar del azulejo uno.
      22.01.2016 (9)

      En el objeto de juego llamado azulejo, añadir un componente
      Sprite Procesador
      22.01.2016 (10)
      En el controlador, añadir el guión Forros motor en la misma. hacer que la x e y para ser 100. imagen predeterminada está en blanco 1, Baldosa de contenedores prefabricados adjuntar el objeto contenedor terracota, baldosas de Prefab asociar el objeto de baldosas, la posición actual x e y es 50. tamaño Ver puerto x es 11 e y es 39.
      22.01.2016 (11)

      Minus por las Sprites Azulejos flecha justo debajo de la secuencia de comandos del embaldosado del motor.
      22.01.2016 (12)

      Cambiar el tamaño de cada una 4. Nombre a lo que debe ser similar (de acuerdo con las enumeraciones), ajustar la imagen de acuerdo con el nombre, y establecer el tipo de baldosa a lo lo ha llamado.
      22.01.2016 (13)

      Ejecutar el proyecto y Ahora tiene una parte superior 2D abajo baldosa del mapa aparece en la pantalla del juego.
      22.01.2016 (15)

      Mensajes relacionados

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *