Mortal Balance


Un plataformas que pone a prueba tu habilidad aprovechandose del acelerómetro y la pantalla táctil.

Salta, esquiva y mantén el equilibrio mientras recoges objetos y consigues desbloquear los 40 niveles que componen el universo Mortal Balance!


Información del proyecto

Plataforma

Motor

Lenguaje

C# y Unity Javascript

Compañía

Pizza&Play Videogames

Finalizado

01/07/2012

Descarga

Mortal Balance

Para el correcto funcionamiento de esta Apk, será necesario activar en Android el permiso para instalación de aplicaciones desde orígenes desconocidos puesto que no procede de Google Play.

Equipo

Diseño:
José Miguel Barros Reche
Christian Rodríguez Elvira
Pitu Álvarez
Cristian Soriano Crespo
Ilustración:
Pitu Álvarez
Modelado 3D:
José Miguel Barros Reche
Christian Rodríguez Elvira
Escultura 3D:
Christian Rodríguez Elvira
Animación 3D:
José Miguel Barros Reche
Programación:
Cristian Soriano Crespo
Efectos de sonido:
José Miguel Barros Reche
Música ambiente:
José Miguel Barros Reche
Nota: Debido a que en un futuro alguno de mis proyectos podría no estar disponible en los lugares de publicación, añado una versión descargable para que sea posible probarlo. Así mismo también añado un apartado de capturas de pantalla y gameplays por si en un futuro no pudiesen ejecutarse en los sistemas en rigor.
Ejemplo de código

//Script para controlar el movimiento del Player.

//Necesita que el objeto tenga el script ControlAcelerometro.js 
//para bloquearlo en el salto y cambiarle la cuerda sobre la que rotar.
@script RequireComponent(ControlAcelerometro);

//Variables con las rutas de salto y movimiento.
public var cuboAvanzarDerecha : Transform;
public var cuboAvanzarCentro : Transform;
public var cuboAvanzarIzquierda : Transform;
public var cuboCentroIzquierda : Transform;
public var cuboCentroDerecha : Transform;
public var cuboCaidaIzquierda : Transform;
public var cuboCaidaCentroIzquierda : Transform;
public var cuboCaidaDerecha : Transform;
public var cuboCaidaCentroDerecha : Transform;


//Clips de las Animaciones.
public var animacionIdle : AnimationClip;
public var animacionAvanzar : AnimationClip;
public var animacionAvanzarDerecha : AnimationClip;
public var animacionAvanzarIzquierda : AnimationClip;
public var animacionEmpezarSalto : AnimationClip;
public var animacionAcabarSalto : AnimationClip;
public var animacionCaer : AnimationClip;
public var animacionCelebrar : AnimationClip;

//Variable con el Animation del Player.
private var animacion : Animation;


//Velocidades de las Animaciones.
public var velocidadAnimacionAvanzar : float = 1f;
public var velocidadAnimacionesPerderEquilibrio : float = 2f;
public var velocidadAnimacionEmpezarSalto : float = 2f;


//Boleana para saber si estamos acabando el salto.
private var acabandoSalto : boolean = false;

//Variable con la precision del movimiento que tendrá.
private var precisionMovimiento : float = 1f;

//Enums y Clases Globales comentadas, estarán en el Script correspondiente.

//Creamos un enum con las posibles cuerdas en la escena.
//enum Cuerdas
//{
//  Ninguna = 0,
//  Central = 1,
//  Derecha = 2,
//  Izquierda = 3
//}

//Creamos una variable del enum Cuerdas para marcar en que cuerda estamos en cada momento.
private var cuerdaActual : Cuerdas = Cuerdas.Central;

private var cuerdaObstaculoVirtual : Cuerdas = Cuerdas.Ninguna;

//Creamos un enum con las posibles caidas con las que se encontrará el Player.
//enum TiposCaida
//{
//  Ninguna = 0,
//  Fisica = 1,
//  Virtual = 2
//}

private var tipoCaida : TiposCaida = TiposCaida.Ninguna;

//Creamos un enum con los posibles estados que tendrá el player.
enum EstadosPlayer{
  Idle = 0,
  Avanzando = 1,
  EmpezandoSaltoDerecha = 2,
  SaltandoDerecha = 3,
  AcabandoSaltoDerecha = 4,
  EmpezandoSaltoIzquierda = 5,
  SaltandoIzquierda = 6,
  AcabandoSaltoIzquierda = 7,
  Cayendo = 8,
  Celebrando = 9
}

