Crear un juego en 2D con Unity3d.
0. Índice de contenidos.
- 1. Introducción.
- 2. Entorno.
- 3. Antes de empezar.
- 3.1. Instalación de Unity. Familiarizarse con el interfaz.
- 3.2. Recursos para usar en el proyecto.
- 3.3. Scripting en Unity.
- 3.4. Crear proyecto e importar recursos.
- 4. Desarrollando el juego.
- 5. Conclusiones.
1. Introducción.
Siguiendo con los tutoriales para desarrollo de videojuegos para iPhone (cocos2d y GameSalad) en este tutorial vamos a ver la herramienta para desarrollo de videojuegos Unity 3d.
Unity 3d es una herramienta para desarrollo de videojuegos que permite crear videojuegos bastante espectaculares. Además es una herramienta completamente multiplataforma, se puede desarrollar para pc, mac, web, iOS, android, Wii, Xbox 360 y PS3. De forma gratuita solamente se pueden crear aplicaciones para pc, mac y web; para el resto es necesario adquirir una licencia aparte (podéis ver los precios de las distintas licencias en https://store.unity3d.com/shop/). Este tutorial esta hecho usando la versión gratuita.
En este tutorial volveremos a crear nuestra versión del Space Invaders en 2D usando Unity3d. Vamos a aprovechar la mosca en 3d que hizo Roberto en un tutorial anterior (https://adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=moscayblender) y que luego animó Daniel (https://adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=Blender_Animaciones_Renderizacion), además usaré una nave espacial que me bajé de www.wirecase.com y una textura metálica que encontré en http://www.freetextures.org. podéis descargaros todos los recursos necesarios aquí, el proyecto acabado aquí y los ejecutables para mac y windows aquí y por último podeís probar la aplicación web aquí (os pedirá instalar unity web player).
2. Entorno.
- Hardware: MacBookPro8,2 (2 GHz Intel Core i7, 4GB DDR3 SDRAM).
- AMD Radeon HD 6490M 256MB.
- Sistema Operativo: Mac OS X Snow Leopard 10.6.7.
- Unity 3.3.0
3. Antes de empezar.
3.1. Instalación de Unity. Familiarizarse con la interfaz.
Para instalar Unity nos lo descargamos de su página web http://unity3d.com/unity/download/ y simplemente seguimos el instalador.
Unity tiene una interfaz parecida a los editores 3d. En http://www.unityspain.com/ podeís encontrar un videotutorial sobre la interfaz (http://www.unityspain.com/Archivosformacion/tutorial_interfaz.htm), os recomiendo que lo veais antes de empezar a leer este tutorial así como que trasteéis un poco con el ejemplo que viene con unity. podéis abrir el ejemplo en File > Open Project y elegís el Bootcamp Demo.
Nota: Unity permite ejecutar el juego en cualquier momento y realizar cambios durante la ejecución en los objetos y sus distintos componentes. Es importante saber que cualquier cambio que realiceís durante la ejecución del juego no se guarda cuando la ejecución para. Por lo tanto todos los cambios que hagaís y queraís conservar debeís volver a realizarlos al parar la ejecución.
3.2. Recursos para usar en el proyecto.
Todos los recursos 3d y las texturas que se usan en Unity tienen que crearse con programas externos de diseño. Unity es capaz de importar fácilmente (copiando en la carpeta assets) recursos de Maya, Cinema 4D, 3ds Max, Cheetah3D y Blender; además permite importar formatos .fbx y .obj. De los editores anteriores Blender es el único opensource.
Todos los recursos que usaremos en nuestro proyecto os los podeís bajar de aquí (poner link). Y el proyecto acabado os lo podeís bajar aquí.
3.3. Scripting en Unity.
En cuanto al scripting, Unity permite el uso de tres lenguajes: C#, Boo y JavaScript. El JavaScript que usa Unity es una versión propia (a vecés se le llama UnityScript) que usa compilación Just In Time lo que hace que funcione más rápido que el JavaScript normal. Se puede elegir el lenguaje que se prefiera, incluso se pueden usar combinaciones de los tres a la hora de hacer el scripting para el videojuego.
Toda la lógica del juego en unity esta basada en Mono, una plataforma basada en .net para crear aplicaciones cross-platform, podeís encontrar más información en su página (http://www.mono-project.com/).
Todo script deriva de la clase MonoBehaviour que tiene cinco funciones básicas que controlan cuando se ejecuta el código, en cada frame o al inicializase. Las funciones son: Update, LateUpdate, FixedUpdate, Awake y Start.
- Update: se ejecuta una vez en cada frame.
- LateUpdate: también se ejecuta en cada frame pero cuando ha acabado la función Update.
- FixedUpdate: se llama en cada ciclo del motor de físicas, en esta función deben ponerse los calculos que afecten a dicho motor (como los movimientos del jugador).
- Awake: se llama cuando el script se carga en tiempo de ejecución y es un buen sitio para poner inicializaciones y referencias entre scripts.
- Start: se llama despues de la función Awake y antes del primer Update, es otro buen sitio en el que poner inicializaciones o comprobaciones que sólo se vayan a hacer una vez.
En este tutorial usaremos UnityScript para crear los scripts que usará el videojuego. Y en cuanto al editor de texto para crear los scripts, usaremos el que viene al instalar Unity que en Mac se llama Unitron.
3.4. Crear proyecto e importar recursos.
Para empezar creamos un nuevo proyecto, File > New Project, elegimos la ruta en la que lo guardaremos y los paquetes que queremos importar (en nuestro caso ninguno). Se nos crea una escena con una camara principal. Guardamos la escena como Juego y ya nos aparecé en la ventana de project. Ahora vamos a importar los recursos que nos van a hacer falta para el juego, simplemente tenemos que copiar desde el Finder los archivos que nos bajamos a la carpeta assets de este proyecto y unity se encargará de importarlos automáticamente.
Si lo que quereís es abrir el proyecto ya acabado podeís hacerlo ejecutando el archivo .unity que hay en la carpeta Assets.
4. Desarrollando el juego
De momento tenemos una escena que sólo tiene una camara principal. Para que sea mas fácil ajustar la camara vamos a colocar la nave en posición.
Arrastramos el cubo con la hojita naves, de la ventana project a la de hierarchy naves. Con lo que se nos cargaran las naves en la escena. Ahora mismo tenemos las 6 naves cargadas, como sólo necesitamos una, elegimos la que más nos guste y en la ventana de hierarchy la arrastramos fuera de naves (yo voy a elegir la 6). Nos avisa que con esto se perderá el prefab, le damos a continuar.
Ahora podemos borrar el resto de naves de la escena, seleccionamos naves en el hierarchy y hacemos edit > delete (o cmd + supr). Con lo que nos quedaría sólo la nave y la cámara principal en nuestra escena. Vamos a cambiarle el nombre a Nave y a crear un prefab.
En la ventana project le damos a create Prefab. Vamos a crear también una carpeta para guardar todos los prefabs. Arrastramos el nuevo prefab dentro de la nueva carpeta.
Ahora arrastramos la Nave de Hierarchy encima del prefab Nave que hemos creado (observamos que cambia el color del prefab a azul).
Seleccionamos la Nave en el Hierarchy y vemos que en el Inspector aparecen los datos de la nave. Le cambiamos la posición a la x=0, y=0 y z=0) y la rotamos en el eje x 90º.
Para que sea más sencillo colocar la camara vamos a crear un objeto que delimitará el espacio por el que se podrá mover. Le cambiamos el nombre a Level Attributes. Y vamos a usar uno de los script que se usa en el tutorial de unity para crear un juego en 2d (http://unity3d.com/support/resources/tutorials/2d-gameplay-tutorial). Este script creará unas lineas en la vista de scene que nos indicarán donde estarán los limites jugables, y cuando se ejecute el juego creará los colliders en esa posición. Le añadimos el script al objeto Level Attributes y ajustamos los atributos a través del inspector.
var bounds : Rect; // Rectángulo con Los tamaños para el nivel var fallOutBuffer = 5.0; // Margen para la caída var colliderThickness = 10.0; // Grosor del Collider // Color en el que se dibujarán en el scene private var sceneViewDisplayColor = Color (0.20, 0.74, 0.27, 0.50); static var instance : LevelAttributes; static function GetInstance() { if (!instance) { instance = FindObjectOfType(LevelAttributes); if (!instance) Debug.LogError("There needs to be one active LevelAttributes script on a GameObject in your scene."); } return instance; } function OnDisable () { instance = null; } // Función para dibujar las lineas que se verán en la Scene function OnDrawGizmos () { Gizmos.color = sceneViewDisplayColor; var lowerLeft = Vector3 (bounds.xMin, bounds.yMax, 0); var upperLeft = Vector3 (bounds.xMin, bounds.yMin, 0); var lowerRight = Vector3 (bounds.xMax, bounds.yMax, 0); var upperRight = Vector3 (bounds.xMax, bounds.yMin, 0); Gizmos.DrawLine (lowerLeft, upperLeft); Gizmos.DrawLine (upperLeft, upperRight); Gizmos.DrawLine (upperRight, lowerRight); Gizmos.DrawLine (lowerRight, lowerLeft); } function Start () { createdBoundaries = new GameObject ("Created Boundaries"); createdBoundaries.transform.parent = transform; leftBoundary = new GameObject ("Left Boundary"); // Limite izquierdo leftBoundary.transform.parent = createdBoundaries.transform; boxCollider = leftBoundary.AddComponent (BoxCollider); // Collider izquierdo boxCollider.size = Vector3 (colliderThickness, bounds.height + colliderThickness * 2.0 + fallOutBuffer, colliderThickness); boxCollider.center = Vector3 (bounds.xMin - colliderThickness * 0.5, bounds.y + bounds.height * 0.5 - fallOutBuffer * 0.5, 0.0); rightBoundary = new GameObject ("Right Boundary"); // Limite derecho rightBoundary.transform.parent = createdBoundaries.transform; boxCollider = rightBoundary.AddComponent (BoxCollider); // Collider derecho boxCollider.size = Vector3 (colliderThickness, bounds.height + colliderThickness * 2.0 + fallOutBuffer, colliderThickness); boxCollider.center = Vector3 (bounds.xMax + colliderThickness * 0.5, bounds.y + bounds.height * 0.5 - fallOutBuffer * 0.5, 0.0); topBoundary = new GameObject ("Top Boundary"); // Limite superior topBoundary.transform.parent = createdBoundaries.transform; boxCollider = topBoundary.AddComponent (BoxCollider); boxCollider.size = Vector3 (bounds.width + colliderThickness * 2.0, colliderThickness, colliderThickness); // Collider superior boxCollider.center = Vector3 (bounds.x + bounds.width * 0.5, bounds.yMax + colliderThickness * 0.5, 0.0); bottomBoundary = new GameObject ("Bottom Boundary (Including Fallout Buffer)"); // Limite inferior bottomBoundary.transform.parent = createdBoundaries.transform; boxCollider = bottomBoundary.AddComponent (BoxCollider); // Collider inferior boxCollider.size = Vector3 (bounds.width + colliderThickness * 2.0, colliderThickness, colliderThickness); boxCollider.center = Vector3 (bounds.x + bounds.width * 0.5, bounds.yMin - colliderThickness * 0.5 - fallOutBuffer, 0.0); }
Con las lineas que delimitan el espacio de juego ya dibujadas vamos a colocar la camara. Como va a ser un juego en 2d, tendremos que decir que el tipo de proyección de la camara será ortográfica (Projection Orthographic). La movemos para que este centrada y le cambiamos el tamaño para que entre todo (luego en un futuro quizás cambiemos esto para que quede mejor). Hay que girar la camara en el eje Y 180º para que enfoque hacia el area de juego.
Ahora que ya tenemos la camara enfocando vamos a hacer que la nave se pueda mover. Vamos a añadirle al prefab de la nave un Character Controller que se encargará de gestionar las colisiones y el movimiento de nuestra nave. Seleccionamos el prefab en la ventana fde Project y le damos a Component > Physics > Character Controller. En el Inspector podemos ver que se ha añadido el Character Controller.
Si pinchamos en el Hierarchy podemos ver que hay una esfera alrededor de nuestra nave, esta esfera será el area de colisión de nuestra nave, vamos a ajustarla lo máximo posible al tamaño de la nave cambiandole el radio al controller. Una vez hechos los cambios arrastramos la nave del Hierarchy al Prefab para que guarden los cambios.
Unity gestiona la entrada de datos a la aplicación mediante unos ejes de entrada que podemos ver en Edit > Project Settings > Input. Para nuestro juego sólo necesitaremos dos, el horizontal y uno para disparar que llamaremos Fire, por lo que cambiamos el atributo Size a 2 y editamos el segundo para gestionar el disparo.
Para mover la nave creamos un script que se llame NaveController y lo editamos añadiendole lo siguiente:
// Recuperar el Character Controller var controller : CharacterController; controller = GetComponent(CharacterController); var moveSpeed : float = 30.0; // Velocidad de movimiento de la nave. // Método que se ejecuta antes del método Update function FixedUpdate () { moveDirection = Vector3(- Input.GetAxis("Horizontal"), 0, 0); moveDirection = transform.TransformDirection(moveDirection); moveDirection *= moveSpeed; controller.Move(moveDirection * Time.deltaTime); // Le decimos al controlador que se mueva pasandole la dirección múltiplicada por deltaTime }
Ahora arrastramos el script desde la ventana de proyecto sobre el prefab de la nave y si le damos a play ya podremos mover nuestra nave.
Lo siguiente será crear el disparo y hacer que la nave dispare.
Creamos un nuevo GameObject que sea una cápsula y le damos forma para que parezca un disparo, yo lo he hecho más o menos del mismo tamaño que la nave. Lo renombramos a disparo en el Hierarchy y creamos un Prefab también llamado Disparo que «llenamos» arrastrando el del Hierarchy encima. Después de crear el Prefab se puede borrar el disparo del Hierarchy.
Vamos a añadirle un objeto a la nave desde el que se va a generar el disparo, esto lo hacemos para evitar colisiones entre la nave y el disparo cuando se genere el disparo. Creamos el objeto GameObject > Create Empty lo renombramos a Disparador y lo arrastramos a la Nave que tenemos en el Hierarchy, le damos a continue (cuando acabemos tenemos que arrastrar de nuevo la Nave del Hierarchy al Prefab).
Vamos a colocar el Disparador encima de la esfera del Character Controller (las coordenadas del disparador son relativas al padre, la nave).
Una vez colocado arrastramos la Nave del Hierarchy al Prefab. Ahora vamos a añadirle al script NaveController lo necesario para que cuando se pulse la tecla asignada al disparo (el espacio en nuestro caso) se genere el disparo. Añadimos las siguientes lineas al principio del script y la función Update.
var disparo : Rigidbody; var disparoSpeed : float = 50.0; function Update(){ if (Input.GetButtonUp("Fire")){ disparador = transform.Find("Disparador"); var disparoClone : Rigidbody = Instantiate(disparo, disparador.transform.position , disparador.transform.rotation); disparoClone.velocity.y = disparoSpeed; alreadyShoot = true; } }
El motor que gestiona los aspectos físicos en unity sólo se aplica a los objetos si tienen el componente RigidBody, por lo tanto si queremos que nuestro objeto se tenga en cuenta por este motor (gestión de colisiones, ser afectado por la gravedad, aceleraciones, etc.) debemos añadirle ese componente.
Añadimos el componente RigidBody al disparo, Component > Physics > RigidBody. Y tenemos que deshabilitar el Use Gravity y dentro de las constraints limitar todo menos la posición en el eje Y.
Ahora seleccionamos la Nave en el Hierarchy y arrastramos el Disparo desde el Project hasta el atributo Disparo del Inspector.
Si lo probamos podemos ver que los disparos colisionan con el limite superior del juego y se quedan parados, vamos a hacer que se destruyan al colisionar con este limite. Para ello creamos el script DisparoCollisionController con el método OnCollisionEnter.
function OnCollisionEnter (collision : Collision) { if (collision.gameObject.name == "Top Boundary"){ Destroy(this); } }
Arrastramos el script al prefab del disparo y comprobamos que los disparos se destruyen al colisionar con el limite superior. Vamos a añadir ahora algo a lo que disparar, arrastramos la mosca al hierarchy y creamos un prefab para la mosca en el project. La mosca que estamos usando tiene una animación, como la animación que tiene no estaba pensada para el juego lo mejor es desactivarla (o incluso remover el componente).
Escalamos la mosca para que tenga un tamaño parecido al de la nave y creamos un prefab para la mosca. Le añadimos un Sphere Collider (GameObject > Physics > Sphere Collider) y lo ajustamos lo mejor posible a la forma de la mosca.
Una vez ajustado el tamaño del collider actualizamos el prefab de la mosca. Ahora añadimos al script DisparoCollisionController las lineas necesarias para destruir las moscas cuando colisionen con el disparo.
function OnCollisionEnter (collision : Collision) { // Cuando el gameObject con el que se colisiona // se llame Top Boundary if (collision.gameObject.name == "Top Boundary"){ Destroy(this.gameObject); // Destruir el gameObject que tiene asociado este script }else if (collision.gameObject.name == "Mosca"){ Destroy(collision.gameObject); // Destruir la mosca Destroy(this.gameObject); // Destruir el disparo } }
Una vez comprobado que se destruyen la mosca y el disparo podemos borrar la mosca de la escena. Vamos a crear ahora un GameObject que hará de padre para todas las moscas, para que a la hora de moverlas sólo tengamos que mover este GameObject. Lo creamos (GameObject > CreateEmpty) y le ponemos el nombre Padre Moscas. Ahora creamos un script para llenarlo de moscas y posicionarlas, CrearPosicionarMoscas.
var columnas : int = 8; var filas : int = 3; var separacionMoscas = 50; var mosca : Transform; function Start(){ var padre = this.gameObject.transform; // Cogemos la referencia del transform del padre padre.position = Vector3.zero; // Posicionar al padre en (0,0,0) for (yPos = 0; yPos< filas; yPos++){ for (xPos = 0; xPos < columnas; xPos++){ var moscaClone = Instantiate(mosca); // Crear la mosca moscaClone.parent = padre; // Asignamos el padre a la mosca mosca.localPosition = new Vector3( xPos * separacionMoscas, yPos * separacionMoscas, 0); // La posicionamos con respecto al padre } } padre.position = new Vector3(separacionMoscas, 300,0); // posicionamos el padre en la parte de arriba y a la derecha }
Al hacer esto, vemos que los limites del juego se han quedado pequeños, los hacemos más grandes (Width 900 y Height 500) y volvemos a ajustar la camara.
Si lo habeis probado vereis que ahora los disparos no destruyen la mosca, esto es porque ahora las moscas se llaman Mosca(Clone) en vez de mosca. Podemos cambiar el nombre con el que lo comparamos en if a Mosca(Clone) o crear una tag y comparar con la tag. Vamos a hacer esto último. Seleccionamos la mosca en la ventana de project, y en el inspector en la parte superior izquierda vemos que hay un desplegable que pone Tag, lo desplegamos y le damos a Add Tag... Se nos abre el Tag Manager en el inspector, dentro de tags cambiamos el Element 0 a enemigo. Esta será nuestra tag.
Una vez creada se la asignamos al prefab de la Mosca y cambiamos la condición del script:
collision.gameObject.name == "Mosca" por collision.gameObject.tag == "enemigo"
Para seguir y antes de empezar a mover las moscas, vamos a darle un poco de alegría a la escena, vamos a añadirle iluminación y un cielo para el fondo. Para la iluminación añadiremos una luz direccional, GameObject > Create Other > Directional Light. La movemos (aunque realmente no afecta la posición) y la orientamos a nuestro gusto, yo la voy a poner orientada hacia abajo en diagonal. Para orientarla lo mejor es ejecutar la escena e ir probando como afecta la luz a las moscas con la escena ejecutandose pero acordaros que los cambios realizados mientras se ejecuta la escena no se guardan.
Ahora añadimos el cielo, el paquete estandar de unity viene con algunos por defecto, asi que vamos a importarlos. Assets > Import Package > Skyboxes. Se nos ha añadido en el project la carpeta Standard Assets y dentro la carpeta Skyboxes.
Ahora para añadir el skybox vamos a Edit > Render Settings y se nos abrirán en el inspector. En Skybox Material elegimos el que más nos guste, yo elegiré Sunny2 Skybox.
Podeís cambiar la luz y el cielo como más os guste (yo al final he puesto la luz directamente de frente), también vamos a añadir una textura metálica a la nave y al disparo. Creamos en el project una carpeta Textures y desde el Finder copiamos la textura a esa carpeta. Seleccionamos el prefab de la nave y en la parte inferior derecha del inspector añadimos la textura dandole a Select y seleccionamos la de metal. Encima del cuadradito de la textura podemos elegir el color, yo voy a elegir un tono gris.
Ahora que esta todo un poco más presentable vamos a hacer el movimiento de las moscas. Creamos un script MoverPadreMoscas que arrastramos al Padre Moscas.
var velocidadMovimiento = 60; function Update () { this.gameObject.transform.position.x += velocidadMovimiento * Time.deltaTime; // Cambiar la posicion x del padre según el incremento del tiempo. }
Tenemos que añadir un RigidBody a la Mosca (Component > Physics > RigidBody) para que sea capaz de detectar las colisiones con los muros y crear un script MoscaColisionMuro para cambiar la dirección del movimiento y bajar a las moscas. Se lo añadimos al prefab de la mosca.
function OnCollisionEnter (collision : Collision) { // Mirar si la colision es con uno de los muros if (collision.gameObject.name == "Right Boundary" || collision.gameObject.name == "Left Boundary"){ // Cambiar la dirección hacia el otro lado this.gameObject.transform.parent.GetComponent(MoverPadreMoscas).velocidadMovimiento *= -1; this.gameObject.transform.parent.transform.position.y -= 10; // Desplazar hacia abajo el padre } }
Para que sólo pueda haber un disparo cambiamos el script MoscaController para añadir un booleano que nos diga si ya tenemos disparo o no y preguntar en la condición antes de disparar. Además tenemos que poner otra vez este atributo a false cuando el disparo colisione (Aprovecho para cambiar el código de este script).
// DisparoCollisionController.js var naveController : NaveController; function Awake(){ // Cogemos la referencia al script NaveController de la Nave naveController = GameObject.Find("Nave").GetComponent("NaveController"); } function OnCollisionEnter (collision : Collision) { // Si es un enemigo lo destruimos if (collision.gameObject.tag == "enemigo"){ Destroy(collision.gameObject); } // Si no es la propia nave if (collision.gameObject.name != "Nave"){ Destroy(this.gameObject); // Destruir el disparo naveController.hayDisparo = false; // Decir que no hay disparo } }
// NaveController.js var disparo : Rigidbody; var disparoSpeed : float = 150.0; var hayDisparo : boolean = false; // Recuperar el Character Controller var controller : CharacterController; controller = GetComponent(CharacterController); var moveSpeed : float = 100.0; // Velocidad de movimiento de la nave. // Método que se ejecuta antes del método Update function FixedUpdate () { moveDirection = Vector3(- Input.GetAxis("Horizontal"), 0, 0); moveDirection = transform.TransformDirection(moveDirection); moveDirection *= moveSpeed; controller.Move(moveDirection * Time.deltaTime); // Le decimos al controlador que se mueva pasandole la dirección múltiplicada por deltaTime } function Update(){ if (Input.GetButtonUp("Fire") && !hayDisparo){ disparador = transform.Find("Disparador"); var disparoClone : Rigidbody = Instantiate(disparo, disparador.transform.position , disparador.transform.rotation); disparoClone.velocity.y = disparoSpeed; hayDisparo = true; } }
Vamos a hacer que cuando se destruyán todas las moscas, o una mosca colisione con la nave, se termine el juego. Creamos el script perder juego que añadiremos al padre y el de ganar juego que añadiremos al prefab de la mosca.
// GanarJuego.js function Update(){ if (transform.childCount == 0){ GanarPartida(); } } function GanarPartida(){ print("¡Enhorabuena has ganado la partida!"); }
// PerderJuego.js var naveController : NaveController; function Awake(){ naveController = gameObject.Find("Nave").GetComponent("NaveController"); } function OnCollisionEnter(collision : Collision){ if (collision.gameObject.name == "Nave"){ PerderPartida(); PararMoscas(); PararNave(); } } function PerderPartida(){ print("¡Has perdido!"); } function PararMoscas(){ this.gameObject.transform.parent.GetComponent("MoverPadreMoscas").velocidadMovimiento = 0; } function PararNave(){ naveController.naveControlable = false; }
Por último vamos a añadir una escena inicial que servirá de menu y unos botones que nos lleven a la escena de jugar (y a otras dos escenas que no haremos en este tutorial). Creamos la nueva escena File > New Scene y la guardamos con el nombre Menu Principal. Y creamos el script que le añadiremos a la camara de esta escena.
var anchoBoton : int = 320; var altoBoton : int = 80; function OnGUI() { if (GUI.Button(Rect((Screen.width -anchoBoton) /2,((Screen.height -altoBoton)/2 - altoBoton *2),anchoBoton,altoBoton), "Empezar Juego")){ Application.LoadLevel("Juego"); } if (GUI.Button(Rect((Screen.width -anchoBoton) /2,((Screen.height -altoBoton)/2) - altoBoton,anchoBoton,altoBoton), "Instrucciones")){ print("Este botón nos redirigirá a las instrucciones"); } if (GUI.Button(Rect((Screen.width -anchoBoton) /2,((Screen.height -altoBoton)/2),anchoBoton,altoBoton), "Configuración")){ print("Este botón nos redirigirá a la configuración"); } }
Para que quede más bonito podemos añadirle el mismo cielo que a la pantalla del juego y ponerle una luz, ademas podemos añadirle la mosca con la animación para darle algo más de alegria.
Para pintar la GUI Unity lo hace de forma sencilla mediante UnityGUI. UnityGUI llamá a la función OnGUI durante cada frame (igual que la funcion Update) siempre que el script este habilitado. Y con GUI.Button estamos creando un botón, que cuando sea pulsado realizará la condición que se encuentra dentro del if. Ejecutandolo nos quedará lo siguiente:
Y para finalizar el tutorial vamos a hacer que nos salga un menu similar cuando se acabe la partida, que nos permita reiniciar la partida, mirar las instrucciones y volver al menu principal. Creamos el script GUIJuego y modificamos los scripts GanarJuego y PerderJuego para poner la referencia a estos scripts.
var anchoBoton : int = 320; var altoBoton : int = 80; var juegoAcabado : boolean = false; function OnGUI() { if (juegoAcabado){ if (GUI.Button(Rect((Screen.width -anchoBoton) /2,((Screen.height -altoBoton)/2 - altoBoton *2),anchoBoton,altoBoton), "Reiniciar Juego")){ Application.LoadLevel("Juego"); } if (GUI.Button(Rect((Screen.width -anchoBoton) /2,((Screen.height -altoBoton)/2) - altoBoton,anchoBoton,altoBoton), "Instrucciones")){ print("Este botón nos redirigirá a las instrucciones"); } if (GUI.Button(Rect((Screen.width -anchoBoton) /2,((Screen.height -altoBoton)/2),anchoBoton,altoBoton), "Menu Principal")){ Application.LoadLevel("Menu Principal"); } } }
var guiJuego : GUIJuego; function Awake(){ guiJuego = gameObject.Find("Level Attributes").GetComponent("GUIJuego"); } function Update(){ if (transform.childCount == 0){ GanarPartida(); } } function GanarPartida(){ print("¡Enhorabuena has ganado la partida!"); guiJuego.juegoAcabado = true; }
var naveController : NaveController; var guiJuego : GUIJuego; function Awake(){ naveController = gameObject.Find("Nave").GetComponent("NaveController"); guiJuego = gameObject.Find("Level Attributes").GetComponent("GUIJuego"); } function OnCollisionEnter(collision : Collision){ if (collision.gameObject.name == "Nave"){ PerderPartida(); PararMoscas(); PararNave(); } } function PerderPartida(){ guiJuego.juegoAcabado = true; print("¡Has perdido!"); } function PararMoscas(){ this.gameObject.transform.parent.GetComponent("MoverPadreMoscas").velocidadMovimiento = 0; } function PararNave(){ naveController.naveControlable = false; }
Por último seleccionamos la escena del Menu Principal y vamos a configurar la configuración para hacer el build. Le damos a File > Build Settings y le tenemos que añadir la escena Menu Principal dandole a Add Current. También seleccionamos Web Player y le damos a Switch Platform para seleccionar que queremos hacer el build para navegador web. Veremos que cambia el simbolo de unity al navegador web. Y también tenemos que cambiar el orden de las escenas para que la de Menu Principal sea la primera en ejecutarse.
Con ambas escenas añadidas, le damos a build y elegimos el nombre con el que lo queremos guardar. Se nos creará un archivo html y un archivo unity3d. Si abrimos el archivo html en el navegador podremos probar nuestro juego.
Podemos hacer el build para pc o para mac cambiando la plataforma y eligiendo el sistema operativo, con lo que se nos creará el ejecutable para windows o mac según lo que elijamos.
Y con esto ya tendríamos nuestro juego acabado. En la versión de este tutorial hay algún bug, por ejemplo, las moscas no cambian de dirección si sólo hay dos en esa columna y al ejecutarlo la resolución de la pantalla no se ajusta al tamaño de los limites del nivel, por lo que las moscas se salen de la pantalla al jugar. Hice el juego antes de preparar el tutorial y consegui solventar el problema del cambio de dirección haciendo que tuviese en cuenta el momento en el que impactaban, pero no conseguí que funcionase en este tutorial. Podeís descargaros el primer proyecto aquí.
5. Conclusiones.
Unity 3d es una herramienta muy completa que nos permite crear unos videojuegos impresionantes. En este tutorial se ha hecho un juego muy básico y no se han cubierto todas las posibilidades que ofrece unity, como por ejemplo, crear animaciones, añadir sonidos y un largo etcétera.
Espero que os haya gustado el tutorial y Unity. Si quereís hacer algún comentario, sugerencia o preguntar alguna duda podeís hacerlo en la zona de comentarios.
Un saludo.
César López.
hola! excelente tutorial… todo funciona a la perfeccion… solo tengo una duda: como puedo hacer que aparezcan moscas en modo random, es decir que vayan saliendo de arriba pero en diferentes posiciones??? no se si me doy bien a entender… espero me puedas responder y de nueva cuenta gracias por el tutorial!!!
Buenas, puedes probar a hacer un script que genere las moscas con una posición y fija (cerca del borde superior), y con una posición x aleatoria dentro de los bordes del juego. Lo único que tendrías que cambiar también la forma en la que se mueven las moscas, creando un script para su movimiento y añadiendoselo a las moscas que has creado. Espero que te sirva, un saludo.
buenas
no esta el link para descargar el proyecto i las texturas
odos los recursos que usaremos en nuestro proyecto os los podeís bajar de aquí (poner link).
buenas.. excelente tutorial , el unico problema es que no se pueden descargar los recursos.