Publicado por

Entrega PEC2 – Programación de videojuegos 2D aula 1

Bienvenida

Bienvenido a mi Folio, les adjunto el trabajo de la PEC02 de programación de videojuegos 2D.

Git: https://gitlab.com/jantoncoy/2d-pec02.git

Juego WebGL: www.superninjabros.crzycats.com

Copia del Readme

Como jugar

Para este juego tiene que utilizar tres botones:

  • A -> Ir hacia la izquierda
  • D -> Ir hacia la derecha
  • W -> Saltar

Pasos para comenzar el juego:

  1. Iniciar el ejecutable del juego.

  2. Cuando se inicie el juego estara en la escena Menu donde tiene que pulsar encima del texto para empezar a jugar.

  3. Debera pasarse el nivel y llegar al castillo del final para salir victorioso.

  4. Si muere puede presionar para volver a empezar el nivel.

Video

Enlace al video

Instaladores

Partes importantes del codigo

Estructura del proyecto

  • Assets
  • Scenes: En esta carpeta se encuentra el la scena del juego.
  • Scripts: Se encuentra los scripts de todo el juego.
    • Juego: Los scripts del nivel. (jugador, enemigos, bloques, etc…)
      • Controladores: scripts para los controladores del juego.
      • Ninja: scripts del protagonista.
      • Dinosaurio: scripts del enemigo.
      • Estados: estados que puede tomar el enemigo.
    • Menu: Scripts para los menus del juego.
  • Tiles: Contiene todas las imagenes y tiles del juego.
    • Personajes: Contiene los tiles del protagonista y de los enemigos.
    • Escenario
      • Nivel1: tiles del primer juego.
        • Prefabs: prefabs utilizados para el nivel 1.
      • Paletas: contiene las paletas creadas para los tilemaps.
  • Sonidos: Contiene los sonidos del juego.
    • Juego: Tiene los sonidos del juego.
    • Menus: Tiene los sonidos del menu.

Equivalencia de elementos

En este apartado les mostramos las equivalencias entre los elementos de super mario bros y super ninja bros.

Mapa recreado

Bloques ladrillo

Bloque ?

Mario

Goombas

Super Mushrooms

Protagonista

Archivo: Assets/Scripts/Juego/Ninja/Controlador

Primero leemos los inputs con la libreria de Inputs de unity.

    private void leerInputs()
    {
        bool pulsa = false;

        if (Input.GetKey(KeyCode.Space))
        {
            Debug.Log("Entro Space");
            disparar();
            pulsa = true;
        }
        if (Input.GetKey(KeyCode.W))
        {
            Debug.Log("Entro W");
            saltar();
            pulsa = true;
        }
        if (Input.GetKey(KeyCode.D))
        {
            Debug.Log("Entro D");
            irDerecha();
            pulsa = true;
        }
        if (Input.GetKey(KeyCode.A))
        {
            Debug.Log("Entro A");
            irIzquierda();
            pulsa = true;
        }

        if (!pulsa)
        {
            Debug.Log("No pulso");
            quieto();
        }
    }

Una vez que leemos el input detectamos que accion leer, si se mueve o salta se utiliza la velocidad para darle movimiento constante:

    private void irDerecha()
    {
        if (miraHaciaIzquierda)
        {
            this.transform.eulerAngles = new Vector3(
                this.transform.eulerAngles.x,
                this.transform.eulerAngles.y + 180,
                this.transform.eulerAngles.z
            );
            miraHaciaIzquierda = false;
        }

        rigidBody.velocity = new Vector2(3,rigidBody.velocity.y);

        //Vector2 fuerza = this.transform.right * 30;
        //rigidBody.AddForce(fuerza);

        animator.SetInteger("Estado",2);
    }

Detectamos las colisiones con su funcion de deteccion:

    private void irDerecha()
    {
        if (miraHaciaIzquierda)
        {
            this.transform.eulerAngles = new Vector3(
                this.transform.eulerAngles.x,
                this.transform.eulerAngles.y + 180,
                this.transform.eulerAngles.z
            );
            miraHaciaIzquierda = false;
        }

        rigidBody.velocity = new Vector2(3,rigidBody.velocity.y);

        //Vector2 fuerza = this.transform.right * 30;
        //rigidBody.AddForce(fuerza);

        animator.SetInteger("Estado",2);
    }

Enemigo

Archivo: Assets/Scripts/Juego/Dinosaurio/Dino

Utilizamos el controlador para realizar el movimiento y la coordinacion con su cuerpo, al igual que en el protagonista utilizamos la velocidad. El estado contiene todos los estados de IA que se pueden tener en este caso solo andar.

    // Update is called once per frame
    void Update()
    {
        controladorEstado.procesarEstado();
    }

