Aprende a desarrollar una IA para tus NPCs parte 5

inteligencia artificial Unity (IA)

Hola a todos, bienvenidos a una nueva edición de como desarrollar una IA para tu NPC en Unity. Como había prometido hoy vamos a ver como rotar y mover nuestro personaje para que siga a nuestro player o jugador.Bien lo primero que debemos hacer es borrar las paredes que teníamos puestas de la edición anterior, los sensores dentro del NPC los desactivamos. (Para desactivar un objeto se debe destildar el check que se encuentra al lado de su icono y nombre). Una vez hecho esto debemos crear un cubo y nombrarlo Player, este mismo lo colocamos en alguna parte del escenario.Ya teniendo listo el escenario, vamos a la pestaña de proyectos y creamos un nuevo C# script dentro de la carpeta scripts y colocamos el siguiente código.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotateToTargetAndMove : MonoBehaviour {
    public GameObject target;
    public float speedCharacter = 8.0f;

	void Update () {
        Vector3 pointA = gameObject.transform.position;
        Vector3 pointB = target.transform.position;
        Vector3 direction = pointB - pointA;

        float distance = Vector3.Distance(pointA, pointB);
        Debug.Log("Distancia:" + distance);

        Ray ray = new Ray(pointA, pointB);
        Debug.DrawRay(ray.origin, direction * 1.0f, Color.red);

        if (Input.GetKey(KeyCode.Mouse0))
        {
            transform.LookAt(pointB);
            gameObject.transform.position += gameObject.transform.forward * 
            speedCharacter * Time.deltaTime; 
        }
	}
}

Como podrán ver es un código similar a las primeras ediciones, ahora vamos a pasar a explicar el código detalladamente.

inteligencia artificial Unity (IA)

 

Explicación del código

public GameObject target;
    public float speedCharacter = 8.0f;

La primer variable es la que ya habíamos creado en ediciones anteriores es para almacenar el objetivo del NPC, en este caso va a ser el Player, y la segunda variable determina con que velocidad se moverá el NPC.

Vector3 pointA = gameObject.transform.position;
        Vector3 pointB = target.transform.position;
        Vector3 direction = pointB - pointA;

Ahora definimos las 3 variables típicas de las ediciones anteriores las cuales usamos para determinar la dirección.

float distance = Vector3.Distance(pointA, pointB);
        Debug.Log("Distancia:" + distance);

Utilizamos nuevamente las variables declaradas anteriormente para determinar la distancia entre el NPC y el Player.

 

Ray ray = new Ray(pointA, pointB);
        Debug.DrawRay(ray.origin, direction * 1.0f, Color.red);

Creamos un rayo a modo depuración para poder verlo en la pestaña de escena.

if (Input.GetKey(KeyCode.Mouse0))
        {
            transform.LookAt(pointB);
            gameObject.transform.position += gameObject.transform.forward * 
            speedCharacter * Time.deltaTime; 
        }

Este es el código nuevo, creamos un condicional que nos dice “si presionamos el click izquierdo del mouse…” nos rotamos en dirección al player (transform.LookAt(pointB)) y luego nos movemos en dirección a él con una velocidad determinada.Puesta en marcha Ahora borramos todos los scripts que estaban añadidos a nuestro NPC y agregamos el script que acabamos de crear. En la variable target debemos arrastrar el Player. Luego de eso estamos listo para probarlo.Bueno eso es todo espero que les haya gustado.
Nos vemos en la próxima edición en la cual les enseñare:

 “Como mejorar el movimiento del NPC”. 

Si te gusto este post comenta y comparte.

curso sobre desarrollo de videojuegos en unity
Pulsa en la imagen para ir al curso.

Aprende a desarrollar una IA para tus NPCs parte 4

inteligencia artificial Unity (IA)