//Creamos la variable con el estado del Player en cada momento.
private var estadoPlayer : EstadosPlayer = EstadosPlayer.Idle;

//Velocidad de movimiento del Player.
public var velocidadPlayer : float = 1f;

//Incremento que se aplicará a la velocidad cuando estemos en el aire.
public var incrementoVelocidadAire : float = 2f;

//Variable con los fx del player.
public var fxCaida : AudioClip;
public var fxCelebracion : AudioClip;
public var fxSalto : AudioClip;

//Variable con el script del fx manager.
public var scriptFxManager : FxManager;

//Variable para saber si ya hemos lanzado un fx.
private var fxLanzado : boolean = false;

//Variable para saber si hemos lanzado el fx de salto.
private var fxSaltoLanzado : boolean = false;

//Velocidad final de movimiento.
private var velocidadFinal : float = 0f;

//Punto de destino actual.
private var puntoDestino : Vector3 = Vector3.zero;

//Vector con el calculo del Avance en cada momento.
private var vectorAvanzar : Vector3 = Vector3.zero;

//Variable con la espera calculada antes de moverse tras lanzar la animación de saltar.
private var tiempoEspera : float = 0f;

//Variable con el control del acelerometro.
private var controlAcel : ControlAcelerometro;


function Awake (){
  //Al iniciar guardamos el componente del acelerometro en una variable.
  controlAcel = this.GetComponent("ControlAcelerometro");
  
  //Inicializamos la cuerda, sobre la que rotar, en la cuerda central.
  controlAcel.setCuerda(cuboAvanzarCentro);
  
  //Variable con el Animation del Player.
  animacion = this.animation;
}


function Update (){
  //Actualizamos el puntoDestino cuando haga falta.
  ActualizarDestino();
  
  //Actualizamos la velocidad a la que nos desplazamos en cada caso.
  ActualizarVelocidades();
  
  //Lanzamos la animación correspondiente en cada caso.
  LanzarAnimaciones();
  
  //Aplicamos el movimiento al Player.
  this.transform.position += vectorAvanzar * velocidadFinal * Time.deltaTime;
}