Detectamos si alguno de los estados toca el limite o es debilitado por los enemigos:

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.collider.name == "LimiteDerecho"
        || collision.collider.name == "Muerte"
        || collision.otherCollider.name == "Muerte")
        {
            eliminar();
        }

        if (collision.contacts.Length > 0
            && collision.GetContact(0).point.y > this.transform.position.y)
        {

            if (collision.collider.name.ToLower().Contains("mario"))
            {
                //Golpe desde abajo
                this.GetComponent<Animator>().SetInteger("Estado", 2);
                EventosSonido.aplastar();
                EventosMarcadores.sumarPuntuacion(this.transform.position, 1000);
            }
        }
        controladorEstado.colisiona(collision);
    }

Archivo: Assets/Scripts/Juego/Estados/ControladorEstado.cs

Dentro del controlador lo limitamos a diez veces por segundo para su ejecucion:

public class ControladorEstado
{
    Estado estado;
    Estado andar;
    public GameObject my;
    public Collision2D collision;
    float tiempoMaximo = 100.0f;
    float tiempoActual = 0.000f;

    public ControladorEstado()
    {
        andar = new Andar();
        andar.definirControlador(this);
        estado = andar;
    }

    public void procesarEstado(){
        tiempoActual += Time.deltaTime/0.001f;
        if(tiempoActual >= tiempoMaximo)
        {
            estado.procesarEstado();
            tiempoActual = 0.000f;
        }

    }
}

Archivo: Assets/Scripts/Juego/Estados/Andar.cs

En el estado andar tenemos la deteccion de la colision si existiera. De esta forma sabemos si tenemos que cambiar de direccion:

    /// <summary>
    /// Procesa el estado andar
    /// </summary>
    public void procesarEstado()
    {
        int distanciaActual = ((int)controlador.my.transform.position.x) - distanciaInicial;

        //Cambiamos de direccion si se choca con una pared o otro enemigo
        if(controlador != null
            && controlador.collision != null
            && controlador.collision.otherCollider != null
            && (controlador.collision.otherCollider.name.ToLower().IndexOf("dino") > -1
                || controlador.collision.otherCollider.name.ToLower().IndexOf("pared") > -1
                || controlador.collision.otherCollider.name.ToLower().IndexOf("mario") > -1))
        {
 
            cambiarDireccion();
            //Limpiamos la colision para procesar la siguiente
            controlador.collision = null;
        }
        else
        {
            //Cambiamos direccion si el recorrido maxima a sido superado o igualado
            if (distanciaMaxima <= positivo(distanciaActual))
            {
                cambiarDireccion();
            }
        }

        //Procesamos el movimiento
        Rigidbody2D rigidBody = controlador.my.GetComponent<Rigidbody2D>();
        Animator animator = controlador.my.GetComponent<Animator>();

        if (irDerecha)
        {
            rigidBody.velocity = new Vector2(3, rigidBody.velocity.y);
            
        }
        else
        {
            rigidBody.velocity = new Vector2(-3, rigidBody.velocity.y);
        }

        //cambiamos estado solo si esta quieto
        if(animator.GetInteger("Estado") == 0)
        {
            animator.SetInteger("Estado", 1);
        }
    }

Bloques

Archivo: Assets/Scripts/Juego/Golpe.cs

En los bloques o cajas detectamos el golpe y sus animaciones:

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.collider.name == "LimiteDerecho"
        || collision.collider.name == "Muerte"
        || collision.otherCollider.name == "Muerte")
        {
            eliminar();
        }

        if (collision.contacts.Length > 0
            && collision.GetContact(0).point.y > this.transform.position.y)
        {

            if (collision.collider.name.ToLower().Contains("mario"))
            {
                //Golpe desde abajo
                this.GetComponent<Animator>().SetInteger("Estado", 2);
                EventosSonido.aplastar();
                EventosMarcadores.sumarPuntuacion(this.transform.position, 1000);
            }
        }
        controladorEstado.colisiona(collision);
    }

Creditos

Creditos de las diferentes obras que se han utilizado:

Sonidos

  • Menu Music Autor: Mrpoly Enlace: OpenGameArts

  • Happy Arcade Tune Autor: Rezoner Enlace: OpenGameArts

  • 512 Sound Effects (8-bit style) Autor: SubspaceAudio Enlace: OpenGameArts

Tiles, Imagenes

  • Escenario y Enemigos Autor: GameArt2DZuhria Alfitra Enlace: https://www.gameart2d.com/

  • Animated Coins Autor: Clint Bellanger Enlace: OpenGameArts

  • Dark Halloween Castle Autor: 3dproartist786 Enlace: OpenGameArts

#s3gt_translate_tooltip_mini { display: none !important; }

Debate1en Entrega PEC2 – Programación de videojuegos 2D aula 1

Deja un comentario