using UnityEngine; // Este script controla la nave del jugador: cómo se mueve por la pantalla y cómo dispara. // Hay que arrastrarlo (o agregarlo con Add Component) sobre el GameObject "Player". public class PlayerController : MonoBehaviour { [Header("Movimiento")] // Qué tan rápido se mueve la nave (unidades por segundo). // Al ser "public", este valor aparece en el Inspector y se puede ajustar sin tocar código. public float moveSpeed = 8f; // Un pequeño margen para que la nave no quede "cortada" justo en el borde de la pantalla. public float edgePadding = 0.4f; [Header("Disparo")] // El prefab de la bala que vamos a crear cada vez que se dispara. // Se asigna arrastrándolo desde Assets/Prefabs hasta este campo en el Inspector. public GameObject bulletPrefab; // El punto exacto de la nave desde donde "nace" cada bala (la punta de la nave). public Transform firePoint; // Cuántos segundos mínimo deben pasar entre un disparo y el siguiente. public float fireRate = 0.3f; [Header("Vida")] public int vidaMaxima = 3; // Estas variables guardan los límites de la pantalla (izquierda, derecha, abajo, arriba) // en coordenadas del mundo del juego. Son "private" porque solo las usa este script. private float minX, maxX, minY, maxY; // Guarda el momento (según Time.time) en el que se vuelve a poder disparar. private float proximoDisparoDisponible; // Cuánta vida le queda a la nave en este momento (empieza igual a vidaMaxima). private int vidaActual; // Start() se ejecuta una sola vez, apenas empieza el juego. // Acá aprovechamos para calcular los límites de la pantalla y dejar la vida lista. void Start() { CalcularLimitesDePantalla(); vidaActual = vidaMaxima; GameManager.instancia.ActualizarVida(vidaActual, vidaMaxima); } // Calcula en qué coordenadas del mundo empiezan y terminan los bordes de la cámara. // Así, más abajo, podemos evitar que la nave se salga de la vista del jugador. void CalcularLimitesDePantalla() { Camera camara = Camera.main; // ViewportToWorldPoint convierte una posición "de pantalla" (0,0 = esquina inferior // izquierda; 1,1 = esquina superior derecha) a una posición real del mundo del juego. Vector3 esquinaInferior = camara.ViewportToWorldPoint(new Vector3(0f, 0f, 0f)); Vector3 esquinaSuperior = camara.ViewportToWorldPoint(new Vector3(1f, 1f, 0f)); // Guardamos los bordes, dejando un pequeño margen (edgePadding) hacia adentro. minX = esquinaInferior.x + edgePadding; maxX = esquinaSuperior.x - edgePadding; minY = esquinaInferior.y + edgePadding; maxY = esquinaSuperior.y - edgePadding; } // Update() se ejecuta una vez por cada frame (muchas veces por segundo). // Separamos el trabajo en dos métodos (Mover y Disparar) para que quede ordenado. void Update() { Mover(); Disparar(); } void Mover() { // Input.GetAxisRaw("Horizontal") devuelve: // -1 si se presiona A o la flecha izquierda // +1 si se presiona D o la flecha derecha // 0 si no se presiona nada // Input.GetAxisRaw("Vertical") funciona igual, pero con W/S y las flechas arriba/abajo. // Estos dos ejes ya vienen configurados en Unity por defecto: no hay que tocar nada más. float horizontal = Input.GetAxisRaw("Horizontal"); float vertical = Input.GetAxisRaw("Vertical"); // Armamos un vector de dirección con esos dos valores. // .normalized evita que la nave vaya más rápido al moverse en diagonal // (sin esto, moverse en diagonal sumaría velocidad en X y en Y al mismo tiempo). Vector3 direccion = new Vector3(horizontal, vertical, 0f).normalized; // Calculamos la nueva posición: la posición actual, más la dirección, // multiplicada por la velocidad y por Time.deltaTime. // Time.deltaTime es el tiempo (en segundos) que pasó desde el último frame. // Multiplicar por Time.deltaTime hace que la nave se mueva a la misma velocidad // sin importar qué tan rápido o lento vaya el juego en cada computador. Vector3 nuevaPosicion = transform.position + direccion * moveSpeed * Time.deltaTime; // Mathf.Clamp "encierra" un valor entre un mínimo y un máximo. // Acá lo usamos para que la nave nunca salga de los límites de la pantalla. nuevaPosicion.x = Mathf.Clamp(nuevaPosicion.x, minX, maxX); nuevaPosicion.y = Mathf.Clamp(nuevaPosicion.y, minY, maxY); // Por último, aplicamos la nueva posición calculada a la nave. transform.position = nuevaPosicion; } void Disparar() { // Input.GetKey (a diferencia de Input.GetKeyDown) devuelve true todo el rato que la // tecla está apretada, por eso además controlamos un "tiempo de espera" (fireRate) // para que no salga una bala nueva en cada frame. if (Input.GetKey(KeyCode.Space) && Time.time >= proximoDisparoDisponible) { proximoDisparoDisponible = Time.time + fireRate; // Instantiate crea una copia nueva del prefab bulletPrefab, // ubicada en la posición y rotación de firePoint (la punta de la nave). Instantiate(bulletPrefab, firePoint.position, firePoint.rotation); } } // Los objetos que dañan a la nave (meteoritos, balas enemigas, enemigos que chocan // de frente) llaman a este método público desde su propio OnTriggerEnter2D, // pasándole cuánta vida deben quitar. public void RecibirDano(int cantidad) { vidaActual -= cantidad; vidaActual = Mathf.Max(vidaActual, 0); // nunca dejamos que la vida baje de 0 GameManager.instancia.ActualizarVida(vidaActual, vidaMaxima); if (vidaActual <= 0) { Morir(); } } // Algunos peligros (como los meteoritos) no hacen daño parcial: matan de una sola vez. // En vez de duplicar la lógica de "matar", reutilizamos RecibirDano quitando toda la vida. public void MorirInstantaneamente() { RecibirDano(vidaMaxima); } void Morir() { // Todavía no armamos la pantalla de Game Over (eso es el Módulo 7). // Por ahora, avisamos al GameManager y apagamos la nave. GameManager.instancia.TerminarJuego(); gameObject.SetActive(false); } }