//Función para actualizar el punto de destino del Player.
function ActualizarDestino(){
  //Valoramos en que estado esta el Player para saber que actualización aplicar.
  switch(estadoPlayer){
    //Si tenemos que avanzar por una cuerda
    case EstadosPlayer.Avanzando:
      //Si no tenemos punto al que ir
       if(puntoDestino == Vector3.zero){
         //Cambiamos el punto al que ir según la cuerda en la que estemos.
        switch(cuerdaActual){
          case Cuerdas.Central:
            puntoDestino = cuboAvanzarCentro.position;
          break;
          case Cuerdas.Derecha:
            puntoDestino = cuboAvanzarDerecha.position;
          break;
          case Cuerdas.Izquierda:
            puntoDestino = cuboAvanzarIzquierda.position;
          break;
        }
      }
      //Si ya tenemos un punto al que ir y hemos llegado, vaciamos el punto de destino para el siguiente Update.
      else if(Vector3.Distance(puntoDestino, this.transform.position) <= precisionMovimiento){
        puntoDestino = Vector3.zero;
      }
    break;
    
    case EstadosPlayer.EmpezandoSaltoDerecha:
      //Si no tenemos punto al que ir
       if(puntoDestino == Vector3.zero){
         //Cambiamos el punto al que ir según la cuerda en la que estemos.
        switch(cuerdaActual){
          case Cuerdas.Central:
            puntoDestino = cuboAvanzarCentro.position;
          break;
          case Cuerdas.Derecha:
            puntoDestino = cuboAvanzarDerecha.position;
          break;
          case Cuerdas.Izquierda:
            puntoDestino = cuboAvanzarIzquierda.position;
          break;
        }
      }
      //Si ya tenemos un punto al que ir y hemos llegado, vaciamos el punto de destino para el siguiente Update.
      else if(Vector3.Distance(puntoDestino, this.transform.position) <= precisionMovimiento){
        puntoDestino = Vector3.zero;
      }
      
      //Si no tenemos un tiempo de espera, lo inicializamos a lo que toque.
      if(tiempoEspera == 0){
        animacion.Rewind(animacionEmpezarSalto.name);
        tiempoEspera = Time.time + animacion[animacionEmpezarSalto.name].length/velocidadAnimacionEmpezarSalto;
      }
      //Si tenemos tiempo de espera y ha pasado, cambiamos el estado a saltando y reinicializamos el tiempo.
      else if(tiempoEspera < Time.time){
        this.SetEstado(EstadosPlayer.SaltandoDerecha);
        tiempoEspera = 0f;
      }
    break;
    
    case EstadosPlayer.SaltandoDerecha:
      //Si no tenemos punto al que ir
      if(puntoDestino == Vector3.zero){
        //Cambiamos el punto al que ir según la cuerda en la que estemos.
        switch(cuerdaActual){
          case Cuerdas.Central:
            puntoDestino = cuboCentroDerecha.position;
          break;
          case Cuerdas.Izquierda:
            puntoDestino = cuboCentroIzquierda.position;
          break;
        }
      }
      //Si ya tenemos un punto al que ir y hemos llegado, vaciamos el punto de destino para el siguiente Update.
      else if(Vector3.Distance(puntoDestino, this.transform.position) <= precisionMovimiento){
        puntoDestino = Vector3.zero;
        estadoPlayer = EstadosPlayer.AcabandoSaltoDerecha;
      }
    break;
  
    case EstadosPlayer.AcabandoSaltoDerecha:
      //Si no tenemos punto al que ir
      if(puntoDestino == Vector3.zero){
        //Cambiamos el punto al que ir según la cuerda en la que estemos.
        switch(cuerdaActual){
          case Cuerdas.Central:
            puntoDestino = cuboAvanzarDerecha.position;
          break;
          case Cuerdas.Izquierda:
            puntoDestino = cuboAvanzarCentro.position;
          break;
        }
      }
      //Si ya tenemos un punto al que ir y hemos llegado.
      else if(Vector3.Distance(puntoDestino, this.transform.position) <= precisionMovimiento){
        //Cambiamos la cuerda en la que estamos.
        switch(cuerdaActual){
          case Cuerdas.Central:
            cuerdaActual = Cuerdas.Derecha;
            controlAcel.setCuerda(cuboAvanzarDerecha);
          break;
          case Cuerdas.Izquierda:
            cuerdaActual = Cuerdas.Central;
            controlAcel.setCuerda(cuboAvanzarCentro);
          break;
        }
        //Cambiamos el estado a Avanzando y le ponemos el punto a 0 para
        //que en la próxima vuelta reconozca a que punto ir.
        puntoDestino = Vector3.zero;
        estadoPlayer = EstadosPlayer.Avanzando;
        acabandoSalto = true;
      }
    break;
    
    case EstadosPlayer.EmpezandoSaltoIzquierda:
      //Si no tenemos punto al que ir
       if(puntoDestino == Vector3.zero){
         //Cambiamos el punto al que ir según la cuerda en la que estemos.
        switch(cuerdaActual){
          case Cuerdas.Central:
            puntoDestino = cuboAvanzarCentro.position;
          break;
          case Cuerdas.Derecha:
            puntoDestino = cuboAvanzarDerecha.position;
          break;
          case Cuerdas.Izquierda:
            puntoDestino = cuboAvanzarIzquierda.position;
          break;
        }
      }
      //Si ya tenemos un punto al que ir y hemos llegado, vaciamos el punto de destino para el siguiente Update.
      else if(Vector3.Distance(puntoDestino, this.transform.position) <= precisionMovimiento){
        puntoDestino = Vector3.zero;
      }
      
      //Si no tenemos un tiempo de espera, lo inicializamos a lo que toque.
      if(tiempoEspera == 0){
        animacion.Rewind(animacionEmpezarSalto.name);
        tiempoEspera = Time.time + animacion[animacionEmpezarSalto.name].length/velocidadAnimacionEmpezarSalto;
      }
      //Si tenemos tiempo de espera y ha pasado, cambiamos el estado a saltando y reinicializamos el tiempo.
      else if(tiempoEspera < Time.time){
        this.SetEstado(EstadosPlayer.SaltandoIzquierda);
        tiempoEspera = 0f;
      }
    break;
    
    case EstadosPlayer.SaltandoIzquierda :
      //Si no tenemos punto al que ir
      if(puntoDestino == Vector3.zero){
        //Cambiamos el punto al que ir según la cuerda en la que estemos.
        switch(cuerdaActual){
          case Cuerdas.Central:
            puntoDestino = cuboCentroIzquierda.position;
          break;
          case Cuerdas.Derecha:
            puntoDestino = cuboCentroDerecha.position;
          break;
        }
      }
      //Si ya tenemos un punto al que ir y hemos llegado, vaciamos el punto de destino para el siguiente Update.
      else if(Vector3.Distance(puntoDestino, this.transform.position) <= precisionMovimiento){
        puntoDestino = Vector3.zero;
        estadoPlayer = EstadosPlayer.AcabandoSaltoIzquierda;
      }
    break;
    
    case EstadosPlayer.AcabandoSaltoIzquierda:
      //Si no tenemos punto al que ir
      if(puntoDestino == Vector3.zero){
        //Cambiamos el punto al que ir según la cuerda en la que estemos.
        switch(cuerdaActual){
          case Cuerdas.Central:
            puntoDestino = cuboAvanzarIzquierda.position;
          break;
          case Cuerdas.Derecha:
            puntoDestino = cuboAvanzarCentro.position;
          break;
        }
      }
      //Si ya tenemos un punto al que ir y hemos llegado.
      else if(Vector3.Distance(puntoDestino, this.transform.position) <= precisionMovimiento){
        //Cambiamos la cuerda en la que estamos.
        switch(cuerdaActual){
          case Cuerdas.Central:
            cuerdaActual = Cuerdas.Izquierda;
            controlAcel.setCuerda(cuboAvanzarIzquierda);
          break;
          case Cuerdas.Derecha:
            cuerdaActual = Cuerdas.Central;
            controlAcel.setCuerda(cuboAvanzarCentro);
          break;
        }
        //Cambiamos el estado a Avanzando y le ponemos el punto a 0 para
        //que en la próxima vuelta reconozca a que punto ir.
        puntoDestino = Vector3.zero;
        estadoPlayer = EstadosPlayer.Avanzando;
        acabandoSalto = true;
      }
    break;
    
    case EstadosPlayer.Cayendo:
      //Si no tenemos punto al que ir
      if(puntoDestino == Vector3.zero){
        switch(tipoCaida){ 
          case TiposCaida.Fisica:
            //Calculamos cual es la distancia más corta que hay entre el Player y alguno de los Cubos de caída.
            var distanciaMinima : float = Mathf.Min(
                Vector3.Distance(cuboCaidaCentroDerecha.position, this.transform.position),
                Vector3.Distance(cuboCaidaCentroIzquierda.position, this.transform.position)
              );
            //Comprobamos a que cubo corresponde dicha distancia.
            switch(distanciaMinima){
              case Vector3.Distance(cuboCaidaCentroDerecha.position, this.transform.position):
                puntoDestino = cuboCaidaCentroDerecha.position;
              break;
              
              case Vector3.Distance(cuboCaidaCentroIzquierda.position, this.transform.position):
                puntoDestino = cuboCaidaCentroIzquierda.position;
              break;
            }
            
            //Si estamos en una de las cuerdas de los laterales y el Player
            //esta inclinado hacia fuera, caemos por los laterales exteriores.
            if(this.transform.eulerAngles.z > 180 && cuerdaActual == Cuerdas.Derecha){
              puntoDestino = cuboCaidaDerecha.position;
            }
            else if(this.transform.eulerAngles.z < 180 && cuerdaActual == Cuerdas.Izquierda){
              puntoDestino = cuboCaidaIzquierda.position;
            }
          break;
          
          case TiposCaida.Virtual:
            //Si la caida es virtual y estamos en la misma cuerda del obstaculo
            //caemos hacia delante.
            if(cuerdaActual == cuerdaObstaculoVirtual){
              puntoDestino.z = this.transform.position.z + 30;
            }
            else{
              //Si la cuerda es distinta a la que estamos.
              switch(cuerdaActual){
                //En caso de que partamos de la cuerda central.
                case Cuerdas.Central:
                  //Si vamos a la derecha caeremos por el límite derecho.
                  if(cuerdaObstaculoVirtual == Cuerdas.Derecha){
                    puntoDestino = cuboCaidaDerecha.position;
                  }
                  //Si vamos a la izquierda caeremos por el límite izquierdo.
                  else{
                    puntoDestino = cuboCaidaIzquierda.position;
                  }
                break;
                
                //Si partimos de cualquier otra cuerda la de destino será la central.
                default:
                  //Calculamos cual es la distancia más larga que hay entre el Player y alguno de los Cubos de caída para que de el efecto de continuar el salto pasandose de largo.
                  var distanciaMaxima : float = Mathf.Max(
                      Vector3.Distance(cuboCaidaDerecha.position, this.transform.position),
                      Vector3.Distance(cuboCaidaIzquierda.position, this.transform.position)
                    );
                  //Comprobamos a que cubo corresponde dicha distancia.
                  switch(distanciaMaxima){    
                    case Vector3.Distance(cuboCaidaDerecha.position, this.transform.position):
                      puntoDestino = cuboCaidaDerecha.position;
                    break;
                    
                    case Vector3.Distance(cuboCaidaIzquierda.position, this.transform.position):
                      puntoDestino = cuboCaidaIzquierda.position;
                    break;
                  }
                break;
              }
            }
          break;
        }
      }
    break;
  }
}