Hola a todos, bienvenidos a una nueva edición de como hacer una inteligencia artificial en Unity. Como había prometido hoy les traigo como desarrollar sensores para la detección de obstáculos en el entorno.Para comenzar vamos a ir a nuestra pestaña de proyectos, carpeta scripts, creamos un nuevo C# script y escribimos el siguiente código.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CoordinatesSensors : MonoBehaviour {
    public GameObject sForward,sBackward, sRight, sLeft;
    private GameObject obstacle;
	
	void Update () {
        Forward();
        Backward();
        Left();
        Right();
	}
    void Forward()
    {
        Vector3 point_A = gameObject.transform.position;
        Vector3 point_B = sForward.transform.position;
        Vector3 direction = point_B - point_A;
        float distance = Vector3.Distance(point_A, point_B);

        Ray ray = new Ray(gameObject.transform.position, sForward.transform.position);
        Debug.DrawRay(ray.origin, direction * 1.0f, Color.red);

        RaycastHit[] hit;
        hit = Physics.RaycastAll(ray);

        if (hit.Length > 0)
        {
            foreach (RaycastHit h in hit)
            {
                obstacle = h.transform.gameObject;
                Vector3 posObstacle = obstacle.transform.position;
                Vector3 directionObstacle = posObstacle - point_A;
                float distanceObstacle = Vector3.Distance(point_A, posObstacle);
                Debug.Log("adelante " + h.transform.gameObject.name + " a distancia: " 
                + distanceObstacle);
                Debug.DrawRay(ray.origin, directionObstacle * 1.0f, Color.magenta);
                break;
            }
        }
    }
    void Backward()
    {
        Vector3 point_A = gameObject.transform.position;
        Vector3 point_B = sBackward.transform.position;
        Vector3 direction = point_B - point_A;
        float distance = Vector3.Distance(point_A, point_B);

        Ray ray = new Ray(gameObject.transform.position, sBackward.transform.position);
        Debug.DrawRay(ray.origin, direction * 1.0f, Color.red);

        RaycastHit[] hit;
        hit = Physics.RaycastAll(ray);

        if (hit.Length > 0)
        {
            foreach (RaycastHit h in hit)
            {
                obstacle = h.transform.gameObject;
                Vector3 posObstacle = obstacle.transform.position;
                Vector3 directionObstacle = posObstacle - point_A;
                float distanceObstacle = Vector3.Distance(point_A, posObstacle);
                Debug.Log("atras " + h.transform.gameObject.name + " a distancia: "
               + distanceObstacle);
                Debug.DrawRay(ray.origin, directionObstacle * 1.0f, Color.magenta);
                break;
            }
        }
    }
    void Right()
    {
        Vector3 point_A = gameObject.transform.position;
        Vector3 point_B = sRight.transform.position;
        Vector3 direction = point_B - point_A;
        float distance = Vector3.Distance(point_A, point_B);

        Ray ray = new Ray(gameObject.transform.position, sRight.transform.position);
        Debug.DrawRay(ray.origin, direction * 1.0f, Color.red);

        RaycastHit[] hit;
        hit = Physics.RaycastAll(ray);

        if (hit.Length > 0)
        {
            foreach (RaycastHit h in hit)
            {
                obstacle = h.transform.gameObject;
                Vector3 posObstacle = obstacle.transform.position;
                Vector3 directionObstacle = posObstacle - point_A;
                float distanceObstacle = Vector3.Distance(point_A, posObstacle);
                Debug.Log("derecha " + h.transform.gameObject.name + " a distancia: " 
                + distanceObstacle);
                Debug.DrawRay(ray.origin, directionObstacle * 1.0f, Color.magenta);
                break;
            }
        }
    }
    void Left()
    {
        Vector3 point_A = gameObject.transform.position;
        Vector3 point_B = sLeft.transform.position;
        Vector3 direction = point_B - point_A;
        float distance = Vector3.Distance(point_A, point_B);

        Ray ray = new Ray(gameObject.transform.position, sLeft.transform.position);
        Debug.DrawRay(ray.origin, direction * 1.0f, Color.red);

        RaycastHit[] hit;
        hit = Physics.RaycastAll(ray);

        if (hit.Length > 0)
        {
            foreach (RaycastHit h in hit)
            {
                obstacle = h.transform.gameObject;
                Vector3 posObstacle = obstacle.transform.position;
                Vector3 directionObstacle = posObstacle - point_A;
                float distanceObstacle = Vector3.Distance(point_A, posObstacle);
                Debug.Log("izquierda " + h.transform.gameObject.name + " a distancia: " 
                + distanceObstacle);
                Debug.DrawRay(ray.origin, directionObstacle * 1.0f, Color.magenta);
                break;
            }
        }
    }
}

Bueno como podrán apreciar para los que están siguiendo esta saga, este código es muy similar al que ya habíamos visto en la edición anterior salvo algunos cambios. Básicamente lo que hicimos fue separarlo de alguna manera y lanzar un rayo en cada dirección.

Explicación del código

Ahora pasamos a explicar los cambios que hemos realizados en el código.

public GameObject sForward,sBackward, sRight, sLeft;

Lo que hemos hecho acá es dividir la variable target en 4 variables llamadas sensor+dirección, por ejemplo sIzquierda o en su traducción al ingles sLeft.

