Este documento detalla los pasos iniciales y conceptos clave para trabajar con scripts en Unity, abarcando la creación, el comportamiento de los objetos, el movimiento, la rotación y la interacción básica.
Creación del Primer Script
Para comenzar a programar en Unity, sigue estos pasos:
-
Añadir un script: Haz clic derecho en la ventana del Proyecto, selecciona Create > C# Script. El nombre del archivo definirá la clase del script (ej.
GestorMovimiento). -
Verificar la concordancia: Abre el script en tu editor de código (como Visual Studio). Asegúrate de que el nombre de la clase coincida exactamente con el nombre del archivo.
-
Escribir código inicial: Dentro del método
Start(), que se ejecuta una vez al inicio, puedes añadir una instrucción de registro. Por ejemplo: ```using UnityEngine;
public class GestorMovimiento : MonoBehaviour { void Start() { Debug.Log("¡Mi primer script ha sido ejecutado!"); } }
`Debug.Log()` es una herramienta útil de la API de Unity para mostrar mensajes en la consola. -
Guardar y compilar: Guarda el archivo (Ctrl+S) y cierra el editor. Unity compilará automáticamente el script al detectar cambios.
-
Asignar el script a un objeto: Puedes adjuntar el script a un GameObject de dos maneras:
- Selecciona el GameObject, haz clic en Add Component y busca tu script en la sección Scripts.
- Arrastra directamente el archivo del script desde la ventana del Proyecto a la ventana del Inspector del GameObject seleccionado.
-
Ejecutar y observar: Presiona el botón de Play en Unity. Si la ventana de Console no está visible, puedes abrirla desde Window > General > Console. Deberías ver el mensaje que añadiste. Detén la ejecución para continuar.
Posicionamiento y Movimiento de Objetos
Unity maneja las coordenadas de los objetos en un espacio 3D. El movimiento se puede lograr de diversas formas, incluyendo el movimiento relativo y la dirección basada en la orientación del objeto.
Movimiento Basado en la Orientación y Distancia
El siguiente código hace que un objeto se mueva hacia un objetivo específico, ajustando su dirección y distancia.
using UnityEngine;
public class SeguidorObjeto : MonoBehaviour
{
public string nombreObjetivo = "Objetivo"; // Nombre del objeto a seguir
private Transform transformObjetivo;
void Start()
{
// Busca el objeto por su nombre
GameObject objObjetivo = GameObject.Find(nombreObjetivo);
if (objObjetivo != null)
{
transformObjetivo = objObjetivo.transform;
// Orienta el objeto hacia el objetivo al inicio
this.gameObject.transform.LookAt(transformObjetivo);
}
else
{
Debug.LogError($"No se encontró el objeto con el nombre: {nombreObjetivo}");
}
}
void Update()
{
if (transformObjetivo != null)
{
// Mueve el objeto hacia adelante en su espacio local
float velocidadMovimiento = 3.0f;
float distanciaEsteFrame = velocidadMovimiento * Time.deltaTime;
this.gameObject.transform.Translate(0, 0, distanciaEsteFrame, Space.Self);
}
}
}
Ejercicio: Seguir hasta una Distancia Detreminada
Este ejemplo extiende el anterior para que el objeto se detenga cuando esté lo suficientemente cerca del objetivo.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SeguidorConDistancia : MonoBehaviour
{
public string nombreObjetivo = "Objetivo";
public float distanciaMinima = 0.3f; // Distancia para detenerse
private Transform transformObjetivo;
void Start()
{
GameObject objObjetivo = GameObject.Find(nombreObjetivo);
if (objObjetivo != null)
{
transformObjetivo = objObjetivo.transform;
this.gameObject.transform.LookAt(transformObjetivo);
}
else
{
Debug.LogError($"No se encontró el objeto con el nombre: {nombreObjetivo}");
}
}
void Update()
{
if (transformObjetivo != null)
{
Vector3 posicionActual = this.gameObject.transform.position;
Vector3 posicionObjetivo = transformObjetivo.position;
Vector3 diferenciaVector = posicionActual - posicionObjetivo;
float distanciaAlObjetivo = diferenciaVector.magnitude;
if (distanciaAlObjetivo > distanciaMinima)
{
float velocidadMovimiento = 3.0f;
float distanciaEsteFrame = velocidadMovimiento * Time.deltaTime;
this.gameObject.transform.Translate(0, 0, distanciaEsteFrame, Space.Self);
}
}
}
}
Nota: La función GameObject.Find() es sensible a mayúsculas y minúsculas y puede ser lenta si se usa repetidamente. Para objetos con nombres duplicados o en jerarquías complejas, es preferible usar otros métodos de obtención.
Truco: Al seleccionar la opción "Lock View" en la ventana de Scene, la cámara seguirá al objeto seleccionado automáticamente.
Rotación de Objetos
Los objetos en Unity pueden rotar sobre sus propios ejes o en relación con otros objetos.
Rotación Continua en el Eje Y (Local)
Este script hace que un objeto rote continuamente alrededor de su eje Y local.
using UnityEngine;
public class RotadorLocal : MonoBehaviour
{
void Start()
{
// Limita la tasa de frames a 60 por segundo
Application.targetFrameRate = 60;
}
void Update()
{
float velocidadRotacion = 60.0f; // Grados por segundo
// Rota el objeto en su eje Y local
this.gameObject.transform.Rotate(0, velocidadRotacion * Time.deltaTime, 0, Space.Self);
}
}
Sistema Tierra-Luna (Rotación Relativa)
Simula el movimiento orbital y de rotación de planetas.
// Script para el Planeta (ej. Tierra)
using UnityEngine;
public class LogicaPlaneta : MonoBehaviour
{
void Start()
{
Application.targetFrameRate = 60;
}
void Update()
{
float velocidadRotacionPlaneta = 10.0f;
// Rota el planeta sobre sí mismo
this.transform.Rotate(0, velocidadRotacionPlaneta * Time.deltaTime, 0, Space.Self);
}
}
// Script para la Luna
using UnityEngine;
public class LogicaLuna : MonoBehaviour
{
void Start()
{
Application.targetFrameRate = 60;
}
void Update()
{
float velocidadOrbitaLuna = 30.0f;
// Rota el objeto padre (que contiene al planeta y la luna)
Transform padre = this.transform.parent;
if (padre != null)
{
padre.Rotate(0, velocidadOrbitaLuna * Time.deltaTime, 0, Space.Self);
}
}
}
Para este sistema, la Luna debería ser un hijo del Planeta en la jerarquía, y el script de la Luna se aplicaría al objeto padre (el Planeta) para simular la órbita.
Parámetros y Referencias en Scripts
Los scripts pueden exponer variables públicamente para ser configuradas desde el Inspector de Unity.
Parámetros Públicos y Tooltips
Las variables declaradas como public aparecen en el Inspector. La directiva [Tooltip()] añade una descripción emergente al pasar el ratón sobre el parámetro.
using UnityEngine;
public class ParametrosRotacion : MonoBehaviour
{
[Tooltip("Velocidad de rotación en grados por segundo.")]
public float velocidadRotacion = 50.0f;
void Update()
{
this.transform.Rotate(0, velocidadRotacion * Time.deltaTime, 0, Space.Self);
}
}
Tipos de Referencia
Se pueden asignar referencias a otros componentes u objetos de Unity directamente en el Inspector, arrastrando el objeto deseado al campo correspondiente.
Depuración y Modificación de Parámetros en Tiempo de Ejecución
Unity permite modificar los valores de las variables públicas mientras el juego está en ejecución, pero estos cambios se pierden al detener la simulación. Para conservarlos temporalmente, se puede copiar y pegar los valores entre la ejecución y la edición.
Entrada del Usuario (Teclado y Ratón)
Entrada del Ratón
Los eventos del ratón se activan al hacer clic en la ventana de Game.
Posición del Ratón
Se puede obtener la posición del cursor en coordenadas de pantalla.
Entrada del Teclado
Detectar pulsaciones de teclas permite controlar acciones en el juego.
using UnityEngine;
public class ControlMovimientoTeclado : MonoBehaviour
{
public float velocidadMovimiento = 0f;
public float velocidadBase = 5.0f; // Velocidad base de movimiento
void Update()
{
velocidadMovimiento = 0;
// Detecta si la tecla 'W' está siendo presionada continuamente
if (Input.GetKey(KeyCode.W))
{
velocidadMovimiento = velocidadBase;
}
// Detecta si la tecla 'S' está siendo presionada continuamente
else if (Input.GetKey(KeyCode.S))
{
velocidadMovimiento = -velocidadBase;
}
// Mueve el objeto hacia adelante/atrás en su eje Z local
this.gameObject.transform.Translate(0, 0, velocidadMovimiento * Time.deltaTime, Space.Self);
}
}
Nota: Input.GetKey() detecta la pulsación cotninua, mientras que Input.GetKeyDown() solo se activa una vez al presionar la tecla.
Invocación de Componentes y Métodos
Referencia a Componentes de Audio
Se puede obtener una referencia a un componente AudioSource para reproducir sonidos.
Play() vs PlayOneShot():
| Característica | audio.Play() |
audio.PlayOneShot() |
|---|---|---|
| Exclusividad | Interrumpe el audio que se está reproduciendo en el mismo AudioSource. |
No interrumpe, se reproduce superpuesto al audio actual. |
| Control de Volumen | Controla el volumen global del AudioSource mediante audio.volume. |
Controla el volumen de la reproducción actual mediante el parámetro volumeScale, sin afectar el volumen base. |
| Casos de Uso | Adecuado para sonidos únicos y exclusivos (música de fondo, diálogos principales). | Ideal para sonidos cortos, frecuentes y superponibles (disparos, pasos, clics de UI). |
Usar PlayOneShot() es beneficioso para reproducir múltiples efectos de sonido sin interrupciones.
using UnityEngine;
public class ReproductorSonidos : MonoBehaviour
{
public AudioClip clipExito; // Clip de audio para el éxito
public AudioClip clipFallo; // Clip de audio para el fallo
void Update()
{
AudioSource fuenteAudio = GetComponent<audiosource>();
// Reproduce el clip de éxito al presionar 'A'
if (Input.GetKeyDown(KeyCode.A))
{
if (fuenteAudio != null && clipExito != null)
{
fuenteAudio.PlayOneShot(clipExito);
}
}
// Reproduce el clip de fallo al presionar 'B'
if (Input.GetKeyDown(KeyCode.B))
{
if (fuenteAudio != null && clipFallo != null)
{
fuenteAudio.PlayOneShot(clipFallo);
}
}
}
}
</audiosource>
Arrays de Recursos (AudioClips)
Se pueden gestionar múltiples recursos, como canciones, en un array.
using UnityEngine;
public class CajaMusical : MonoBehaviour
{
public AudioClip[] listaCanciones; // Array de clips de audio
private AudioSource reproductor;
void Start()
{
reproductor = GetComponent<audiosource>();
if (listaCanciones == null || listaCanciones.Length == 0)
{
Debug.LogWarning("No hay canciones asignadas en la lista.");
}
}
void Update()
{
// Cambia de canción al hacer clic con el ratón izquierdo
if (Input.GetMouseButtonDown(0))
{
CambiarCancionAleatoria();
}
}
void CambiarCancionAleatoria()
{
if (listaCanciones != null && listaCanciones.Length > 0 && reproductor != null)
{
int indiceAleatorio = Random.Range(0, listaCanciones.Length);
AudioClip cancionSeleccionada = listaCanciones[indiceAleatorio];
reproductor.clip = cancionSeleccionada;
reproductor.Play(); // Usa Play() para reemplazar la canción actual
Debug.Log($"Reproduciendo: {cancionSeleccionada.name} (Índice: {indiceAleatorio})");
}
}
}
</audiosource>
Llamadas Temporizadas
Unity permite ejecutar métodos después de un cierto retardo o de forma repetida.
using UnityEngine;
public class TemporizadorAcciones : MonoBehaviour
{
void Start()
{
Debug.Log($"Inicio del juego. Tiempo actual: {Time.time}");
// Llama a DoSomething después de 1 segundo
// this.Invoke("DoSomething", 1.0f);
// Llama a DoSomething cada 0.5 segundos, comenzando después de 1 segundo
this.InvokeRepeating("DoSomething", 1.0f, 0.5f);
}
void Update()
{
// El método Update se ejecuta en cada frame
}
private void DoSomething()
{
Debug.Log($"Acción ejecutada. Tiempo actual: {Time.time}");
}
}
Ejercicio: Semáforo (Cambio de Luces)
Simula el ciclo de luces de un semáforo.
using UnityEngine;
public class LogicaSemaforo : MonoBehaviour
{
public Material[] materialesLuces; // Array de materiales para Rojo, Amarillo, Verde
private int indiceActual = 0;
private MeshRenderer renderizador;
void Start()
{
renderizador = GetComponent<meshrenderer>();
if (materialesLuces == null || materialesLuces.Length == 0)
{
Debug.LogError("Materiales de luces no asignados.");
return;
}
CambiarLuz(); // Inicia el ciclo
}
void Update()
{
// El ciclo de luces se maneja mediante Invoke
}
private void CambiarLuz()
{
if (renderizador != null && materialesLuces.Length > 0)
{
renderizador.material = materialesLuces[indiceActual];
Debug.Log($"Luz cambiada a: {materialesLuces[indiceActual].name}. Tiempo: {Time.time}");
float tiempoEspera;
// Determina el tiempo de espera según el color
switch (indiceActual)
{
case 0: // Rojo
tiempoEspera = 4.0f;
break;
case 1: // Amarillo
tiempoEspera = 1.5f;
break;
case 2: // Verde
tiempoEspera = 4.0f;
break;
default:
tiempoEspera = 1.0f; // Por seguridad
break;
}
// Programa el próximo cambio de luz
Invoke("CambiarLuz", tiempoEspera);
}
// Avanza al siguiente índice, ciclando si es necesario
indiceActual++;
if (indiceActual >= materialesLuces.Length)
{
indiceActual = 0;
}
}
}
</meshrenderer>
Importante: La llamada inicial a CambiarLuz() debe estar en Start(), no en Update().
Ejercicio: Aceleración y Deceleración (Ventilador)
Controla la velocidad de rotación de un objeto, simulando aceleración y desaceleración.
using UnityEngine;
public class LogicaVentilador : MonoBehaviour
{
public float velocidadActual = 0f;
public float velocidadMaxima = 720f; // RPM máximas
public bool acelerando = false; // Indica si está acelerando o desacelerando
// Intervalo de tiempo para ajustar la velocidad
public float intervaloJustaVelocidad = 0.1f;
// Incremento/decremento de velocidad por intervalo
public float cambioVelocidadPorIntervalo = 10f;
void Start()
{
// Programa la función para ajustar la velocidad periódicamente
InvokeRepeating("JustificarVelocidad", intervaloJustaVelocidad, intervaloJustaVelocidad);
}
void Update()
{
// Cambia el estado de aceleración al hacer clic
if (Input.GetMouseButtonDown(0))
{
acelerando = !acelerando;
}
// Aplica la rotación si la velocidad es mayor que cero
if (velocidadActual > 0)
{
this.transform.Rotate(0, velocidadActual * Time.deltaTime, 0, Space.Self);
}
}
void JustificarVelocidad()
{
if (acelerando)
{
// Acelera hasta la velocidad máxima
if (velocidadActual < velocidadMaxima)
{
velocidadActual += cambioVelocidadPorIntervalo;
// Asegura que no exceda la máxima
velocidadActual = Mathf.Min(velocidadActual, velocidadMaxima);
}
}
else
{
// Desacelera hasta cero
if (velocidadActual > 0)
{
velocidadActual -= cambioVelocidadPorIntervalo;
// Asegura que no sea negativa
velocidadActual = Mathf.Max(velocidadActual, 0f);
}
}
}
}
Vectores en Unity
Los vectores son fundamentales para representar direcciones y magnitudes en el espacio 3D.
- Un vector (0,0,0) normalizado sigue siando (0,0,0).
- Permiten realizar operaciones como suma, resta y escalado.
- Se utilizan para calcular distancias y direcciones entre puntos.
Uso de Vectores para el Movimiento
Se puede definir una velocidad como un vector y aplicarla al movimiento del objeto.
using UnityEngine;
public class MovimientoVectorial : MonoBehaviour
{
// Define la velocidad como un vector editable en el Inspector
public Vector3 velocidadTranslacion;
void Update()
{
// Aplica el vector de velocidad para trasladar el objeto en su espacio local
// Multiplicado por Time.deltaTime para que sea independiente de la tasa de frames
this.transform.Translate(velocidadTranslacion * Time.deltaTime, Space.Self);
}
}
Al definir un parámetro de tipo Vector3 como público, Unity proporciona campos para editar los componentes X, Y y Z directamente en el Inspector.