//Función para actualizar la velocidad que llevará el Player.
function ActualizarVelocidades(){
  //Si tenemos un punto de destino.
  if(puntoDestino != Vector3.zero){  
    //Calculamos el vector con el que avanzar y lo normalizamos.
    vectorAvanzar = puntoDestino - this.transform.position;
    vectorAvanzar.Normalize();
    
    //Hacemos que la velocidad final sea la velocidad del Player.
    velocidadFinal = velocidadPlayer;
    
    //Comprobamos si necesitamos cambiar la velocidad en función del estado en el que este el Player.
    switch(estadoPlayer){
      case EstadosPlayer.SaltandoDerecha: 
      case EstadosPlayer.SaltandoIzquierda:
        //Probando valores para suavizar el salto con 4 y 5 buenos resultados.
        //Al avanzar más rápida la Y de lo normal provocamos que, con las correcciones del normalizado al punto de destino,
        //se cree una parábola en los saltos.
        vectorAvanzar.y *= 4;
        //Velocidad Incrementada en salto
        velocidadFinal += incrementoVelocidadAire;
      break;
      
      case EstadosPlayer.AcabandoSaltoDerecha:
      case EstadosPlayer.AcabandoSaltoIzquierda:
        //Volvemos a velocidad normal de caida, para más naturalidad, 
        //al reducir la distancia en X al punto de destino.
        if(Mathf.Abs(vectorAvanzar.x) > 0.7){
          //Probando valores para suavizar la caida con 1/4 o 1/3 buenos resultados.
          //Al avanzar más lenta la Y de lo normal provocamos que, con las correcciones del normalizado al punto de destino,
          //cree una parábola en las caidas.
          //vectorAvanzar.y *= 0.33;
          vectorAvanzar.y *= 0.25;
        }
        //Velocidad Incrementada en caida
        velocidadFinal += incrementoVelocidadAire;
      break;
      case EstadosPlayer.Cayendo:
        //Velocidad Incrementada en caida
        velocidadFinal += incrementoVelocidadAire;
      break;
    }
  }
  else{
    vectorAvanzar = Vector3.zero;
    velocidadFinal = 0f;
  }
}