void Update () {
        Forward();
        Backward();
        Left();
        Right();
	}

Ya no tenemos escrito todo nuestro código en el método Update, sino que creamos 4 métodos individuales y los llamamos desde el Update.

void Forward()
    {
        Vector3 point_A = gameObject.transform.position;
        Vector3 point_B = sForward.transform.position;
        Vector3 direction = point_B - point_A;
        float distance = Vector3.Distance(point_A, point_B);

        Ray ray = new Ray(gameObject.transform.position, sForward.transform.position);
        Debug.DrawRay(ray.origin, direction * 1.0f, Color.red);
       //Continua...

Este es uno de los métodos individuales para cada dirección, en este caso es para la dirección hacia adelante. Lo único que modificamos en esto es cambiar la variable target por la variable correspondiente a la dirección del método en donde estamos.

inteligencia artificial Unity (IA)

 

Puesta en marcha

 

Bien, ya tenemos nuestro código listo pero aun no esta funcional, ya que debemos hacer algunos cambios en nuestra escena.Primero antes que nada tenemos que crear paredes a modo de obstáculos que servirán a nuestro NPC para detectar su entorno, pueden tener o no la misma forma que presentamos en la imagen.Segundo debemos crear 4 cubos dentro de nuestro NPC, a estos cubos le debemos sacar la colisión, desactivar el mesh renderer para que no se vean y colocarlo a 4 unidades de distancia desde el NPC. Es decir que debemos tener estos 4 cubos como hijo de nuestro NPC.Para colocarlo en 4 unidades seria de esta manera cuboDerecha (x=4), cuboIzquierda (x=-4), cuboAdelante(z=4), cuboAtrás(z=-4).Tercero para poder visualizar los sensores solo en la vista de escena, debemos cambiarle el icono a cada uno de ellos, esto se hace haciendo click en el cubo celeste que esta junto al nombre del objeto en la pestaña inspector.Y por último debemos borrar nuestro antiguo script de nuestro NPC, agregar este script y asignarle los 4 cubos según correspondan a sus direcciones.
Bueno eso seria todo espero que les haya gustado, nos veremos en la siguiente edición. Si te ha gustado el post no te olvides de comentarlo y compartir.
Siguiente edición Como rotar y mover nuestro NPC hacia el waypoint.
curso sobre desarrollo de videojuegos en unity
Pulsa en la imagen para ir al curso.

Aprende a desarrollar una IA para tus NPCs parte 3

inteligencia artificial Unity (IA)

Hola a todos bienvenido nuevamente a una nueva edición de como desarrollar una inteligencia artificial en Unity. Como lo prometido es deber, hoy vamos a ver como detectar obstáculos entre el NPC y el waypoint. Lo primero que tenemos que hacer es ir a nuestra pestaña de proyectos , ir a la carpeta de scripts y crear un nuevo C# script. Luego de eso escribimos el siguiente código:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RaycastArrayObstacles : MonoBehaviour {
    public GameObject target;
    private GameObject obstacle;
	
	// Update is called once per frame
	void Update () {
        Vector3 point_A = gameObject.transform.position;
        Vector3 point_B = target.transform.position;
        Vector3 direction = point_B - point_A;
        float distance = Vector3.Distance(point_A, point_B);

        Ray ray = new Ray(gameObject.transform.position, target.transform.position);
        Debug.DrawRay(ray.origin, direction * 1.0f, Color.red);

        RaycastHit[] hit;
        hit = Physics.RaycastAll(ray);

        if (hit.Length > 0)
        {
            foreach (RaycastHit h in hit)
            {
                obstacle = h.transform.gameObject;
                Vector3 posObstacle = obstacle.transform.position;
                Vector3 directionObstacle= posObstacle -  point_A;
                float distanceObstacle = Vector3.Distance(point_A, posObstacle);
                Debug.Log("Primer obstaculo " + h.transform.gameObject.name + " a distancia: " 
                + distanceObstacle);
                Debug.DrawRay(ray.origin, directionObstacle * 1.0f, Color.magenta);
                break;
            }
        }
	}
}

Como podrán ver el código no es muy distinto al anterior, si se habrán dado cuenta que es una simple modificación del código de la edición numero 2. Es por lo cual antes de continuar debemos sacar el antiguo script de nuestro NPC y agregarle el que acabamos de crear.

Explicación del código

RaycastHit[] hit;
        hit = Physics.RaycastAll(ray);

Declaramos un array de variables de tipo RaycastHit con el nombre de hit. En dicho array almacenamos todas las colisiones que pueda tener nuestro antiguo rayo de detección llamado ray.

