
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:
-
Iniciar el ejecutable del juego.
-
Cuando se inicie el juego estara en la escena Menu donde tiene que pulsar encima del texto para empezar a jugar.
-
Debera pasarse el nivel y llegar al castillo del final para salir victorioso.
-
Si muere puede presionar para volver a empezar el nivel.
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.
- Juego: Los scripts del nivel. (jugador, enemigos, bloques, etc…)
- 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.
- Nivel1: tiles del primer juego.
- 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: GameArt2D – Zuhria 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; }
Debatecontributions 1en Entrega PEC2 – Programación de videojuegos 2D aula 1