//Función para lanzar la animación que toque en cada caso.
function LanzarAnimaciones(){
  //Valoramos en que estado esta el Player para saber que animación lanzar.
  switch(estadoPlayer){
    case EstadosPlayer.Idle:
      animacion.CrossFade(animacionIdle.name);
    break;
    
    case EstadosPlayer.Avanzando:
      fxSaltoLanzado = false;
      if(acabandoSalto){
        acabandoSalto = false;
        animacion[animacionAcabarSalto.name].layer = 1;
        animacion.CrossFade(animacionAcabarSalto.name);
      }
      else{
        var anguloMaximo : float = controlAcel.GetAnguloMaximo();
        if(this.transform.eulerAngles.z > anguloMaximo/2 && this.transform.eulerAngles.z < anguloMaximo){
          animacion[animacionAvanzarIzquierda.name].speed = velocidadAnimacionesPerderEquilibrio;
          animacion.CrossFade(animacionAvanzarIzquierda.name);
        }
        else if(this.transform.eulerAngles.z < 360 - anguloMaximo/2 && this.transform.eulerAngles.z > 360 - anguloMaximo){
          animacion[animacionAvanzarDerecha.name].speed = velocidadAnimacionesPerderEquilibrio;
          animacion.CrossFade(animacionAvanzarDerecha.name);
        }
        else{
          animacion[animacionAvanzar.name].speed = velocidadAnimacionAvanzar;
          animacion.CrossFade(animacionAvanzar.name);
        }
      }
    break;
    
    case EstadosPlayer.Cayendo:
      //Si aun no hemos lanzado el fx, lo lanzamos.
      if(!fxLanzado){
        //Lanzamos el sonido de la caida al caernos.
        scriptFxManager.ActivarFX(fxCaida);
        //Cambiamos la booleana a sonido lanzado.
        fxLanzado = true;
      }
      animacion.CrossFade(animacionCaer.name);
    break;
    
    case EstadosPlayer.EmpezandoSaltoDerecha:
    case EstadosPlayer.SaltandoDerecha:
    case EstadosPlayer.EmpezandoSaltoIzquierda:
    case EstadosPlayer.SaltandoIzquierda:
      animacion[animacionEmpezarSalto.name].speed = velocidadAnimacionEmpezarSalto;
      animacion.CrossFade(animacionEmpezarSalto.name);
      //Si aun no hemos lanzado el fx, lo lanzamos.
      if(!fxSaltoLanzado){
        //Lanzamos el sonido de la celebración al llegar al final.
        scriptFxManager.ActivarFX(fxSalto);
        //Cambiamos la booleana a sonido lanzado.
        fxSaltoLanzado = true;
      }
    break;
    
    case EstadosPlayer.Celebrando:
      //Si aun no hemos lanzado el fx, lo lanzamos.
      if(!fxLanzado){
        //Lanzamos el sonido de la celebración al llegar al final.
        scriptFxManager.ActivarFX(fxCelebracion);
        //Cambiamos la booleana a sonido lanzado.
        fxLanzado = true;
      }
      animacion.CrossFade(animacionCelebrar.name);
    break;
  }
}