if (hit.Length > 0)
        {
            foreach (RaycastHit h in hit)
            {
                obstacle = h.transform.gameObject;
                Vector3 posObstacle = obstacle.transform.position;
                Vector3 directionObstacle= posObstacle -  point_A;
                float distanceObstacle = Vector3.Distance(point_A, posObstacle);
                Debug.Log("Primer obstaculo " + h.transform.gameObject.name +
                " a distancia: " + distanceObstacle);
                Debug.DrawRay(ray.origin, directionObstacle * 1.0f, Color.magenta);
                break;
            }
        }

inteligencia artificial Unity (IA)

Comprobamos si existen al menos una colisión que nos impida avanzar en la dirección de nuestro rayo. Si eso es cierto, recorremos a través de un foreach (estructura repetitiva similar a for) todas las colisiones almacenadas en nuestro array hit. Como a nosotros no nos interesan todas las colisiones vamos a trabajar únicamente con la primera que nos está obstruyendo el paso.A la variable obstáculo le asignamos el objeto que contiene la primer colisión. Creamos una variable de tipo Vector3 para almacenar la posición del obstáculo, también otra variable del mismo tipo para almacenar la dirección del mismo.Declaramos una variable de tipo flotante que almacene la distancia entre el NPC y el obstáculo, luego de eso imprimimos en consola el nombre del objeto obstáculo y su distancia ,para luego dibujar el rayo correspondiente a esa acción en la pestaña escena. Por ultimo y no por eso menos importante un break , el cual sirve para detener la búsqueda de colisiones una vez encontrada la primer colisión.

Bueno mis queridos seguidores eso es todo por hoy, en la próxima edición veremos:

“Sensores cardinales”.

No se olviden de comentar y compartir si les ha gustado esta edición.

curso sobre desarrollo de videojuegos en unity
Pulsa en la imagen para ir al curso.

Aprende a desarrollar una IA para tus NPCs parte 2

inteligencia artificial Unity (IA)

Hola a todos bienvenidos nuevamente a la segunda edición este emocionante saga sobre inteligencia artificial aplicada a videojuegos. Como había prometido hoy vamos a ver como calcular la distancia y dirección entre el NPC y su respectivo waypoint. Lo primero que debemos hacer es crear un nuevo C# script en la pestaña de proyectos (Nota: si tienes una carpeta para scripts o programación debes crearlo ahí dentro.)Luego escribimos este código:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RaycastPointToPoint : MonoBehaviour {
    public GameObject target;
	
	void Update () {
        Vector3 pointA = gameObject.transform.position;
        Vector3 pointB = target.transform.position;
        Vector3 direction = pointB - pointA;

        float distance = Vector3.Distance(pointA, pointB);
        Debug.Log("Distancia: " + distance);

        Ray ray = new Ray(pointA, pointB);
        Debug.DrawRay(ray.origin, direction * 1.0f, Color.red);
        
	}
}

Como podrán apreciar este código es mas corto que el de la edición pasada. Recuerden, una vez escrito este código debe ser asignado su NPC (nota: si no tienen un NPC, creen una esfera para hacer la prueba).Lo que debe hacer este código es calcular la distancia y dirección entre ambos, y lanzar una linea de depuración para que veamos su comportamiento.

inteligencia artificial Unity (IA)

 

Explicación del código

Antes que nada les recuerdo que el nombre del archivo debe coincidir con el nombre de la clase principal.

 public GameObject target;

Dicho esto, lo primero que hacemos es crear una variable publica de tipo GameObject llamada target, en esta variable se almacenará el waypoint actual.

Vector3 pointA = gameObject.transform.position;
        Vector3 pointB = target.transform.position;
        Vector3 direction = pointB - pointA;

Declaramos 3 variables locales dentro del método Update de tipo Vector3 , este tipo almacena 3 valores flotantes etiquetados como x,y,z. A la primer variable le asignamos la posición de nuestro NPC ,a la segunda variable la posición de nuestro waypoint y por último a la tercer variable le asignamos la dirección. Para calcular la dirección entre 2 vectores basta con restar el primer vector al segundo.

 float distance = Vector3.Distance(pointA, pointB);
        Debug.Log("Distancia: " + distance);

Ahora creamos una variable local de tipo flotante llamada distancia a la cual le asignamos la distancia entre el NPC y el waypoint. Para hacer este calculo nos ayudamos de un método que nos provee Unity el cual pueden apreciar en el código. Luego de esto mostramos en consola el mensaje “Distancia: el valor de la variable”.

