Jump to content

Search the Community

Showing results for tags 'coroutines'.



More search options

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • General
    • Proyectos
    • Offtopic
    • Unity Feeds
    • Website
    • Youtube
  • Mesa de ayuda
    • Unity
    • Arte
    • Builds & Dispositivos
    • Otras Especialidades
  • Aportes de la Comunidad
    • Recursos
    • Eventos
  • Bolsa de trabajo & Colaboraciones
    • Ofertas y Demandas
  • Post Antiguos
    • General (Antiguo)

Blogs

There are no results to display.

There are no results to display.


Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Especialidad

Found 4 results

  1. Saludos colegas y entusiastas de la programación, quiero iniciar este articulo diciéndoles de entrada que no voy a tocar todos los aspectos de la inteligencia artificial ni tampoco haré ejemplos extensos ya que si lo hiciera podría escribir un libro de al menos unas 500 paginas, al menos con mi conocimiento, estoy seguro que otros podrían escribir de mas paginas, ya que es un tema bastante extenso. Lo primero que debemos hacer (se aplica a todo en programación) es establecer el o los problemas, les recomiendo que busquen unaherramienta* para crear mapas mentales o tener un pizarron o mas simple un lápiz y un papel porque esto requerirá de unaLÓGICA ACERTADA, recuerden que lo que pretendemos hacer es crear una simulación de inteligencia, analicemos el siguiente mapa mental: Lo que primero debemos saber es que nuestro actor con AI deberá tener un comportamiento principal, este comportamiento definirá sus acciones y movilidad. Este actor también deberá tener varios estados por ejemplo “en persecucion” “default”(viene dado por el comportamiento, ejem.: patrullando, explorando,…),”bajo ataque”, “en alerta” y los que uds. consideren que pudiera aplicar, estos estados serán importante para informar a otros componentes que esta haciendo el actor. Lo segundo que deberemos definir es un área de acción en los casos de patrullaje y de defensa, esto sera necesario para definir la movilidad del actor. La movilidad de un actor puede gestionarse perfectamente a través de un navmesh, esto nos ahorrara mucho calculo. El navagent se encargara de encontrar una ruta hacia un punto este punto se puede definir en cualquier momento al igual que la velocidad a la que se dirigirá hacia él, y también la distancia de frenado, estos son 3 aspectos que nos harán la vida mas fácil. Ahora que tengamos esto listo podremos resolver nuestro siguiente problema y es quizás el punto mas importante en AI para videojuegos la percepción. La PercepciónLa percepción se podría definir como “el primer conocimiento de una cosa por medio de las impresiones que comunican los sentidos”. La percepción disparara comportamientos y acciones en nuestro actor con AI de allí su importancia en programarla. Lo primero que debemos definir son lossentidos que tendrá nuestro actor, como vieron en la imagen yo solo he definido la vista, el tacto y la audición, aunque pudieran definirse otros, inclusive sentidos de fantasía como la habilidad de percibir enemigos que estén detrás de las paredes por ejemplo. Empecemos por el menos complejo, el tacto. Digo que este sentido es el menos complejo ya que este ya viene preparado por uniti con los colliders, un simple OnCollisionEnter podrá notificar que algo o un enemigo ha sido detectado. Y esto nos llevara a un punto importante, ladireccion de dicha interacción, para ello podemos recurrir a la propiedad “contacts[0].point”* del objeto “Collision” que nos da OnCollisionEnter, con ello podemos calcular la dirección de dicha interaccion, por ejemplo: 123void OnCollisionEnter(Collision other) { touchDirection = (other.contacts[0].point - transform.position);}y como pasaría en la vida real, dicha interacción provocaría una reacción motora inmediata la cual seria un giro y evasión. Con la información recogida por el sentido ya podríamos ser capaces de rotar hacia la dirección nuestro actor y moverlo en sentido opuesto al contacto con algo como esto: 12345void Update() { transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(touchDirection ), Time.deltaTime * turnSpeed); float step = speed * Time.deltaTime; transform.position = Vector3.MoveTowards(transform.position, touchDirection, step);}Ya tenemos la ‘información recogida por el sentido‘ y la respuesta motora correspondiente, si por ejemplo estamos programando un shooter deberemos disparar o no en dirección al contacto o tomar alguna acción en consecuencia, y para ello nos apoyaríamos seguramente en otro sentido como la vista por ejemplo para poder apuntar o captar donde esta nuestro enemigo pero eso lo trataremos mas adelante, pasemos al siguiente sentido. La AudiciónPara este sentido pudiéramos usar un SphereCollider y activar isTrigger, de manera de usar su radio como radio de detección, es decir el actor podrá “escuchar” todo lo con este dentro de este radio, para ello podríamos usar la función OnTriggerStay(Collider other) para gestionar la información, por ejemplo: 123456public void OnTriggerStay(Collider other) { AudioSource audioSource = other.transform.root.GetComponent<AudioSource>(); // asumiendo que el componente audiosource estara siempre en la raiz del enemigo if(audioSource != null && audioSource.isPlaying){ // implementacion... }}de esta manera podría saber si el objeto dentro del rango de audición esta reproduciendo algún sonido e inclusive podríamos aplicar algunas atenuantes de sonido, por ejemplo el volumen, la obstrucción por parte de muros y otros objetos, e incluso otros sonidos cercanos. Como en el sentido anterior esta recepción de información de igual forma disparara una reacción motora solo que esta podría ser mas relajada, ya que normalmente la percepción de un sonido aumenta desde 0 a medida que el origen se acerca a la posición del actor, por ello la reacción seria mas de “curiosidad”, como en la vida real un sonido nos puede alertar que algo esta cerca y gracias a que tenemos dos oídos tenemos la habilidad de escuchar en stereo, eso, una sutil diferencia de decibeles entre un oído y el otro puede dar la dirección aproximada del origen del sonido y con ello podríamos ir a investigar. Podemos usar el mismo principio que aplicamos en el sentido anterior para rotar el actor debido a que estamos gestionando el sentido a través de un trigger podemos obtener la posición exacta de la fuente con algo parecido: 12345678public void OnTriggerStay(Collider other) { AudioSource audioSource = other.transform.root.GetComponent<AudioSource>(); // asumiendo que el componente audiosource estara siempre en la raiz del enemigo if(audioSource != null && audioSource.isPlaying){ // implementacion de atenuacion... soundDirection = (other.transform.position - transform.position); transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(soundDirection), Time.deltaTime * turnSpeed); }}inclusive si usamos un navmesh podríamos definir other.transform.position como punto de destino a fin de investigar oexplorar si el origen esta tras un muro y así tomar una acción en consecuencia bien sea apoyándose en la vista para confirmar que fue lo que causo el sonido o tomar una acción ciegamente por ejemplo disparar sin confirmación visual. Como pudimos notar tanto el sentido del tacto como el auditivo, pueden llevar al actor a apoyarse en el sentido de la vista para tomar una acción definitiva, si por ejemplo disparar o interactuar con el origen de la información, por esto he dejado este sentido de ultimo pero ahora debemos considerarlo. La VisiónPara la visión debemos establecer dos variables importantes el campo visual y el rango de visión estos son dos factores importantes para la detección visual de otra entidad en el videojuego. El campo visual lo expresaremos en grados y este sera el angulo con respecto al frente del actor, lo normal seria establecer un campo de visión de 90° esto nos dará un angulo total de 180° en frente de nuestro personaje. El rango de visión lo expresaremos con un float y sera el resultado de Vector3.Distance. Siempre es muy recomendable crear un GameControl o como lo quieran llamar, un script que gestione en general aspectos del juego o el nivel: 1234567891011121314public class GameControl : MonoBehaviour{ // implementacion singletone public static GameControl mainGameControl; public List<GameObject> actores = new List<GameObject>(); public void Awake() { if (mainGameControl == null) { mainGameControl = this; } }}como notaron tenemos una variable llamada actores, la que llenaremos a través de los diferentes scripts que tengamos para cargar enemigos o jugadores en la escena. De esta manera podemos tener una lista de los actores activos en la escena de manera de poder recurrir a esta para verificar la posición de cada enemigo y distancia del actor para saber si esta dentro del campo y rango visual, veamos el siguiente ejemplo: 1234567891011public void Update(){ IEnumerable<GameObject> actores = GameControl.mainGameControl.actores.Where(e => (e != null || e.transform != null || e.transform.root != null || e.transform.root.gameObject != null) && !e.GetInstanceID().Equals(gameObject.GetInstanceID()) && nameIsEnemy.Any(nk => e.transform.root.tag.Contains(nk) || e.transform.root.name.Contains(nk) ) && Vector3.Distance(transform.position, e.transform.root.position) < maxDistanceSight );}en el ejemplo anterior creamos un IEnumerable de los GameObjects que cumplen las condiciones dadas, existen en la escena, no es el mismo actor, también podemos usar un listado de strings llamado “nameIsEnemy” para definir que los objetos que contenga cierta palabra en el tag o en el nombre serán considerados como enemigos, y una variable llamada “maxDistanceSight” donde pudiéramos definir la distancia máxima de visión de manera de limitar el rango visual, noten también que estamos accediendo a la lista de actores del GameControl con la linea GameControl.mainGameControl.actores una de las ventajas de usar una implementacion singletone de un componente. Hasta ahora tenemos los actores activos que son enemigos potenciales y que están en rango visual, pero todavía no sabemos si están en campo de visión y si de hecho no están obstaculizados, no sabemos aun si son visibles o no. Yo he creado una clase que nos ayudara a filtrar esos actores para encontrar el enemigo al que se le deberá poner atención, el que cumpla ciertas condiciones que antes mencionamos, que no este obstaculizado y este en el campo de visión 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778public class Enemies : System.Object{ [SerializeField] public Vector3 point { get; set; } [SerializeField] public float distance { get; set; } [SerializeField] public float angle { get; set; } [SerializeField] public bool isVisible { get { return _isVisible; } set { _isVisible = value; } } public RaycastHit raycastHitInformation { get; private set; } AIEnums.Senses _sensoryModalities; public AIEnums.Senses sensoryModalities { get { return _sensoryModalities; } set { __sensoryModalities = value; _sensoryModalities = value; } } private GameObject _gameObject; public GameObject gameObject; { get { return _gameObject; } set { _gameObject = value; if (_monoBehaviour != null && _maxDistanceSight > 0) { this.isVisible = CheckForVisibility(_monoBehaviour, _maxDistanceSight); } } } public AIEnums.Senses __sensoryModalities; public bool _isVisible; private MonoBehaviour _monoBehaviour { get; set; } private float _maxDistanceSight { get; set; } public Enemies() { } public Enemies(MonoBehaviour monoBehaviour, float maxDistanceSight) { this._monoBehaviour = monoBehaviour; this._maxDistanceSight = maxDistanceSight; } public bool CheckForVisibility(MonoBehaviour monoBehaviour, float maxDistanceSight) { Vector3 itemFollowingPos = Vector3.zero; Vector3 actorPos = Vector3.zero; itemFollowingPos = gameObject.transform.position; actorPos = monoBehaviour.transform.position; Vector3 enemyDir = (itemFollowingPos - actorPos); RaycastHit hit; if (Physics.Raycast(actorPos, enemyDir, out hit, maxDistanceSight)) { if (gameObject == hit.transform.gameObject) { raycastHitInformation = hit; return true; } } raycastHitInformation = hit; return false; }}como pueden notar he creado un campo llamado gameObject que contendrá el gameObject del enemigo y en el cuerpo del set de este campo se ejecutara un método llamado “CheckForVisibility” el cual envía un rayo a dicho enemigo para verificar si hace hit es porque no esta obstaculizado y devuelve true o en caso contrario devuelve false y lo guarda en el campo llamado isVisible, ademas tenemos las propiedades point, distance y angle que nos serán de utilidad mas tarde. Volviendo a nuestro componente de AI debemos recabar la informacion que nos interesa de cada actor: 12345678IEnumerable<Enemies> enemies = actores.Select(x => new Enemies(monoBehaviour, maxDistanceSight){ gameObject = x, distance = Vector3.Distance(monoBehaviour.transform.position, x.transform.root.position), angle = Vector3.Angle(monoBehaviour.transform.gameObject.transform.forward, (x.transform.position - monoBehaviour.transform.gameObject.transform.position)), point = x.transform.position, sensoryModalities = AIEnums.Senses.Vision,});entonces tendremos una lista con cada enemigo, la distancia, el angulo en relación al frente de nuestro actor y la posición del enemigo. Con el listado como esta y asumiendo que contenga algún ítem, solo bastaría ordenar la lista de la siguiente manera con linq: 12345List<Enemies> enemysInRange = new List<Enemies>();if (enemies.Count() > 0){ enemysInRange = enemies.OrderBy(e => e.isVisible).ThenBy(e => e.angle).ThenBy(e => e.distance).ToList();}ordenamos primero si isVisible esta en true primero, luego los que tengan menor valor en la propiedad angle y por ultimo los que tengan menos distancia. Ya de esta manera lo que debemos es obtener el primer elemento de esta coleccion 1234567891011if (enemysInRange.Count > 0){ Enemies enemy = enemysInRange.First(); if (enemy.isVisible) { if(enemy.angle < FOV) { } }}ya acá podemos saber si el enemigo esta en el campo visual, donde FOV es como les comente anteriormente el campo visual y esta expresado en grados, este sera el angulo en relación al forward o el frente de nuestro actor donde forward es 0 y sin importar si esta a la derecha o a la izquierda si el angulo del enemigo es menor a FOV quiere decir que esta dentro del campo visual. Podemos incluso verificar un segundo angulo para saber si esta en angulo de tiro y así accionar el disparador en el caso de que sea un shooter 1if (enemy.angle < FOVForFire)Otro tema importante es el performance del componente, como pudieron observar para llegar a este punto debimos de hacer unas cuantas comprobaciones y estas comprobaciones deben ser efectuadas a cada instante por ello en el componente pudieron notar que estaba en la función Update ejecutándose en cada frame, pero esto no es lo mas idóneo ya que esto significaría un uso cuantioso por el numero de comprobaciones y constante por lo que lo mejor seria ejecutarla en una corutina o coroutine para que puedan aplicar bien este concepto les recomiendo que pasen por el articulo Entendiendo las Coroutines (Co-Rutinas) donde explico mejor este tema. Como les dije al comienzo no toque todos los aspectos de la inteligencia artificial ya que seria muy extenso el articulo, pero espero haberles dado una base o una idea de como armar su propia clase, tratare de en futuros artículos profundizar mas en algunos puntos de la AI por separado, por ejemplo, que el actor persiga al enemigo, tocare otros temas acerca de la navegación por navmesh, y otros comportamientos que pudieran presentarse en un juego. Nuevamente espero que le saquen provecho e inclusive puedan mejorar este tema con sus comentarios y sugerencias, lamento haberles alargado tanto la lectura pero como un iceberg esto es solo la punta. Saludos! fuente: https://eleazarcelis.wordpress.com/2015/10/10/creando-una-clase-basica-de-inteligencia-artificial-ai-en-unity/
  2. Que son las Co-Rutinas? Las Co-Rutinas (Coroutines) son métodos que tienen la capacidad de pausarse y reiniciarse exactamente donde se quedo en el frame anterior. Para entenderlo mejor, consideremos el siguiente ejemplo de la documentación oficial : 1234567void Fade() { for (float f = 1f; f >= 0; f -= 0.1f) { Color c = renderer.material.color; c.a = f; renderer.material.color = c; }}Si quisiéramos hacer una función como la anterior y la colocamos en el Update no tendría ningún efecto fade debido a que la funcion se ejecutara toda en el mismo frame y por ello no se renderizara los estados intermedios entre 1 y 0. En cambio si la colocáramos dentro de una Co-Rutina de la siguiente manera: 12345678IEnumerator Fade() { for (float f = 1f; f >= 0; f -= 0.1f) { Color c = renderer.material.color; c.a = f; renderer.material.color = c; yield return null; }}La función se pausara en la linea donde esta el yield y el bucle for continuara en el siguiente frame sin perder el valor de la variable f de esta manera en cada frame se renderizara el cambio entre el valor de c.a y c.a -0.1. Adicionalmente las Co-Rutinas se pueden pausar cada n segundos con la clase WaitForSeconds como vemos en el siguiente ejemplo: 12345678IEnumerator Fade() { for (float f = 1f; f >= 0; f -= 0.1f) { Color c = renderer.material.color; c.a = f; renderer.material.color = c; yield return new WaitForSeconds(.1f); }}Esto es de suma importancia ya que podemos controlar la periodicidad del código de manera que no se ejecute en cada frame como la función Update, sino cada cierto tiempo. Para entender la importancia de este punto ejemplifiquemos el asunto. Supongamos que estamos creando una clase de AI (inteligencia artificial) y estamos diseñando la característica de Percepción de nuestro actor, si queremos que la clase este constantemente checando la posición de un enemigo, para calcular la distancia del enemigo, o si esta en rango y campo de visión o para verificar con un raycast si esta visible o no, esto podría significar un gasto cuantioso de recursos y podría afectar el rendimiento del juego. 1234567891011function Percepcion(){ for (int i = 0; i < enemies.Length; i++) { // implementacion aqui.... }} void Update(){ Percepcion();}Ahora bien, si usamos una Co-Rutina 123456IEnumerator Percepcion() { for(;;) { // implementacion aqui... yield return new WaitForSeconds(.5f); }}la implementación se ejecutara cada medio segundo y no en cada frame lo que representa una disminución significativa en el uso constante de recursos, e impactara de manera positiva en el performance del juego. Las Co-Rutinas se ejecutan en paralelo? Esta es una cuestión que siempre se da por sentado (inclusive yo mismo), e investigando un poco sobre este asunto me he percatado que realmente Unity no menciona explicitamente que sea de esa manera, así que me dispuse a hacer unas pruebas para verificar esto. 12345678910111213141516171819public class PruebaCoRutinas : MonoBehaviour { void Start () { StartCoroutine(CoRutina()); } void Update () { Debug.Log("Iniciando Update " + Time.frameCount); } IEnumerator CoRutina() { for (;;) { Debug.Log("Desde Corutina " + Time.frameCount); yield return new WaitForSeconds(1f); } }}Si ejecutamos el codigo anterior y abrimos la ventana de subprocesos de Visual Studio como se ve en las siguientes imagenes, notaremos inmediatamente que se ejecutan en el mismo subproceso, lo que prueba que la ejecución de una Co-Rutina en realidad no corre en paralelo. Espero les haya sido útil el articulo, siéntanse libres de comentar para alguna corrección o sugerencia. Fuentes: Unity Oficial Doc, Mi Blog
  3. bueno como el tema lo dice quisiera saber si piensan que las coroutines son en realidad de gran utilidad o no. Yo soy un poco nuevo en tema de C# y eh aprendido bastante estos últimos meses y al descubrir la función de las coroutines las vi que eran de muy buena utilidad que rapido las empece a agregar y utilizar bastante en mi codigo, pero ahora que mi código a crecido y a avanzado veo que las coroutines me estan dando un poco de dolor de cabeza, bueno en realidad en tema de su funciona son bastante útiles pero en donde me eh dado cuenta de su desventaja es al momento de querer detenerlas, bueno mas que nada es al momento de pausar el juego las coroutine siguen su curso y no se detienen al menos que se le agregue un poco de código extra pero aun asi no se recuperan (al quitar la pausa) en el punto en donde se quedaron y eso provoca un problema, un ejemplo sencillo es poner un ship que dispare cada 2 segundos, al momento de poner pausa en el mismo momento que dispare, si pongo un IF dentro de la coroutine para que el código no se ejecute mientras está en pausa, pero la coroutine sigue haciendo su conteo, si al momento de quitar la pausa el conteo de la coroutine está al final de su conteo de 2 segundos provocará un doble disparo. Eh visto también que agregan un WaitForEndOfFrame para detener la coroutine, aun no lo eh probado pero al analizar la funcion que haria WaitForEndOfFrame creo que pasaría lo mismo que poner un IF.Tambien eh visto otra personas que agregan un código algo grande con muchas líneas para determinar el tiempo de la coroutine pero siendo así ya la coroutine pierde su encanto ya que prefiero hacer un código con DeltaTime y crear mi propio código que sería solo una cuantas líneas en vez de estar modificando una coroutine. ok almenos eso es lo que eh visto y me eh dado cuenta sobre las coroutines, son en realidad útiles o no? o será que me estoy perdiendo de algún truco que google no me a querido mostrar?, Usan Ustedes coroutines para sus codigo? si existe algún truco para detener una coroutine y su conteo haganlo saber please, gracias
  4. Buenas... es sólo una pequeña duda... ¿Las "Coroutines" son específicas de Unity o existen en C# en general? Lo pregunto porque al buscar en Google todo me remite a Unity, y es que me parecen de una utilidad extrema... no tengo planes todavía de dar el salto a otro tipo de aplicaciones (soy novato y prefiero centrarme todavía en Unity), pero me da curiosidad, porque el gusanillo de intentar otro tipo de proyectos que no sean solo juegos me resulta cada día más atractivo... Soy novato... soy de letras... pero cada día me engancha más esto... Doctor: ¿¿¿Qué es lo que tengo???
UnitySpain © Todos los derechos reservados 2020
×
×
  • Create New...