miércoles, 30 de julio de 2014

#Script Barra de vida #unity3d - Health Bar

En todo videojuego existen diferentes formas o maneras de mostrar las vidas del player o enemigos. Por lo tanto he considerado importante realizar un script basado en JS (javascript) el cual muestre por medio de una barra, las vidas del personaje principal.


Como se indica en la parte superior este script está realiazado en JS.

En primera instancia lo que debemos de tener en consideración es la relación existente entre los siguientes scripts. Primero debemos de crear el entorno en el cual nuestro player se va a ver afectado por el enemigo.

En mi caso he elaborado un plano, un cubo (enemigo), y el player (Primera Persona). Se pueden optimizar los scripts a realizar pero en mi caso he elaborado 3 Scripts para que se aprecie la mecánica.

El primer Script "muestra_vidas" se lo colocaremos a la cámara o a un gameobject vació, puesto que este script permitirá que se muestren las texturas de la vida del player. Este script posee la variable progress en la cual almacenaré la vida total del player. Las variables de tipo Texture2D sirven para adicionar las texturas de la barra de vida y la del fondo de la barra. Las texturas a usar pueden estar en formato PNG.

Adicional se han aumentado variables de tipo float que permiten almacenar el tamaño de la pantalla a visualizar y realizar una operación matemática para devidir la pantalla en cinco partes iguales. Pero esto se explicará en otra publicación.

*JS
------------------------------------------------
var progress : float= 0;
var progressBarEmpty : Texture2D;
var progressBarFull:Texture2D;

static var ScreenX:float;
static var ScreenY:float;
static var AnchoxPantalla: int;
static var AltoxPantalla:int;

function OnGUI () {
//barra vacia (posicionx,posiciony, ancho, alto)
GUI.DrawTexture (Rect(ScreenX*0.1,ScreenY*0.1,400,140), progressBarEmpty);
//barra llena
GUI.DrawTexture (Rect(ScreenX*0.55,ScreenY*0.65,progress,20), progressBarFull);
}

function Update (){
AnchoxPantalla=Screen.width;
AltoxPantalla=Screen.height;
ScreenX = (Screen.width)/5;
ScreenY=(Screen.height)/5;
progress = PlayerStat.vida; //llamo a la variable vida que se encuentra en el script PlayerStat
}
------------------------------------------------


La función OnGUI dibujará en la pantalla la barra de vida y el fondo. Como se ha comentado el GUIDrawTexture mostrará la textura almacenada en progressBarEmpty, y en otra línea se mostrará progressBarFull, la barra que se irá debilitando apenas el player reciba daño.


La variable progress almacenará el valor total de las vidas, osea que al principio esta tiene 250 vidas, pero por cada daño esta va a bajar 50pts. Si revisamos el GUIDraw de la progressBarFull, nos damos cuenta que en el ancho no tenemos un valor fijo, sino que el ancho va a estar guiado por los puntos de la vida.

Hasta este momento solo hemos mostrado las barras de vida pero no poseen interacción con el player. Para lo cual crearemos el segundo script llamado "PlayerStat" en este script creamos la variable vida que almacenará el total de la vida del player. Aquí realizamos el cálculo del daño, donde la funcion Update valida al momento que vida sea 0 se active la funcion dead (cambio de nivel), y para resetear la variable vida luego le asignamos los 250pts. Este Script se lo ponen al player.

*JS
------------------------------------------------
static var vida = 250;

function Update (){
if(vida <= 0){
Dead();
}
}

function Dead(){
Application.LoadLevel("enemigo 2");
vida = 250;
}
------------------------------------------------

El último script a realizar lo llamaremos "baja_vidas" Este script lo tiene el enemigo, y en este le indicamos cuanto es el daño que va a ocasionar. En mi caso he simplificado el ejemplo usando el OnTriggerEnter a un cubo.
En este script lo único que se realiza es cuando entra a la zona sensible la variable vida es restada por 50pts.

*JS
------------------------------------------------
function OnTriggerEnter () {
PlayerStat.vida = PlayerStat.vida -50;
}
------------------------------------------------


Y con esto hemos elaborado nuestra barra de vida. Debemos de tener claro que cuando llegue "vidas"a cero se cambiará de escena. La misma mecánica se puede usar para restar vidas al enemigo u otros elementos.

Si tienes alguna duda o consulta no dudes en comentar.