Ray ray = new Ray(pointA, pointB);
        Debug.DrawRay(ray.origin, direction * 1.0f, Color.red);

Antes de finalizar este script creamos una variable de tipo rayo llamada ray, en la cual decimos que este rayo sea desde el NPC hasta el waypoint. Después de eso, dibujamos este rayo en la pestaña de escena con un color rojo. Para dibujar este rayo lo primero que debemos hacer es decirle su origen, hacia donde se dirige y en que color va a estar representado.
Eso seria todo por hoy espero que les haya gustado y servido. Por el momento no estamos detectando nuevos waypoint ni obstáculos solo lo estamos calculando para luego utilizarlos en la siguiente edición.No te olvides de comentar y compartir.

En la próxima edición se verá la detección de obstáculos.
curso sobre desarrollo de videojuegos en unity
Pulsa en la imagen para ir al curso.

Aprende a desarrollar una IA para tus NPCs parte 1

inteligencia artificial Unity (IA)

Hola a todos bienvenidos a nuestra primer edición sobre inteligencia artificial para videojuegos. En esta guía mostraremos a modo de ejemplo como se realiza una IA en Unity. Si bien hay varias técnicas que se pueden utilizar en este motor, como el sistema de navigation que trae incluido el engine , pero en este caso vamos a hacer una programación basada en waypoint (puntos de camino).

Creación de WayPoints

Lo primero que debemos hacer es crear un nuevo proyecto en Unity. Crear un nuevo C# script en la pestaña de proyectos. (Recuerden tener su pestaña de proyecto bien organizada con carpetas de scripts, prefabs, scenes, entre otras). Una vez creado vamos a escribir el siguiente código.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NavPointCreator : MonoBehaviour {
    public int id;
    GameObject wp;
	
	
	void Update () {
        if (Input.GetKeyUp(KeyCode.Space))
            CreateTarget();
	}
    void CreateTarget()
    {
        if (wp != null)
            Destroy(wp);
        GameObject waypoint = GameObject.CreatePrimitive(PrimitiveType.Cube);
        waypoint.transform.position = new Vector3(Random.Range(7.0f, -7.0f), 0.03f, 
        Random.Range(7.0f, -7.0f));
        waypoint.name = "Waypoint " + id;
        id++;
        waypoint.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f);
        //waypoint.GetComponent<MeshRenderer>().enabled = false;
        wp = waypoint;
    }
}

Explicación del código C#

Para resumir lo que hace este código es simplemente crear un cubo en un punto aleatorio dentro de un rango, si el cubo esta creado lo destruye y crea otro en otra posición. Esto sirve para crear los destinos u objetivos para nuestro NPC, lo que le dará un comportamiento más natural a comparación de un comportamiento lineal o por animación.Ahora pasamos a una explicación mas detalla del código.
“`public int id;GameObject wp;“`Declaramos una variable publica de tipo entero llamado id, la cual usaremos para identificar cada waypoint. También declaramos una variable privada de tipo GameObject nombrada wp, la cual contendrá al objeto cubo que representa al waypoint.

void Update () {
        if (Input.GetKeyUp(KeyCode.Space))
            CreateTarget();
	}

Si tocamos la tecla espacio llamará al método de crear objetivo.

void CreateTarget()
    {
        if (wp != null)
            Destroy(wp);
        GameObject waypoint = GameObject.CreatePrimitive(PrimitiveType.Cube);
        waypoint.transform.position = new Vector3(Random.Range(7.0f, -7.0f), 0.03f, 
        Random.Range(7.0f, -7.0f));
        waypoint.name = "Waypoint " + id;
        id++;
        waypoint.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f);
        //waypoint.GetComponent<MeshRenderer>().enabled = false;
        wp = waypoint;
    }

Por último tenemos el método crear objetivo. Primero se comprueba si wp no esta vacio , en ese caso se destruye el wp existente, luego se crea un cubo nuevo en una posicion aleatoria dentro de un rango , se le asigna un nombre incluyendo en él su id, se le asigna un tamaño de 0.5 y luego se guarda el nuevo waypoint en la variable wp.Existe un código comentado que sirve para ocultar la parte visible del cubo, ya que a fines prácticos nosotros no queremos ver los waypoints de nuestros NPC’s.
Bien eso sería todo, espero que les guste, no se olviden de comentar y compartir si les gusto el post.

En la siguiente edición como detectar la distancia y dirección para que el NPC se dirija al waypoint.
curso sobre desarrollo de videojuegos en unity
Pulsa en la imagen para ir al curso.