//----- Sets ---------

//Cambiamos el estado del Player y reseteamos su punto de destino.
public function SetEstado( nuevoEstado : EstadosPlayer){
  estadoPlayer = nuevoEstado;
  puntoDestino = Vector3.zero;
}

//Marca al player en caída con identificador para objeto físico o virtual.
public function SetCaida( caida : TiposCaida){
  this.SetEstado(EstadosPlayer.Cayendo);
  tipoCaida = caida;
}

//Sobrecarga para que cuando sea virtual marquemos en que cuerda esta el obstaculo. 
public function SetCaida( caida : TiposCaida, cuerda : Cuerdas){
  this.SetEstado(EstadosPlayer.Cayendo);
  tipoCaida = caida;
  
  cuerdaObstaculoVirtual = cuerda;
}

//------ Gets ---------

//Enviamos el estado del Player.
public function GetEstado() : EstadosPlayer
{
  return estadoPlayer;
}

//Enviamos la Cuerda Actual en la que estamos.
public function GetCuerda() : Cuerdas
{
  return cuerdaActual;
}
Capturas de pantalla
Mortal Balance Póster
Mortal Balance Menú
Mortal Balance Instruciones 1
Mortal Balance Instruciones 2
Mortal Balance Instruciones 3
Mortal Balance Instruciones 4
Mortal Balance Selección de mundo
Mortal Balance Selección de nivel jungla
Mortal Balance Inicio Jungla
Mortal Balance Partida Jungla
Mortal Balance Puntuación
Mortal Balance Selección de nivel cementerio
Mortal Balance Inicio Cementerio
Mortal Balance Partida Cementerio
Mortal Balance Fin Cementerio
Mortal Balance Selección de nivel volcán
Mortal Balance Cuenta atrás inicial
Mortal Balance Aviso equilibrio al límite
Mortal Balance Inicio parque de atracciones
Mortal Balance Inicio de salto
Mortal Balance Fin Parque de atracciones
Mortal Balance Game Over