19 comentarios:

  1. Los scripts están muy bien y funcionan de maravilla...excepto por los valores de la función ONGUI. He tenido que ir los acomodando para que queden uno encima de otro...(los DrawTexture)pues con los de la script aparece un recuadro en la esquina izquierda demasiado grande y la BarFull aparece muy por delante y más pequeña. Resetee ambos valores de las DrawTexture al mismo y así he logrado q estén uno encima de otro...

    ResponderEliminar
    Respuestas
    1. hola rolando saludos!! y como hiciste para alinear los Drawtexture porque yo tengo el mismo problemilla ya hize varios cambios pero aun no me sale--Gracias si me respondes y si no tambien Gracias..

      Eliminar
    2. Hola,tengo el mismo problema,puedes explicar como lo solucionaste?

      Eliminar
  2. Listo muchas gracias por la observación. Rolando

    ResponderEliminar
  3. no me ha servido pero esta muy chulo (lo he probado) gracias chabal

    ResponderEliminar
  4. donde puedo conseguir scripts para hacer diferentes cosas?

    ResponderEliminar
    Respuestas
    1. Aquí en mi blog tengo algunos script pero de ahí es de buscar e ir probando, no conozco de un solo sitio donde se encuentren muchos scripts.

      Eliminar
  5. hola alonso! gracias por los tutoriales y scripts.. se aprende y se agradece- solo una preguntita? y como le hago para que la barra se llene otra vez? porque tengo un script que me sube la vida otra vez, pero logicamente pues nada que ver con la barra de vida, o sea la barra de vida se acaba y yo sigo vivo haha! me cambia de nivel solamente que no toque los botiquines de salud..bueno saludos y gracias por compartir tus conocimientos saludos!!

    ResponderEliminar
    Respuestas
    1. Estimado Gerardo, bueno en este sería de forma inversa, cuando toques los botiquines de salud. si usas triggerEnter en ves de restar, sumas. Y esto enviará el valor a la misma variable que usa el GUI para mostrar la barra.

      function OnTriggerEnter () {
      PlayerStat.vida = PlayerStat.vida +50;
      }

      Me comentas si te sirvió la ayuda... Disculpa por contestar ahora, es solo que me encontraba fuera del país y donde andaba no tenía internet...

      Eliminar
  6. Hola Alonso! Gracias por contestar, si en realidad asi lo hize. lo que pasaba es que el script de vida que yo use para subir de vida,era de otro tutorial.y tenia varias funciones.entonces pues logicamente si me subia la vida, pero no en la barra de (mostrar vidas) ya que en el script pues no existia ningun (PlayerStat) function: y lo que hize pues fue eso Agregar al script ::
    function OnTriggerEnter () {
    PlayerStat.vida = PlayerStat.vida +50;
    }
    solo le cambie el - por el + y quedo perfecto:
    y es que en ese script si me mostraba las vidas y la cantidad de balas en pantalla, pero con numeros,pero no tenia barra de vida, y bueno yo queria que tubiera una Barra de vida, y ya la tiene!
    Gracias de nuevo por tu material y por tu tiempo Alonso! Saludos!! cuidate,,

    ResponderEliminar
  7. hola Alonso he colocado los scripts y ya fije la barra de vida excelente pero cuando el enemigo se acerca en mi caso es un zombie, no me resta la vida.

    el enemigo tiene esta script con la que me sigue y me ataca

    le coloque la script que colocaste pero no me resta vida no se que hacer.
    esta script del enemigo trae una opcion con la que ya me resta la vida pero dicha script no posee una barra de vida y tampoco encuentro la forma de colocarle una... podrias ayudarme?

    ResponderEliminar
  8. public var thePlayer : Transform;
    private var theEnemy : Transform;

    public var speed : float = 2.0;

    var isOffScreen : boolean = false;
    public var offscreenDotRange : float = 0.7;

    var isVisible : boolean = false;
    public var visibleDotRange : float = 0.8; // ** between 0.75 and 0.85 (originally 0.8172719)

    var isInRange : boolean = false;

    public var followDistance : float = 24.0;
    public var maxVisibleDistance : float = 25.0;

    public var reduceDistAmt : float = 3.1;

    private var sqrDist : float = 0.0;

    public var health : float = 300.0;
    public var damage : float = 15.0;

    public var enemySightedSFX : AudioClip;

    private var hasPlayedSeenSound : boolean = false;

    private var colDist : float = 5.0; // raycast distance in front of enemy when checking for obstacles


    function Start()
    {
    if ( thePlayer == null )
    {
    thePlayer = GameObject.Find( "Player" ).transform;
    }

    theEnemy = transform;
    }

    function Update()
    {
    // Movement : check if out-of-view, then move
    CheckIfOffScreen();

    // if is Off Screen, move
    if ( isOffScreen )
    {
    MoveEnemy();

    // restore health
    RestoreHealth();
    }
    else
    {
    // check if Player is seen
    CheckIfVisible();

    if ( isVisible )
    {
    // deduct health
    DeductHealth();

    // stop moving
    StopEnemy();

    // play sound only when the Man is first sighted
    if ( !hasPlayedSeenSound )
    {
    GetComponent.().PlayClipAtPoint( enemySightedSFX, thePlayer.position );
    }
    hasPlayedSeenSound = true; // sound has now played
    }
    else
    {
    // check max range
    CheckMaxVisibleRange();

    // if far away then move, else stop
    if ( !isInRange )
    {
    MoveEnemy();
    }
    else
    {
    StopEnemy();
    }

    // reset hasPlayedSeenSound for next time isVisible first occurs
    hasPlayedSeenSound = false;
    }
    }

    }


    function DeductHealth()
    {
    // deduct health
    health -= damage * Time.deltaTime;

    // check if no health left
    if ( health <= 0.0 )
    {
    health = 0.0;
    Debug.Log( "YOU ARE OUT OF HEALTH !" );

    // Restart game here!
    Application.LoadLevel( "perdedor" );
    }
    }


    function RestoreHealth()
    {
    // deduct health
    health += damage * Time.deltaTime;

    // check if no health left
    if ( health >= 300.0 )
    {
    health = 300.0;
    //Debug.Log( "HEALTH is FULL" );
    }
    }


    function CheckIfOffScreen()
    {
    var fwd : Vector3 = thePlayer.forward.normalized;
    var other : Vector3 = (theEnemy.position - thePlayer.position).normalized;

    var theProduct : float = Vector3.Dot( fwd, other );

    ResponderEliminar
    Respuestas

    1. if ( theProduct < offscreenDotRange )
      {
      isOffScreen = true;
      }
      else
      {
      isOffScreen = false;
      }
      }


      function MoveEnemy()
      {
      // Check the Follow Distance
      CheckDistance();

      // if not too close, move
      if ( !isInRange )
      {
      GetComponent.().velocity = Vector3( 0, GetComponent.().velocity.y, 0 ); // maintain gravity

      // --
      // Old Movement
      //transform.LookAt( thePlayer );
      //transform.position += transform.forward * speed * Time.deltaTime;
      // --

      // New Movement - with obstacle avoidance
      var dir : Vector3 = ( thePlayer.position - theEnemy.position ).normalized;
      var hit : RaycastHit;

      if ( Physics.Raycast( theEnemy.position, theEnemy.forward, hit, colDist ) )
      {
      //Debug.Log( " obstacle ray hit " + hit.collider.gameObject.name );
      if ( hit.collider.gameObject.name != "Player" && hit.collider.gameObject.name != "Terrain" )
      {
      dir += hit.normal * 20;
      }
      }

      var rot : Quaternion = Quaternion.LookRotation( dir );

      theEnemy.rotation = Quaternion.Slerp( theEnemy.rotation, rot, Time.deltaTime );
      theEnemy.position += theEnemy.forward * speed * Time.deltaTime;
      //theEnemy.rigidbody.velocity = theEnemy.forward * speed; // Not Working

      // --
      }
      else
      {
      StopEnemy();
      }
      }


      function StopEnemy()
      {
      transform.LookAt( thePlayer );

      GetComponent.().velocity = Vector3.zero;
      }


      function CheckIfVisible()
      {
      var fwd : Vector3 = thePlayer.forward.normalized;
      var other : Vector3 = ( theEnemy.position - thePlayer.position ).normalized;

      var theProduct : float = Vector3.Dot( fwd, other );

      if ( theProduct > visibleDotRange )
      {
      // Check the Max Distance
      CheckMaxVisibleRange();

      if ( isInRange )
      {
      // Linecast to check for occlusion
      var hit : RaycastHit;

      if ( Physics.Linecast( theEnemy.position + (Vector3.up * 1.75) + theEnemy.forward, thePlayer.position, hit ) )
      {
      Debug.Log( "Enemy sees " + hit.collider.gameObject.name );

      if ( hit.collider.gameObject.name == "Player" )
      {
      isVisible = true;
      }
      }
      }
      else
      {
      isVisible = false;
      }
      }
      else
      {
      isVisible = false;
      }
      }


      function CheckDistance()
      {
      var sqrDist : float = (theEnemy.position - thePlayer.position).sqrMagnitude;
      var sqrFollowDist : float = followDistance * followDistance;

      if ( sqrDist < sqrFollowDist )
      {
      isInRange = true;
      }
      else
      {
      isInRange = false;
      }
      }


      function ReduceDistance()
      {
      followDistance -= reduceDistAmt;
      }


      function CheckMaxVisibleRange()
      {
      var sqrDist : float = (theEnemy.position - thePlayer.position).sqrMagnitude;
      var sqrMaxDist : float = maxVisibleDistance * maxVisibleDistance;

      if ( sqrDist < sqrMaxDist )
      {
      isInRange = true;
      }
      else
      {
      isInRange = false;
      }
      }

      Eliminar
    2. Perdona lo largo esas dos partes son el script que te nombre arriba con el que el enemigo me persigue y ataca...

      Eliminar
    3. Luego de confundirme un buen tiempo con tus script!!! no es la mejor forma de leer el codigo.. me puedo dar cuenta que quizas entre esos dos script no estás llamando a las mismas variables. favor indica cual script pertenece a que gameobject enemigo o player.

      Eliminar
  9. amigos este lenguaje esta padre y lo puedo utilizar en html y en video juego esta padre

    ResponderEliminar
  10. Y si solo quiero que me reste vida de un texto canvas que quiero agregar al jugador "player", como puedo hacer?

    ResponderEliminar