Manual de Creacion Juegode laberinto en Unity
Creacion del entorno
Empezaremos creando un objeto 3d tipo Terrain
Utilizaremos la herramienta para la creacion de montañas
Importaremos los arboles del siguiente asset
Rellenaremos el espacio de los arboles con la siguiente herramienta
Aplicaremos al entorno distintos materiales para diversificar el suelo
Crearemos las paredes externas las cuales serán cubos alargados y nos permitirá tener es espacio para la cuadrícula que deseamos realizar
Mediante la siguiente pagina web generamos un laberinto el cual consta de 10 unidades de distancia hacia cada lado para que se ajuste al perímetro que diseñamos
Agregamos cada pared según el formato que hayamos generado
Ya cuando terminemos de crear el laberinto denotaremos el inicio y el final
Agregaremos los materiales a las paredes para darles aspecto de muro
Repetiremos exactamente este mismo proceso para realizar el segundo nivel con el único cambio de generar otro patrón de laberinto
Creación de objetos
Empezaremos creando las puertas que funcionaran para demarcar el final del nivel
A estas les agregaremos un objeto vacio el cual cunplira la funcion de visagra
Cambiaremos el orden de los objetos y dejaremos a la puerta como objeto heredado de la visagra
A esta bisagra le pondremos el siguiente script
<!-- /* Font Definitions */ @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:3 0 0 0 1 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0cm; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Arial",sans-serif; mso-fareast-font-family:Arial; mso-ansi-language:#000A;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:11.0pt; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Arial",sans-serif; mso-ascii-font-family:Arial; mso-fareast-font-family:Arial; mso-hansi-font-family:Arial; mso-bidi-font-family:Arial; mso-ansi-language:#000A;} .MsoPapDefault {mso-style-type:export-only; line-height:115%;} @page WordSection1 {size:612.0pt 792.0pt; margin:70.85pt 3.0cm 70.85pt 3.0cm; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} --> using UnityEngine; using System.Collections; public class DoorScript : MonoBehaviour { public static bool doorKey; public bool open; public bool close; public bool inTrigger; void OnTriggerEnter(Collider other) { inTrigger = true; } void OnTriggerExit(Collider other) { inTrigger = false; } void Update() { if (inTrigger) { if (close) { if (doorKey) { if (Input.GetKeyDown(KeyCode.E)) { open = true; close = false; } } } else { if (Input.GetKeyDown(KeyCode.E)) { close = true; open = false; } } } if (open) { var newRot = Quaternion.RotateTowards(transform.rotation, Quaternion.Euler(0.0f, -90.0f, 0.0f), Time.deltaTime * 200); transform.rotation = newRot; } else { var newRot = Quaternion.RotateTowards(transform.rotation, Quaternion.Euler(0.0f, 0.0f, 0.0f), Time.deltaTime * 200); transform.rotation = newRot; } } void OnGUI() { if (inTrigger) { if (open) { GUI.Box(new Rect(0, 0, 200, 25), "Presiona la tecla E para cerrar"); } else { if (doorKey) { GUI.Box(new Rect(0, 0, 200, 25), "Presiona la tecla E para abrir"); } else { GUI.Box(new Rect(0, 0, 200, 25), "¡Necesitas una llave!"); } } } } }
luego aumentaremos el espacio de choque de la puerta para que sea necesario activar las puertas
copiaremos el mismo proceso para la segunda puerta con la única diferencia de cambiar el script por este
Este es el script de la siguiente puerta
<!-- /* Font Definitions */ @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:3 0 0 0 1 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0cm; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Arial",sans-serif; mso-fareast-font-family:Arial; mso-ansi-language:#000A;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:11.0pt; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Arial",sans-serif; mso-ascii-font-family:Arial; mso-fareast-font-family:Arial; mso-hansi-font-family:Arial; mso-bidi-font-family:Arial; mso-ansi-language:#000A;} .MsoPapDefault {mso-style-type:export-only; line-height:115%;} @page WordSection1 {size:612.0pt 792.0pt; margin:70.85pt 3.0cm 70.85pt 3.0cm; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} --> using UnityEngine; using System.Collections; public class ScriptPuerta : MonoBehaviour { public static bool llavePuerta; public bool open; public bool close; public bool inTrigger; void OnTriggerEnter(Collider other) { inTrigger = true; } void OnTriggerExit(Collider other) { inTrigger = false; } void Update() { if (inTrigger) { if (close) { if (llavePuerta) { if (Input.GetKeyDown(KeyCode.E)) { open = true; close = false; } } } else { if (Input.GetKeyDown(KeyCode.E)) { close = true; open = false; } } } if (open) { var newRot = Quaternion.RotateTowards(transform.rotation, Quaternion.Euler(0.0f, 90.0f, 0.0f), Time.deltaTime * 200); transform.rotation = newRot; } else { var newRot = Quaternion.RotateTowards(transform.rotation, Quaternion.Euler(0.0f, 0.0f, 0.0f), Time.deltaTime * 200); transform.rotation = newRot; } } void OnGUI() { if (inTrigger) { if (open) { GUI.Box(new Rect(0, 0, 200, 25), "Presiona la tecla E para cerrar"); } else { if (llavePuerta) { GUI.Box(new Rect(0, 0, 200, 25), "Presiona la tecla E para abrir"); } else { GUI.Box(new Rect(0, 0, 200, 25), "?Necesitas una llave!"); } } } } }
Luego crearemos el objeto de llave, tanto la idea de la puerta como la llave provienen de los assets creados por el siguiente video
Este seria el link del video https://www.youtube.com/watch?v=KL6rlUGucE4
Crearemos dos objetos llaves y a cada uno le asignaremos su respectivo script, luego de esto buscaremos las llaves en las esquinas opuestas del mapa para darle mayor recorrido a los usuarios
Llave 1
El script de la llave es este
<!-- /* Font Definitions */ @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:3 0 0 0 1 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0cm; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Arial",sans-serif; mso-fareast-font-family:Arial; mso-ansi-language:#000A;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:11.0pt; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Arial",sans-serif; mso-ascii-font-family:Arial; mso-fareast-font-family:Arial; mso-hansi-font-family:Arial; mso-bidi-font-family:Arial; mso-ansi-language:#000A;} .MsoPapDefault {mso-style-type:export-only; line-height:115%;} @page WordSection1 {size:612.0pt 792.0pt; margin:70.85pt 3.0cm 70.85pt 3.0cm; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} --> using UnityEngine; using System.Collections; public class DoorKey : MonoBehaviour { public bool inTrigger; public bool llaveObtenida2 = false; void OnTriggerEnter(Collider other) { inTrigger = true; } void OnTriggerExit(Collider other) { inTrigger = false; } void Update() { if (inTrigger) { if (Input.GetKeyDown(KeyCode.E)) { DoorScript.doorKey = true; Destroy(this.gameObject); llaveObtenida2 = true; } } } void OnGUI() { if (inTrigger) { GUI.Box(new Rect(0, 60, 200, 25), "Presione la E para obtener la llave"); } } }
Llave 2
El script de la llave es este
<!-- /* Font Definitions */ @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:3 0 0 0 1 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0cm; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Arial",sans-serif; mso-fareast-font-family:Arial; mso-ansi-language:#000A;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:11.0pt; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Arial",sans-serif; mso-ascii-font-family:Arial; mso-fareast-font-family:Arial; mso-hansi-font-family:Arial; mso-bidi-font-family:Arial; mso-ansi-language:#000A;} .MsoPapDefault {mso-style-type:export-only; line-height:115%;} @page WordSection1 {size:612.0pt 792.0pt; margin:70.85pt 3.0cm 70.85pt 3.0cm; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} --> using UnityEngine; using System.Collections; public class llavePuerta : MonoBehaviour { public bool inTrigger; public bool llaveObtenida1 = false; void OnTriggerEnter(Collider other) { inTrigger = true; } void OnTriggerExit(Collider other) { inTrigger = false; } void Update() { if (inTrigger) { if (Input.GetKeyDown(KeyCode.E)) { ScriptPuerta.llavePuerta = true; Destroy(this.gameObject); llaveObtenida1 = true; } } } void OnGUI() { if (inTrigger) { GUI.Box(new Rect(0, 1, 1, 1), "Presione E para tomar la llave"); } } }
Crearemos en el primer mapa el objeto que me permitira pasar al siguiente nivel
Asignaremos que el objeto es trigger y le pondremos el siguiente script
using UnityEngine; using System.Collections; using UnityEngine.SceneManagement; public class nextLevel : MonoBehaviour { void OnTriggerEnter(Collider other) { if (other.tag == "Player") { SceneManager.LoadScene("Nivel2"); } } }
Para que ese proceso funcione correctamente primero debemos construirlo
Ya terminando este objeto, creando tanto las puertas como las llaves en ambos niveles crearemos el premio final el cual nos permitirá observar la pantalla de juego terminado satisfactoriamente
A este objeto le colocaremos el siguiente script
<!-- /* Font Definitions */ @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:3 0 0 0 1 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0cm; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Arial",sans-serif; mso-fareast-font-family:Arial; mso-ansi-language:#000A;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:11.0pt; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Arial",sans-serif; mso-ascii-font-family:Arial; mso-fareast-font-family:Arial; mso-hansi-font-family:Arial; mso-bidi-font-family:Arial; mso-ansi-language:#000A;} .MsoPapDefault {mso-style-type:export-only; line-height:115%;} @page WordSection1 {size:612.0pt 792.0pt; margin:70.85pt 3.0cm 70.85pt 3.0cm; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} --> using UnityEngine; using System.Collections; using UnityEngine.SceneManagement; public class Ganar : MonoBehaviour { public bool inTrigger; void OnTriggerEnter(Collider other) { inTrigger = true; } void OnTriggerExit(Collider other) { inTrigger = false; } void Update() { if (inTrigger) { if (Input.GetKeyDown(KeyCode.E)) { Destroy(this.gameObject); SceneManager.LoadScene("Final"); } } } void OnGUI() { if (inTrigger) { GUI.Box(new Rect(0, 60, 200, 25), "Presione E para tomar el trofeo"); } } }
Creacion del personaje
Para la creacion de nuestro personaje utilizaremos los siguientes assets
Utilizaremos un asset el cual nos brinda las armas, este se trae gracias a este video
Insertaremos el siguiente personaje el cual obtenemos mediante el siguiente video este ya posee los scripts de movimiento y las manos que se pueden observar
Los scripts que posee este personaje son los siguientes
<!-- /* Font Definitions */ @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:3 0 0 0 1 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0cm; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Arial",sans-serif; mso-fareast-font-family:Arial; mso-ansi-language:#000A;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:11.0pt; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Arial",sans-serif; mso-ascii-font-family:Arial; mso-fareast-font-family:Arial; mso-hansi-font-family:Arial; mso-bidi-font-family:Arial; mso-ansi-language:#000A;} .MsoPapDefault {mso-style-type:export-only; line-height:115%;} @page WordSection1 {size:612.0pt 792.0pt; margin:70.85pt 3.0cm 70.85pt 3.0cm; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} --> FirstPersonController using System; using UnityEngine; using UnityStandardAssets.CrossPlatformInput; using UnityStandardAssets.Utility; using Random = UnityEngine.Random; namespace UnityStandardAssets.Characters.FirstPerson { [RequireComponent(typeof (CharacterController))] [RequireComponent(typeof (AudioSource))] public class FirstPersonController : MonoBehaviour { [SerializeField] private bool m_IsWalking; [SerializeField] private float m_WalkSpeed; [SerializeField] private float m_RunSpeed; [SerializeField] [Range(0f, 1f)] private float m_RunstepLenghten; [SerializeField] private float m_JumpSpeed; [SerializeField] private float m_StickToGroundForce; [SerializeField] private float m_GravityMultiplier; [SerializeField] private MouseLook m_MouseLook; [SerializeField] private bool m_UseFovKick; [SerializeField] private FOVKick m_FovKick = new FOVKick(); [SerializeField] private bool m_UseHeadBob; [SerializeField] private CurveControlledBob m_HeadBob = new CurveControlledBob(); [SerializeField] private LerpControlledBob m_JumpBob = new LerpControlledBob(); [SerializeField] private float m_StepInterval; [SerializeField] private AudioClip[] m_FootstepSounds; // an array of footstep sounds that will be randomly selected from. [SerializeField] private AudioClip m_JumpSound; // the sound played when character leaves the ground. [SerializeField] private AudioClip m_LandSound; // the sound played when character touches back on ground. private Camera m_Camera; private bool m_Jump; private float m_YRotation; private Vector2 m_Input; private Vector3 m_MoveDir = Vector3.zero; private CharacterController m_CharacterController; private CollisionFlags m_CollisionFlags; private bool m_PreviouslyGrounded; private Vector3 m_OriginalCameraPosition; private float m_StepCycle; private float m_NextStep; private bool m_Jumping; private AudioSource m_AudioSource; // Use this for initialization private void Start() { m_CharacterController = GetComponent<CharacterController>(); m_Camera = Camera.main; m_OriginalCameraPosition = m_Camera.transform.localPosition; m_FovKick.Setup(m_Camera); m_HeadBob.Setup(m_Camera, m_StepInterval); m_StepCycle = 0f; m_NextStep = m_StepCycle/2f; m_Jumping = false; m_AudioSource = GetComponent<AudioSource>(); m_MouseLook.Init(transform , m_Camera.transform); } // Update is called once per frame private void Update() { RotateView(); // the jump state needs to read here to make sure it is not missed if (!m_Jump) { m_Jump = CrossPlatformInputManager.GetButtonDown("Jump"); } if (!m_PreviouslyGrounded && m_CharacterController.isGrounded) { StartCoroutine(m_JumpBob.DoBobCycle()); PlayLandingSound(); m_MoveDir.y = 0f; m_Jumping = false; } if (!m_CharacterController.isGrounded && !m_Jumping && m_PreviouslyGrounded) { m_MoveDir.y = 0f; } m_PreviouslyGrounded = m_CharacterController.isGrounded; } private void PlayLandingSound() { m_AudioSource.clip = m_LandSound; m_AudioSource.Play(); m_NextStep = m_StepCycle + .5f; } private void FixedUpdate() { float speed; GetInput(out speed); // always move along the camera forward as it is the direction that it being aimed at Vector3 desiredMove = transform.forward*m_Input.y + transform.right*m_Input.x; // get a normal for the surface that is being touched to move along it RaycastHit hitInfo; Physics.SphereCast(transform.position, m_CharacterController.radius, Vector3.down, out hitInfo, m_CharacterController.height/2f, Physics.AllLayers, QueryTriggerInteraction.Ignore); desiredMove = Vector3.ProjectOnPlane(desiredMove, hitInfo.normal).normalized; m_MoveDir.x = desiredMove.x*speed; m_MoveDir.z = desiredMove.z*speed; if (m_CharacterController.isGrounded) { m_MoveDir.y = -m_StickToGroundForce; if (m_Jump) { m_MoveDir.y = m_JumpSpeed; PlayJumpSound(); m_Jump = false; m_Jumping = true; } } else { m_MoveDir += Physics.gravity*m_GravityMultiplier*Time.fixedDeltaTime; } m_CollisionFlags = m_CharacterController.Move(m_MoveDir*Time.fixedDeltaTime); ProgressStepCycle(speed); UpdateCameraPosition(speed); m_MouseLook.UpdateCursorLock(); } private void PlayJumpSound() { m_AudioSource.clip = m_JumpSound; m_AudioSource.Play(); } private void ProgressStepCycle(float speed) { if (m_CharacterController.velocity.sqrMagnitude > 0 && (m_Input.x != 0 || m_Input.y != 0)) { m_StepCycle += (m_CharacterController.velocity.magnitude + (speed*(m_IsWalking ? 1f : m_RunstepLenghten)))* Time.fixedDeltaTime; } if (!(m_StepCycle > m_NextStep)) { return; } m_NextStep = m_StepCycle + m_StepInterval; PlayFootStepAudio(); } private void PlayFootStepAudio() { if (!m_CharacterController.isGrounded) { return; } // pick & play a random footstep sound from the array, // excluding sound at index 0 int n = Random.Range(1, m_FootstepSounds.Length); m_AudioSource.clip = m_FootstepSounds[n]; m_AudioSource.PlayOneShot(m_AudioSource.clip); // move picked sound to index 0 so it's not picked next time m_FootstepSounds[n] = m_FootstepSounds[0]; m_FootstepSounds[0] = m_AudioSource.clip; } private void UpdateCameraPosition(float speed) { Vector3 newCameraPosition; if (!m_UseHeadBob) { return; } if (m_CharacterController.velocity.magnitude > 0 && m_CharacterController.isGrounded) { m_Camera.transform.localPosition = m_HeadBob.DoHeadBob(m_CharacterController.velocity.magnitude + (speed*(m_IsWalking ? 1f : m_RunstepLenghten))); newCameraPosition = m_Camera.transform.localPosition; newCameraPosition.y = m_Camera.transform.localPosition.y - m_JumpBob.Offset(); } else { newCameraPosition = m_Camera.transform.localPosition; newCameraPosition.y = m_OriginalCameraPosition.y - m_JumpBob.Offset(); } m_Camera.transform.localPosition = newCameraPosition; } private void GetInput(out float speed) { // Read input float horizontal = CrossPlatformInputManager.GetAxis("Horizontal"); float vertical = CrossPlatformInputManager.GetAxis("Vertical"); bool waswalking = m_IsWalking; #if !MOBILE_INPUT // On standalone builds, walk/run speed is modified by a key press. // keep track of whether or not the character is walking or running m_IsWalking = !Input.GetKey(KeyCode.LeftShift); #endif // set the desired speed to be walking or running speed = m_IsWalking ? m_WalkSpeed : m_RunSpeed; m_Input = new Vector2(horizontal, vertical); // normalize input if it exceeds 1 in combined length: if (m_Input.sqrMagnitude > 1) { m_Input.Normalize(); } // handle speed change to give an fov kick // only if the player is going to a run, is running and the fovkick is to be used if (m_IsWalking != waswalking && m_UseFovKick && m_CharacterController.velocity.sqrMagnitude > 0) { StopAllCoroutines(); StartCoroutine(!m_IsWalking ? m_FovKick.FOVKickUp() : m_FovKick.FOVKickDown()); } } private void RotateView() { m_MouseLook.LookRotation (transform, m_Camera.transform); } private void OnControllerColliderHit(ControllerColliderHit hit) { Rigidbody body = hit.collider.attachedRigidbody; //dont move the rigidbody if the character is on top of it if (m_CollisionFlags == CollisionFlags.Below) { return; } if (body == null || body.isKinematic) { return; } body.AddForceAtPosition(m_CharacterController.velocity*0.1f, hit.point, ForceMode.Impulse); } } }
Vida
<!-- /* Font Definitions */ @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:3 0 0 0 1 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0cm; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Arial",sans-serif; mso-fareast-font-family:Arial; mso-ansi-language:#000A;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:11.0pt; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Arial",sans-serif; mso-ascii-font-family:Arial; mso-fareast-font-family:Arial; mso-hansi-font-family:Arial; mso-bidi-font-family:Arial; mso-ansi-language:#000A;} .MsoPapDefault {mso-style-type:export-only; line-height:115%;} @page WordSection1 {size:612.0pt 792.0pt; margin:70.85pt 3.0cm 70.85pt 3.0cm; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} --> using System.Collections; using System.Collections.Generic; using UnityEngine; public class Vida : MonoBehaviour { public float valor = 100; public Vida padreRef; public float multiplicadorDeDaño = 1.0f; public GameObject textoFlotantePrefab; public float dañoTotal; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } public void RecibirDaño(float daño) { daño *= multiplicadorDeDaño; if(padreRef != null) { padreRef.RecibirDaño(daño); return; } valor -= daño; dañoTotal = daño; if (valor >= 0) MostrarTextoFlotante(); if(valor < 0) { valor = 0; MostrarTextoFlotante(); } } void MostrarTextoFlotante() { var go = Instantiate(textoFlotantePrefab, transform.position, Quaternion.identity, transform); //go.GetComponent<TextMesh>().text = dañoTotal.ToString(); go.GetComponent<TextMesh>().text = valor.ToString(); } }
LogicaJugador
<!-- /* Font Definitions */ @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:3 0 0 0 1 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0cm; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Arial",sans-serif; mso-fareast-font-family:Arial; mso-ansi-language:#000A;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:11.0pt; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Arial",sans-serif; mso-ascii-font-family:Arial; mso-fareast-font-family:Arial; mso-hansi-font-family:Arial; mso-bidi-font-family:Arial; mso-ansi-language:#000A;} .MsoPapDefault {mso-style-type:export-only; line-height:115%;} @page WordSection1 {size:612.0pt 792.0pt; margin:70.85pt 3.0cm 70.85pt 3.0cm; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} --> using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public class LogicaJugador : MonoBehaviour { public Vida vida; public bool Vida0 = false; [SerializeField] private Animator animadorPerder; // Start is called before the first frame update void Start() { vida = GetComponent<Vida>(); } // Update is called once per frame void Update() { RevisarVida(); } void RevisarVida() { if (Vida0) return; if(vida.valor <=0) { AudioListener.volume = 0f; animadorPerder.SetTrigger("Mostrar"); Vida0 = true; Invoke("ReiniciarJuego", 2f); } } void ReiniciarJuego() { SceneManager.LoadScene(SceneManager.GetActiveScene().name); AudioListener.volume = 1f; } private void OnTriggerStay(Collider other) { if(other.gameObject.tag == "Enemigo") { gameObject.GetComponent<AudioSource>().volume = 0f; } } private void OnTriggerExit(Collider other) { if (other.gameObject.tag == "Enemigo") { gameObject.GetComponent<AudioSource>().volume = 1f; } } }
PantallaVida
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class PantallaVida : MonoBehaviour { public Text texto; public Vida vida; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { texto.text = vida.valor + "/100" ; } }
PantallaLlaves
<!-- /* Font Definitions */ @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:3 0 0 0 1 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0cm; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Arial",sans-serif; mso-fareast-font-family:Arial; mso-ansi-language:#000A;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:11.0pt; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Arial",sans-serif; mso-ascii-font-family:Arial; mso-fareast-font-family:Arial; mso-hansi-font-family:Arial; mso-bidi-font-family:Arial; mso-ansi-language:#000A;} .MsoPapDefault {mso-style-type:export-only; line-height:115%;} @page WordSection1 {size:612.0pt 792.0pt; margin:70.85pt 3.0cm 70.85pt 3.0cm; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} --> using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class PantallaLlaves : MonoBehaviour { public Text texto; public RecolectorDeLlaves llaves; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { texto.text = "LLaves: " +llaves.contadorLlaves + "/2"; } }
Junto a estos Scripts ingresamos las siguientes animaciones para las acciones que ocurrirán en el juego
Creación de enemigos
Para los enemigos debemos insertar el siguiente Asset
Estos son los asset de ellos
Estos son los assets que poseen los enemigos
LogicaEnemigo
<!-- /* Font Definitions */ @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:3 0 0 0 1 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0cm; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Arial",sans-serif; mso-fareast-font-family:Arial; mso-ansi-language:#000A;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:11.0pt; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Arial",sans-serif; mso-ascii-font-family:Arial; mso-fareast-font-family:Arial; mso-hansi-font-family:Arial; mso-bidi-font-family:Arial; mso-ansi-language:#000A;} .MsoPapDefault {mso-style-type:export-only; line-height:115%;} @page WordSection1 {size:612.0pt 792.0pt; margin:70.85pt 3.0cm 70.85pt 3.0cm; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} --> using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; public class LogicaEnemigo : MonoBehaviour { private GameObject target; private NavMeshAgent agente; private Vida vida; private Animator animator; private Collider collider; private Vida vidaJugador; private LogicaJugador logicaJugador; public bool Vida0 = false; public bool estaAtacando = false; public float speed = 1.0f; public float angularSpeed = 120; public float daño = 10; public bool estaMirando; // Start is called before the first frame update void Start() { target = GameObject.Find("Jugador"); vidaJugador = target.GetComponent<Vida>(); if(vidaJugador==null) { throw new System.Exception("El objeto Jugador no tiene componente vida"); } logicaJugador = target.GetComponent<LogicaJugador>(); if (logicaJugador == null) { throw new System.Exception("El objeto Jugador no tiene componente LogicaJugador"); } agente = GetComponent<NavMeshAgent>(); vida = GetComponent<Vida>(); animator = GetComponent<Animator>(); collider = GetComponent<Collider>(); } // Update is called once per frame void Update() { RevisarVida(); Perseguir(); RevisarAtaque(); EstaDeFrenteAlJugador(); } void EstaDeFrenteAlJugador() { Vector3 adelante = transform.forward; Vector3 targetJugador = (GameObject.Find("Jugador").transform.position -transform.position).normalized; if(Vector3.Dot(adelante,targetJugador)<0.6f) { estaMirando = false; } else { estaMirando = true; } } void RevisarVida() { if (Vida0) return; if (vida.valor <= 0) { Vida0 = true; agente.isStopped = true; collider.enabled = false; animator.CrossFadeInFixedTime("Vida0", 0.1f); Destroy(gameObject, 3f); } } void Perseguir() { if (Vida0) return; if (logicaJugador.Vida0) return; agente.destination = target.transform.position; } void RevisarAtaque() { if (Vida0) return; if (estaAtacando) return; if (logicaJugador.Vida0) return; float distanciaDelBlanco = Vector3.Distance(target.transform.position, transform.position); if (distanciaDelBlanco <= 2.0 && estaMirando) { Atacar(); } } void Atacar() { vidaJugador.RecibirDaño(daño); agente.speed = 0; agente.angularSpeed = 0; estaAtacando = true; animator.SetTrigger("DebeAtacar"); Invoke("ReiniciarAtaque", 1.5f); } void ReiniciarAtaque() { estaAtacando = false; agente.speed = speed; agente.angularSpeed = angularSpeed; } }
Vida
<!-- /* Font Definitions */ @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:3 0 0 0 1 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0cm; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Arial",sans-serif; mso-fareast-font-family:Arial; mso-ansi-language:#000A;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:11.0pt; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Arial",sans-serif; mso-ascii-font-family:Arial; mso-fareast-font-family:Arial; mso-hansi-font-family:Arial; mso-bidi-font-family:Arial; mso-ansi-language:#000A;} .MsoPapDefault {mso-style-type:export-only; line-height:115%;} @page WordSection1 {size:612.0pt 792.0pt; margin:70.85pt 3.0cm 70.85pt 3.0cm; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} --> using System.Collections; using System.Collections.Generic; using UnityEngine; public class Vida : MonoBehaviour { public float valor = 100; public Vida padreRef; public float multiplicadorDeDaño = 1.0f; public GameObject textoFlotantePrefab; public float dañoTotal; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } public void RecibirDaño(float daño) { daño *= multiplicadorDeDaño; if(padreRef != null) { padreRef.RecibirDaño(daño); return; } valor -= daño; dañoTotal = daño; if (valor >= 0) MostrarTextoFlotante(); if(valor < 0) { valor = 0; MostrarTextoFlotante(); } } void MostrarTextoFlotante() { var go = Instantiate(textoFlotantePrefab, transform.position, Quaternion.identity, transform); //go.GetComponent<TextMesh>().text = dañoTotal.ToString(); go.GetComponent<TextMesh>().text = valor.ToString(); } }
junto a esto tambien dentro del mapa insertaremos los puntos donde se podran generar los enemigos, los cuales sirven en base a este script
<!-- /* Font Definitions */ @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:3 0 0 0 1 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0cm; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Arial",sans-serif; mso-fareast-font-family:Arial; mso-ansi-language:#000A;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:11.0pt; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Arial",sans-serif; mso-ascii-font-family:Arial; mso-fareast-font-family:Arial; mso-hansi-font-family:Arial; mso-bidi-font-family:Arial; mso-ansi-language:#000A;} .MsoPapDefault {mso-style-type:export-only; line-height:115%;} @page WordSection1 {size:612.0pt 792.0pt; margin:70.85pt 3.0cm 70.85pt 3.0cm; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} --> using System.Collections; using System.Collections.Generic; using UnityEngine; public class GeneradorDeEnemigos : MonoBehaviour { public GameObject zombiePrefab; public Transform[] puntosDeGeneracion; public float tiempoDeGeneracion = 5f; // Start is called before the first frame update void Start() { puntosDeGeneracion = new Transform[transform.childCount]; for(int i =0; i<transform.childCount; i++) { puntosDeGeneracion[i] = transform.GetChild(i); } StartCoroutine(AparecerEnemigo()); } IEnumerator AparecerEnemigo() { while(true) { for(int i = 0; i < puntosDeGeneracion.Length ; i++) { Transform puntodeGeneracion = puntosDeGeneracion[i]; Instantiate(zombiePrefab, puntodeGeneracion.position, puntodeGeneracion.rotation); } yield return new WaitForSeconds(tiempoDeGeneracion); } } // Update is called once per frame void Update() { } }
Creación de Canvas
Para la creación del canvas utilizaremos la imagen brindada por nuestro compañero Daniel Enrique Mayorga García, estudiante de Arte y medios audiovisuales de la Universidad Autónoma de Bucaramanga. junto a esto utilizaremos el siguiente asset que ya nos entrega varias funcionalidades como la de iniciar el juego
Este menu proviene del siguiente asset
Luego de esto crearemos la parte visual que el jugador observa, dentro de esta ubicamos, la cantidad de llaves recogidas para el ingreso, el número de balas que posee, y un punto de mira para saber donde se está apuntando mediante el canvas
Luego diseñaremos la pantalla tanto de game over como la de que se finalizó el juego satisfactoriamente.
Autor: Sebastian Rodriguez Velasquez, Nicolás Esteban Echeverri Sánchez
Editor: Carlos Iván Pinzón
Código: UCRV-8
Universidad: Universidad Central