post

En la mayoría de los casos, a la hora de desarrollar un videojuego, se nos plantea la necesidad de mover ciertos objetos en nuestra escena y que, además, sigan una cierta secuencia o patrón para así construir algo dinámico y práctico. Ejemplos de ello serían: un ascensor que sube y baja para darnos acceso a un lugar determinado, un enemigo que se mueve de un punto a otro del escenario a modo de patrulla, una plataforma que se va moviendo a través de una serie de coordenadas de la escena, etc.. En definitiva, objetos que seguirán un movimiento programado por nosotros.

En este artículo vamos a implementar un sistema de patrones genérico consistente en un recorrido formado por diferentes hitos colocados en nuestra escena y por los cuales cualquier objeto se moverá automáticamente.

Ejemplo de patrón de movimientos

Ejemplo de patrón de movimientos

Además prepararemos dicho sistema para que cuando el objeto que se mueve llegue a cada uno de los hitos, éste se detenga y realice una determinada acción antes de proseguir hacia el siguiente hito.

Imaginemos las posibilidades que esto nos ofrecerá a la hora de montar diferentes situaciones en nuestro juego… ¡Vamos manos a la obra!

Creación del script ‘ComportamientoPatron.cs’

Comenzaremos por implementar el script que asignaremos al GameObject que se irá moviendo. En él definiremos 3 variables públicas que después podremos configurar desde el inspector:

  • Terrestre: a través de esta variable indicaremos si el movimiento que seguirá nuestro objeto será tan sólo terrestre (aplicando la gravedad) o a través de todo el espacio (sin aplicar gravedad).
  • HitosPatronMovimiento: será la lista ordenada de objetos que representarán los hitos que seguirá el objeto.
  • VelocidadesPatronMovimiento: será la lista de velocidades a las que se moverá el objeto cuando se dirija a cada uno de los hitos definidos en la lista ‘HitosPatronMovimiento’.

También definiremos algunas variables privadas y propiedades que necesitaremos más adelante:

Ahora vamos a crear la función privada ‘IrHaciaHito()‘ que se encargará de mover el objeto hacia un determinado hito y a una determinada velocidad. Nos devolverá true si el objeto ha llegado al hito indicado:

Implementamos la función Start() de nuestro script para inicializar las variables privadas:

Por último, implementaremos la función Update() del script, que se encargará de realizar todo el trabajo: mover el objeto (a través de la llamada a nuestra función privada declarada más arriba) y controlar sus paradas en los diferentes hitos. En el mismo código vamos explicando cada cosa:

Una vez que tenemos finalizado nuestro script, se lo asignaremos al objeto de nuestra escena (en este ejemplo, representado por un cubo) que queramos que siga un patrón de movimientos. El único requerimiento que debe cumplir este objeto para que el script funcione correctamente es que contenga el componente Rigidbody.

Script ‘ComportamientoPatron.cs’ asignado al objeto

Creación y configuración de los hitos que formarán el patrón

Los hitos de nuestro patrón no serán más que coordenadas del espacio de nuestra escena, por tanto nos bastará con representarlos con GameObjects vacíos. Para este ejemplo vamos a crear 3 hitos (en este ejemplo representados por esferas blancas) y los colocaremos en distintas posiciones de la escena:

Hitos creados en la escena (representados por esferas blancas)

Hitos creados en la escena (representados por esferas blancas)

Una vez que los tenemos creados, solo nos falta indicarle a nuestro cubo la lista ordenada de hitos que tendrá que seguir. Para ello nos iremos al inspector del objeto y configuraremos nuestro script ‘ComportamientoPatron.cs’ del siguiente modo:

Configuración de los hitos

Configuración de los hitos

Como vemos, hemos configurado la lista de hitos de tamaño 3 para arrastrar ahí cada uno de los hitos que hemos creado anteriormente y la lista de velocidades con el mismo tamaño indicando las velocidades que queremos entre hito e hito:
– En caso de no introducir ningún hito en la lista de hitos, nuestro cubo simplemente se quedará parado:
En caso de no introducir algún valor en la lista de velocidades, nuestro cubo se parará cuando no encuentre qué velocidad aplicar.

Objeto siguiendo el patrón en modo terrestre

Objeto siguiendo el patrón en modo Terrestre = true

Si ejecutamos nuestro juego veremos que el cubo, esté donde esté colocado, comenzará a moverse hacia el primero de los hitos que le indicamos, cuando llega a él automáticamente comienza a moverse hacia el segundo y así hasta el tercer hito, donde volverá a moverse hacia el primero y vuelve a empezar. Vemos cómo al cambiar a true o a false la variable ‘Terrestre‘ del script, nuestro cubo seguirá el mismo patrón pero por tierra o aire.

Objeto siguiendo el patrón en modo no terrestre

Objeto siguiendo el patrón en modo no Terrestre = false

Con esto ya tendríamos montada toda la parte referente al movimiento en nuestro sistema, pero como vemos aún nos faltaría hacer que nuestro objeto, cuando pase por los hitos, realice acciones determinadas como por ejemplo desaparecer, cambiar su tamaño o cualquier otra cosa que se nos ocurra. Como veremos a continuación, con este sistema de patrones tendremos total libertad a la hora de definir las acciones en cada uno de los hitos.

Crear scripts de comportamiento para los hitos

Los scripts de comportamiento serán scripts que añadiremos en cada uno de los hitos y que implementarán las acciones que deberá realizar nuestro objeto cuando llegue a dichos hitos. Podremos hacer tantos scripts de comportamiento como deseemos e incluso asignar a un mismo hito más de un script. El único requerimiento que deberán tener todos estos scripts de comportamiento es que su nombre comience con la palabra ‘Patron, para que así nuestro scriptComportamientoPatron.cs‘ los reconozca.

Vamos a implementar la estructura común que tendrán todos los scripts de comportamiento. Comenzaremos por definir las siguientes variables públicas:

  • ObjetoActivador: Será nuestro objeto, el que se va moviendo (en este ejemplo, el cubo).
  • TiempoAntesDeAccion: Es el tiempo, en segundos, que transcurrirá desde que el objeto activador llega al hito hasta que comienza a realizar la acción.
  • TiempoDespuesDeAccion: Es el tiempo, en segundos, que transcurrirá desde que el objeto activador termina de realizar la acción hasta que comienza a irse hacia el siguiente hito.

También definiremos algunas variables privadas:

Pasamos a implementar la función Start() de inicialización:

Y por último la función Update():

Esta sería la plantilla que usaríamos para todos nuestros scripts de comportamiento y, como vemos, en la parte del código comentada como ”IMPLEMENTACIÓN DE LA ACCIÓN…” es donde escribiríamos el código referente a la acción concreta que queramos hacer con el objeto cuando llegue al hito. A partir de aquí ya seremos libres de crearnos tantos scripts de comportamiento específicos como queramos.

Script de comportamiento asociado a un hito

Ejemplo de script de comportamiento asociado a un hito

Una vez que tenemos finalizados nuestros scripts de comportamiento, los asignaremos a cada uno de los hitos donde queramos que se apliquen.

Y eso es todo, ya tendríamos acabado por completo nuestro sistema de patrones de movimiento. El resultado final será el desplazamiento de nuestro objeto a través de cada uno de los hitos definidos en la escena y, a su vez, realizando acciones específicas (o simplemente ninguna acción no asignando ningún script de comportamiento) en función de la posición de cada hito.

Conclusión

Las posibilidades que nos ofrece un sistema de patrones como este son infinitas. Es ahora el lector el que debe hacer uso de su ingenio y creatividad para construir sistemas complejos que se adapten a las necesidades de su juego. Imaginemos desde mecánicas sencillas de movimiento en bucle de ciertos elementos del escenario hasta otras más complejas que hagan uso de scripts de comportamiento capaces, por ejemplo, de modificar aleatoriamente el orden de los hitos en tiempo real, lo cual crearía un movimiento totalmente imprevisible del objeto. ¿Os imagináis construir el comportamiento de un jefe final a base de patrones…?. La complejidad del sistema que montemos va a depender de la complejidad de los diferentes scripts de comportamiento que hagamos. Ahora… ¡a crear!

Acerca de Santi Andrade

Canturreo canciones, hago cosas raras de informáticos y amo las croquetas. Web & Unity Game Developer y miembro de la banda de rock 'Histeria Innokua'. histeriagamedev.wordpress.com | Sígueme en Twitter

85 respuestas sobre “Unity3D: Sistema de patrones de movimiento

  1. Muy buenas perfecto tutorial me sirvió muchisimo sin embargo tiene una pequeña errata, en la diapositiva numero 2, el math.Abs….. me da error, me pide que sea MathF.abs entonces o bien yo me estoy equivocando o bien es que hay una errata, por lo demas esta perfecto el tutorial.

    • Hola Helio, debería funcionarte con “Math.Abs”, tal y como está puesto en el tutorial. Es una función que está contenida dentro de la librería “System”, ¿has añadido su referencia?, mira a ver si se te ha olvidado añadir el “using System;” al inicio del script. De todas formas, en caso de que tengas que utilizar la función de “Mathf” en vez de la de “Math”, también te servirá.

      ¡Gracias por tu comentario y me alegro de que te haya sido útil el tutorial! 🙂

      • Hola Santi, estoy probando el script y no me funciona.. el problema es mio, se programar en R y C (aunque hace tiempo que no lo toco). Javascript y cs aun soy novato. Este script funciona en .js?
        La funcion se ve muy buena, pero me da error al compilarlo en unity, problemas con monobehaviour y otras cosas.. si puedes ayudarme por mail..
        Te comento lo que quiero hacer. Una funcion como la que has puesto (tipo ascensor, que suba y baje un objeto). Hasta ahora, solo consigo que suba hasta el infinito.. jajaja
        Estoy haciendo juegos para android, ya tengo uno casi listo.. solo me falta incorporar esto que te cuento.
        Cuando lo tenga listo lo compartiré con vosotros! No seais muy criticos que es el primero!!
        Saludos!

        • Nada, solucionado, Funciona perfecto! Lo he adaptado un poco a mis necesitades y listo!

          Una cosilla, sabes de funciones para detener el tiempo de ejecucion? Por ejemplo..un objeto llega a un hito, se para 2 segundos, y continua..
          Las funciones que conozco de otros lenguajes son sleep(). Per en cs no sé que tal va..

          Saludos!

          • Hola Daniel, para eso puedes usar la variable “TiempoAntesDeAccion” que lleva cada script de comportamiento en cada hito. Es el tiempo que se esperará tu objeto sin hacer nada antes de realizar la acción, y esa acción simplemente puedes hacer que no haga nada, por tanto el objeto llegaría al hito, se pararía X tiempo y después seguiría al siguiente.

      • Hola de nuevo Santi, sigo aprendiendo, gracias por tus aportes! Una consulta a ver si alguien puede ayudarme. Estoy haciendo juegos para android. He creado un personaje 3D modelado en blender y lo he metido en un thridpersoncontroller ( en el ethan que viene como ejemplo en unity, dejandolo vacio previamente). Luego le meto un crossplatformprefab ( para hacer los botones tactiles para android). He probado tanto joystick como dualtouch. El caso es que, en el ordenador me funciona bien, pero al exportar el apk al movil..el personaje solo me anda hacia la derecha.. dirija donde lo dirija.. hacia alante y derecha.. En el caso del joystick.. lo mismo.. y el joystick se me va hacia la esquina inferior izquierda de la pantalla y se me queda ahí oculto..
        Alguien sabe que puede estar pasando? Como puedo arreglarlo? O como poner unos controladores de movimiento para android en un thirdpersoncontroller?
        Gracias de antemano!

          • Hola santi, gracias por responder! Ya lo solucioné..(gracias a otro aporte tuyo) Era una tonteria.. Tenia la pantalla mal escalada, y las anclas de los touch quedaban fuera de la pantalla del movil, aunque los botones aparecian dentro. Por eso no me respondía a la direccion que pulsaba..
            Por si a alguien le ocurre algo parecido: que sepa que es por el escalado de la pantalla a diferentes resoluciones.
            Gracias de nuevo!
            Ya os informaré cuando publique algunos juegos!! Para que veais sabiduría de Santi aplicada jejeje

            Saludos!

          • Me alegro de que lo hayas podido solucionar. La verdad es que tiene sentido que sea por el anclaje… a mí también me ha fastidiado alguna que otra vez 😉

            A ver si es verdad que podemos ver alguno de tus juegos pronto!!

            Ánimo y un saludo!!

          • Hola de nuevo! Lo prometido es deuda.
            Aquí os dejo el primer juego que he terminado.
            https://youtu.be/WvfFXG9i82k

            Estará en el play store a principios de año (gratis por supuesto)

            Espero que les guste, es simple (llevo poco tiempo en este mundillo). Pero ya tengo bastantes prototipos (y la practica hará el resto)… asi que si este primero va dando resultados, no pararé!
            Saludos y gracias por los tutos!

          • Hola Daniel, enhorabuena!! Me alegro de que vayas a publicar tu primer juego 🙂 Mucha suerte con su lanzamiento y ya me dirás qué tal! Avisa cuando esté en la store para echarle un ojo 😉

  2. Hola. Muy interesante su tutorial. Seria bueno que tuvieran el codigo tambien ya terminado. He esta copiando y pegando las partes en uno solo (comportamiento patron) y no se me da. La verdad no programo mucho y me serviria el script funcionando.

    • Hola Gonzalo, el objetivo de estos tutoriales es el aprendizaje por parte del propio lector de manera que, usando este código como referencia, sea capaz de adaptarlo a sus necesidades concretas. Debido a que este sistema de patrones es bastante genérico, no quise centrarlo en un solo caso concreto e intenté hacer entender que puede ser usado para todos los casos que se nos ocurran.

      Por lo que comentas referente a que no programas mucho, la verdad es que en estos tutoriales damos por hecho que el lector tiene unas mínimas nociones de programación con C# en Unity, entonces supongo que tu problema en gran parte será ese. De todos modos yo te recomiendo que, en vez de hacer ‘copy-paste’ del código de este tutorial, intentes seguirlo paso a paso entendiendo lo que hace y leyendo también los comentarios que hay en el mismo código. Y por supuesto, si tienes alguna duda, aquí estamos para ayudarte 😉

      ¡Gracias por leer el artículo!

      • Buenos días. Querría saber cómo hacer para que al llegar a uno de los hitos (hito1), al pulsar una u otra tecla, el cubo se mueva hacia hito2 o hito3. Muchas gracias!

        • Hola v0rt3x, lo que quieres hacer es muy sencillo con este sistema de patrones. Lo único que tienes que hacer es implementar un script de comportamiento (explicado en la sección “Crear scripts de comportamiento para los hitos” de este tutorial) en el que se espere la pulsación de una determinada tecla (a través del método Input.GetKey(…) de Unity) y que al pulsarla le asigne a la variable “workDone” el valor ‘true’. De este modo, cuando el objeto llegue a un hito, éste se parará (la variable workDone inicialmente será ‘false’), y cuando pulsemos una determinada tecla el objeto comenzará a moverse al siguiente hito (debido a que la variable “workDone” será ‘true’).

          Espero que te haya servido de ayuda, ¡gracias por leer el tutorial! 🙂

    • No puedes esperar que alguien te ragale las cosas tio, si fuera tan facil desarrollar videojuegos todo el mundo lo haría, aprende desde 0, no quieras correr porque todos hemos empezado en el mismo punto, si te animas poco a poco llegaras ser capaz de hacerlo han pasado 4 meses desde que me puse a este tutorial, y en aquel entonces no tenia las capacidades para terminarlo correctamente, y aqui estoy 4 meses despues tras haber estudiado muchas horas de unity/programación/animación y lo he pasado genial aprendiendo.

      mucha suerte

      • Hola Helio! Me alegra muchísimo saber que gente tan motivada como tú haya conseguido avanzar y evolucionar poco a poco y desde cero en el mundo del desarrollo de videojuegos. Si para ello alguno de mis artículos te ha servido de ayuda, para mí es un honor 🙂

        Muchas gracias por tu comentario!

  3. ¿Cómo puedo hacer para que el cubo mire hacia el siguiente hito? He probado con lookAt, pero el cubo se vuelve bastante loco al llegar al primer hito.

    • Hola Alfonso, pues lo que quieres hacer debería poder hacerse sin problemas con transform.LookAt(hito.transform). Lo investigaré en breve y te digo algo, ¿ok?
      El caso al que estás aplicando el sistema de patrones, ¿tiene alguna peculiaridad en especial a mencionar?. Es para intentar reproducir yo el mismo caso.

      Gracias por usar mis tutoriales… ¡y te responderé pronto! 😉

  4. Hola de ante mano muchas gracias por el tutorial me ha servido de mucho, pero queria preguntarte como se hace para que el cubo mire al siguente objetivo puesto que lo he aplicado a un personaje animado y este camina de lado. gracias

    • Hola Jesús! Muchas gracias a ti por seguir el tutorial 🙂

      Creo que tu duda es exactamente la misma que la de Alfonso (comentario anterior a este) y te comento lo mismo que a él: debería poder hacerse usando transform.LookAt(hito.transform), pero lo investigaré y en breve os digo algo.

      ¡Saludos y me alegro de que te esté siendo útil el tutorial!

    • Hola de nuevo Jesús, ya tengo la solución para tu problema. Lo que tienes que hacer es poner, dentro de la función Update() del script “ComportamientoPatron”, este código:


      Vector3 targetPoint = HitosPatronMovimiento[HitoSiguiente].transform.position;
      transform.LookAt(targetPoint);

      Y en la función IrHaciaHito() realizar el siguiente cambio:

      Cambiar la línea:
      thisTransform.Translate(VectorHaciaObjetivo * Time.deltaTime);

      Por esta:
      thisTransform.Translate(VectorHaciaObjetivo * Time.deltaTime, Space.World);

      Así, cuando el personaje llegue a cada hito, se orientará automáticamente hacia el siguiente. Espero que te sirva de ayuda. ¡Gracias!

  5. El problema esta en que el objeto en cuestión se mueve sobre un terreno con diferentes desniveles y al llegar a un Hito, el personaje realiza un rebote hacia atrás he intenta constantemente acercarse al hito. Este rebote cambia dependiendo de la altitud del Hito. ¿Cómo podría hacer un LookAt sólo en un eje?

    He incluido el transform.LookAt (HitosPatronMovimiento[HitoSiguiente].transform); en el update para que mire constantemente al hito al que se dirige.

    • Si quieres que el LookAt() gire sólo en el eje Y, puedes probar con esto:

      Vector3 targetPoint = HitosPatronMovimiento[HitoSiguiente].transform.position;
      targetPoint.y = 0.0F;
      transform.LookAt(targetPoint);

      Ahora mismo no estoy delante del PC y te lo pongo un poco de cabeza, pero pruébalo y dime si es lo que necesitas. Aún así lo probaré yo en cuanto pueda.

      Respecto a lo que me comentas de que el personaje realiza un rebote cuando llega a un hito… no entiendo por qué, ¿tus hitos no son de tipo trigger?.

  6. Gracias, lo del giro funciona, pero no se porque, al poner el Lookat da un rodeo para llegar al Hito. Para ir de uno a otro va haciendo una curva en vez de ir directamente de uno a otro.

    • Quizás lo suyo es que, cada vez que tu personaje llegue a cada hito, le hagas el LookAt(…) sólo una vez de manera que se oriente hacia el siguiente hito y después haga el movimiento normal (recto) hacia él. El hecho de que tú estés haciéndolo dentro del Update() hace que esté continuamente calculando rotaciones y quizás sea eso lo que te está dando los problemas de los rodeos. Prueba a hacerlo solo una vez por hito y a ver qué tal.

      Saludos!

    • Alfonso, ya lo tengo. Lo único que tienes que hacer es poner, dentro de la función Update() del script “ComportamientoPatron”, este código:


      Vector3 targetPoint = HitosPatronMovimiento[HitoSiguiente].transform.position;
      transform.LookAt(targetPoint);

      Y en la función IrHaciaHito realizar el siguiente cambio:

      Cambiar:
      thisTransform.Translate(VectorHaciaObjetivo * Time.deltaTime);

      Por:
      thisTransform.Translate(VectorHaciaObjetivo * Time.deltaTime, Space.World);

      (Voy a actualizar ahora mismo el código del tutorial con este útimo cambio)

      Con esto, cuando el personaje llega a cada hito, se orienta automáticamente hacia el siguiente. Espero que te sirva de ayuda… ¡y gracias por ayudarme a mejorar mi código! 🙂

  7. Como estas maestro, por favor podrias enseñarme un codigo, para hacer que el GameObject se mueva de un hito a otro, pero en 2D, (de izquierda a derecha), te lo agradeceria, si me explicaras plisss. Gracias

    • Hola Aldair! El código este tutorial funciona tanto para 3D como 2D… es exactamente lo mismo para ambos casos. Dime qué es lo que no te funciona en tu caso e intento ayudarte.

      Gracias por leer el artículo! 😉
      Saludos!

      • Gracias a ti maestro por responder, mira lo que necesito es que cuando el personaje vaya, a cierta distancia lo detecte el enemigo y lo ataque o persiga, por ejemplo en juegos como mario bross, que los enemigos se mueven de un lado a otro automaticamente, eso es lo que necesito verdaderamente, el juego es en 2D como super mario bross,! Y Disculpa si me puedes explicar el codigo en javascrip, solo si puedes, de lo contrario acepto en c# . Muchas Gracias

        • Hola de nuevo Aldair, lo que comentas de que el enemigo se mueva de un punto a otro automáticamente ya lo tienes hecho a través de este sistema de patrones, por tanto te reocomiendo que uses lo que explico en este tutorial.

          Respecto a que el enemigo nos persiga y nos ataque cuando estemos cerca, eso ya habría que implementar un algoritmo de inteligencia artificial para dicho enemigo de modo que éste sea capaz de detectarnos (por ejemplo haciendo uso de raycasts) y que realice las acciones necesarias. Obviamente yo no puedo ponerte aquí el código exacto para lo que necesitas ya que depende mucho del caso particular de cada juego, pero te animo a que investigues un poco sobre diferentes algoritmos de IA orientados al comportamiento de enemigos. Así a priori, lo que yo haría sería lanzar un raycast desde el enemigo y si el jugador es tocado por este raycast, entonces hacer que el enemigo realice la acción correspondiente (por ejemplo atacarnos).

          Saludos!

          • Excelente Maestro, ya logre, el objetivo, muchas gracias, si pudieras ayudarme con otra cosita, te agradecería, mira, estoy trabajando con sprites 2D, y logro que el enemigo camine hacia adelante y hacia atrás, pero cuando camina hacia atras no se voltea o no gira, yo necesitaría que cuando llegue a cierto punto y se devuelva voltee, o gire, porque esta haciendo un movimiento de ida y vuelta pero sin girar.. si sabes algo por favor orientarme gracias por la atención.. saludos¡¡

          • Hola aldair! Muchas gracias por leer mi post y comentar, me alegro de que te haya servido de ayuda 🙂
            Respecto a lo que me comentas de girar a tu enemigo hacia el sentido de su movimiento en cada momento, hay una manera muy fácil de hacerlo en este caso concreto en el que estás manejando sprites 2D: Yo lo que hago es sencillamente multiplicar por -1 el valor X del “Scale” de su componente “Transform” y de ese modo conseguiremos invertir el sprite hacia la izquierda y hacia la derecha. Sería algo así como esto:

            Si tu enemigo se mueve hacia la derecha:
            TRANSFORM_DEL_SPRITE.localScale = new Vector3(Mathf.Abs(TRANSFORM_DEL_SPRITE.localScale.x), TRANSFORM_DEL_SPRITE.localScale.y, TRANSFORM_DEL_SPRITE.localScale.z);

            Y si, por el contrario, tu enemigo se mueve hacia la izquierda:
            TRANSFORM_DEL_SPRITE.localScale = new Vector3(Mathf.Abs(TRANSFORM_DEL_SPRITE.localScale.x) * -1, TRANSFORM_DEL_SPRITE.localScale.y, TRANSFORM_DEL_SPRITE.localScale.z);

            Espero que eso te guíe!

            Saludos!

    • Hola aldair, pues la verdad es que aún no he tenido que implementar la mecánica de agacharse, pero así de primeras lo que yo haría sería, al pulsar eje vertical hacia abajo, reducir y desplazar ligeramente hacia abajo el collider del personaje. Te recomiendo que busques información en internet porque seguro que hay gente que ha tenido tu misma duda 😉 Si algún día llego a implementarlo en alguno de mis proyectos, que seguramente sí, haré un post sobre ello. Por ahora espero que al menos te sirva esta pista 😉

  8. Hola, muy bueno el tutorial (y).
    Tengo una duda, en la parte de codigo donde se hace esta condicion (script.GetType().Name.Contains(“Patron”)) en el foreach, no me extiende la funcion GetType() ni Contains, no se si seran funciones que ya no estan en la version q estoy usando, mi pregunta es como puedo hacer esa comparacion en las versiones mas nuevas de Unity. ya q no solo puedo hacer (script.name==”Patron”) ya que es una comparacion exacta y no q contenga la palabra patron.
    Gracias de antemano

    • Hola Jeank! Gracias por leer mi tutorial.

      Estoy ahora mismo probado el GetType().Name.Contains(…) en la última versión de Unity (5.1.2f1) y funciona perfectamente. Dichas funciones son funciones básicas de la librería “UnityEngine” y debería pillártelas. Lo único que se me ocurre es que no tengas importada la librería al principio de tu código con:

      using UnityEngine;

      Si sigues teniendo problemas, mándame tu código y le echo un vistazo.

      Saludos!

      • Hola Santi , gracias por responder.
        Aun tengo problemas con esa linea, lo resolví de la siguiente manera aunque no es la q yo quisiera:
        foreach(MonoBehaviour script in hitosScripts){
        bool sc= script.GetComponent(type:”PatronDeAccion”);

        if(sc){
        patronEncontrado=true;
        script.enabled=true;
        }
        }
        Pero de esa manera solo compara q el nombre del script sea totalmente como lo escribo dentro del GetComponent. Sin embargo quiero q la condicion solo revise si la palabra “Patron” existe dentro del nombre como una subcadena.

        El using UnityEngine si lo tengo como librería, aun q para poder usar la palabra reservada “Contains” tuve q implementar la libreria “Linq”, pero no la usé porq no hace lo q supone debería hacer.

        Por cierto tampoco el .Length me lo extiende.

  9. Buenos días Santi Andrade, he estado buscando como hacer una AI (Inteligencia Artificial) a un personaje 2D, cuando di con este tutorial. Y de pronto me podrías colaborar con lo que necesito. Para ser específico, estoy trabajando en un proyecto en el cual juego yo contra la maquina, por asi decirlo y somos 3 personajes, uno es manejado por mi y los otros dos se deben de mover por si solos (Estilo juego de carreras de motos), la cuestión es que quiero que los otros dos personajes se muevan desde un punto inicial a un punto final por si solos, teniendo en cuenta que trabajo con físicas 2D y por obvias razones rigibody2d, como podría hacer yo eso. Veo que este ejemplo es muy parecido a lo que necesito y si me puedes colaborar de ante mano muchas gracias.

    Feliz día…

    • Hola Andrés David, gracias por comentar. No sé si el uso del sistema de patrones de movimiento sería lo más adecuado para implementar la IA de tu juego de carreras por la sencilla razón de que este sistema de patrones sirve para definir un movimiento FIJO entre diferentes puntos del escenario, sin embargo una IA requeriría un comportamiento dinámico e incluso aleatorio de los personajes a los que aplica.

      Si lo que quieres es que las motos rivales de tu juego SIEMPRE hagan el mismo recorrido exacto en cada partida, pues entonces sí que podrías usar perfectamente este sistema de patrones para marcarles el recorrido, pero si lo que quieres es que tengan un comportamiento “inteligente” ya sería necesario implementar algo más complejo.

      Referente al tema de aplicar el sistema de patrones a un juego 3D o 2D, no habría ningún problema para ello, simplemente tener en cuenta que en el movimiento en 2D trabajamos con 2 ejes en el espacio a diferencia de en el 3D que trabajamos con 3 ejes.

      Saludos!

  10. Hola Santi, gracias por responder, pero te digo, los otros jugadores siempre van a estar en una linea recta, desde el inicio, hasta el final de la partida, solo que en el transcurso de la carrera van a poder hacer ciertas acciones (Como saltar, picar, aumentar la velocidad…). Entonces no creo que haya problemas; que es lo que pasa, que estoy trabajando con Físicas y motores (Wheel Joints 2D), y cuando estoy en una superficie plana no me toma en cuanta los motores, osea las ruedas no demuestran que estén girando, (SI hay desplazamiento, pero estático, me hago entender..), solo empiezan a girar cuando hay pendientes y ahi sii, se demuestra la fisica. Depronto, podes saber como podria solucionar esto. (Osea que me tome en cuenta las fisicas desde el principio, que las llantas giren)…

    Gracias.

    • Si el movimiento de los rivales siempre va a ser el mismo, puedes usar este sistema sin problemas. En cada hito implementarías la acción concreta que quieres que haga cada moto y perfecto 😉

      Respecto al problema que me comentas de las superficies planas, en principio, activando la variable “Terrestre” del script “ComportamientoPatron” y teniendo tus motos el componente Rigidbody para que se les aplique gravedad y apoyen sobre el suelo, debería funcionarte.

      Al activar la variable “Terrestre”, lo que hace el script es ignorar el eje Y del espacio para realizar el movimiento y por tanto hacer que el movimiento se deslice por el suelo.

  11. Hola como estas, me gustaria consultarte algo, quiero implementar un sistema de inteligencia artificial basico, es algo como esto: Cuando mi jugador colicione con un enemigo se pueda reproducir una animacion, sprite etc, en fin. con la idea de eliminarlo o causar daño. Si sabes algo por favor escribeme ¡Gracias¡ recuerda que estoy trabajando en 2D y en C#.

    • Hola de nuevo Aldair, lo que me preguntas se sale un poco de lo que trata este post concreto sobre patrones de movimiento. Implementar una IA es algo bastante amplio y difícil de explicar por un medio como este. Así a grandes rasgos, lo que yo haría para hacer lo que pides sería ponerle un collider de tipo trigger a cada enemigo y detectar, a través del evento OnTriggerEnter(), cuándo tu personaje colisiona contra dicho enemigo y, si es así, realizar las acciones necesarias en el enemigo: atacar, reproducir una animación, perseguirte, etc.

      Esto sería una idea general, pero obviamente la implementación de cada cosa (animación, ataques, etc.) requiere un trabajo de programación más extenso y que no puedo detallarte aquí ya que depende mucho de los requerimientos concretos de tu juego.

      Te recomiendo que vayas por pasos y que te centres en ir aprendiendo cada una de esas acciones (detectar colisiones, crear animaciones, etc.) mediante cursos o tutoriales, que en internet hay miles.

      Suerte y saludos!

    • Gracias si me funciono, ahora si puedes decirme como hago para que dos enemigos que choquen transpasen el uno con el otro, cualquier informacion por favor escribe Gracias de ante mano!!! Saludos!

      • Hola aldair, de nuevo lo que preguntas se sale por completo de la temática de este post en concreto. Te agradecería que las dudas que tengas aparte de lo explicado en el blog me las enviaras vía email, gracias. Para hacer lo que dices de que dos enemigos se transpasen, yo lo que haría sería ponerles colliders con la opción “IsTrigger” activada para que no colisionen entre sí, entonces para detectar los ataques entre ellos se usarían sus eventos “OnTriggerEnter()” para saber cuándo se están tocando y cuando no. Saludos!

  12. Gran trabajo Santi!! La verdad es que me ha ayudado mucho a desarrollar el trabajo que quiero hacer. Te comento, la idea es hacer un texto que aparezca en la parte inferior de la pantalla y que éste, aparezca por la derecha y desaparezca por la izquierda (tipo noticias que salen en los informativos que aparecen en la parte inferior de la pantalla). Para ello, he utilizado tu patrón de movimientos, en la que el texto hace como una especie de bucle, así el texto, pasado un tiempo, vuelve a aparecer en pantalla. Como el tiempo que tarda es quizá demasiado, he pensado que cuando llegué al primer hito, el texto se destruya y el hito de origen vuelva a crear el texto, pero no consigo que me funcione bien. Me podrías echar una mano?

    Gracias de antemano

    • Hola Jose! Ante todo gracias por el comentario, me alegro de que te sirva de ayuda mi sistema.
      Sobre lo que me comentas que quieres hacer, te digo: yo, por motivos de optimización, evitaría la destrucción/instanciación de tu objeto en tiempo de ejecución. Lo que haría en su lugar, haciendo uso de este sistema de patrones, sería lo siguiente: cuando llegues al segundo hito (es decir, donde el texto ya no es visible en pantalla) le desactivas su componente “renderer” ([gameobject].renderer.enabled = false;) para que el texto se haga invisible, ahora haces que vuelva al primer hito (en este caso puedes aumentarle la velocidad para que no tarde tanto en regresar a la posición inicial) y allí le vuelves a activar su componente “renderer” (([gameobject].renderer.enabled = true;)), con lo cual el texto volverá a ser visible y estará listo para que se mueva de nuevo hacia el hito 2, y así sucesivamente. No sé si me explico, ya me dirás si te sirve de ayuda! 😉

  13. Exelente tutorial la verdad que me ayudó bastante..no encontraba en ningun lado como darle movimiento a objeto y me ayudaste bastante de ante mano gracias por darte el tiempo a ayudar a los aspirantes a programadores de videojuegos exelente tu trabajo y bien explicado.. sólo una pregunta.. como puedo hacer para que el objeto rote a llegar a un hito?? Osea cuando llega al primer hito y va por el otro no lo hace de frente si no de costado o de atras..les tendria que asignar un script de rotación a los hito?? O lo puedo hacer desde el mismo script del objeto?? Desde ya muchas gracias..

  14. Alguien tiene el codigo entero, me da errores cuando lo copio por partes.

    A parte no entiendo porque cierras el monobehavior en la primera parte del codigo,ni porque declaras una variable en la segunda parte del codigo, ni que no este el update arriba del todo en la 2 parte del codigo.

    Gracias!!

    • Hola Xavi Vicens, tengo pendiente subir a mi GitHub todo el código con pequeño un ejemplo donde se muestre su funcionamieto. De todos modos el código, tal y como está en este tutorial, funciona bien. Lo que pasa es que, obviamente, copiando y pegando tal cual está en el blog no te va a funcionar… primero hay que entender lo que hace.

      Los trozos de código que voy escribiendo están puestos a modo de tutorial por pasos para que el lector vaya entendiendo poco a poco cómo se va programando todo el sistema, pero no se trata de copiar y pegar de seguido todo tal cual, y según tu comentario creo que es lo que has hecho…

      Este sistema de patrones de movimiento consta de 2 scripts: “ComportamientoPatron” y “PatronAccion”, y cada uno de ellos tiene su método Update(), dando igual si dicho método se declara al principio o al final del script.

      Espero poder subir pronto el código completo y funcionando en un ejemplo. Avisaré por aquí! 😉

      Gracias.

      • Holaa! Igual quisiera tener el código Completo estuve intentando comprenderlo por partes pero me fue difícil, esperoo tu aporte

  15. Hola Santi.
    Primero darte las gracias por tu gran aporte, no hay mucha gente que haga cosas así, eso dice mucho bro. Espero que me puedas ayudar con un problema que tengo, te explico, estoy realizando un juego tipo gta pero solo con carros, y bueno use tus códigos para dar movimiento a otros autos, todo funciona bien, sin embargo al momento de que estos carros se orientan para ir al otro hito antes de ir al siguiente hito estos se elevan 180° en el eje Y y continúan yendo al siguiente hito. La verdad estuve leyendo todos los comentarios y ninguno me ayuda 🙁 tienes alguna idea de poder corregir eso, te lo agradecería mucho.
    Saludos.

    • Hola Deere, gracias por tu comentario. No sé cómo tendrás implementada la parte que orienta en cada momento el objeto de tu coche hacia el siguiente hito, pero a priori lo que yo haría sería implementar el giro del objeto en el script de comportamiento (en este ejemplo sería “PatronAccion.cs”) asociado en cada uno de los hitos. De ese modo, cuando tu coche llegue a cada hito, deberías hacer que se gire en dirección al siguiente hito. Habría varias técnicas de realizar dicho giro, pero así de primeras y de manera simple se me ocurre que podrías calcular el vector dirección entre tu coche y el siguiente hito:

      var nuevaDireccion = (siguienteHito.transform.position – tuCoche.transform.position).normalized;

      Y por último, al vector forward de tu coche, asignarle la nueva dirección:

      tuCoche.transform.forward = nuevaDireccion;

      Y de este modo tu coche apuntará a esa nueva dirección.
      Esta sería una de las muchas soluciones posibles, queda a tu elección cómo realizar dicho giro.

      NOTA: Recuerda que en tu caso, la variable “Terrestre” del script “ComportamientoPatron.cs” debes tenerla activada para que tu coche vaya siempre por el suelo.

      Saludos!

  16. Hola. Muchas gracias por el tutorial. Es una grandísima ayuda.

    Una pregunta si me podéis ayudar. Tengo un pájaro en 2D que va de un punto a otro. Al llegar al Hito 2 intento que esté gire 180 grados para que miré hacia la izquierda y no vaya marcha atrás. La función que he utilizado me funciona en el Player al pulsar la tecla izquierda pero en este Script no consigo hacer que gire al llegar al Hito. ¿Podéis decirme a que se debe?

    Muchas gracias.

    NOTA: Os dejo el Script, el cual no tiene errores. También he asignado el pájaro a ObjetoActivador.
    ————————————————————————-
    using UnityEngine;
    using System.Collections;

    public class Hito2 : MonoBehaviour {

    // Public variables
    public GameObject ObjetoActivador;
    public float TiempoAntesDeAccion;
    public float TiempoDespuesDeAccion;

    // Private variables
    private float timeToStart;
    private float timeToEnd;
    private bool workDone;

    // Use this for initialization
    void Start () {
    // Justo al inicio deshabilitamos el script (será activado por el script
    // ‘ComportamientoPatron.cs’ cuando el objeto activador llegue al hito)
    this.enabled = false;

    timeToStart = 0;
    timeToEnd = 0;
    workDone = false;
    }

    // Update is called once per frame
    void Update () {
    if (!workDone)
    {
    timeToStart += Time.deltaTime;

    // No realizamos la acción hasta que no pase el tiempo ‘TiempoAntesDeAccion’
    if (timeToStart >= TiempoAntesDeAccion)
    {
    // IMPLEMENTACIÓN DE LA ACCIÓN…
    // …
    //Giramos al personaje
    ObjetoActivador.transform.eulerAngles = new Vector3(0,180,0);

    workDone = true;
    }
    }
    else
    {
    timeToEnd += Time.deltaTime;

    //No avanzamos al siguiente hito hasta que no pase el tiempo ‘TiempoDespuesDeAccion’
    if (timeToEnd >= TiempoDespuesDeAccion)
    {
    // Se inicializa el script
    Start();

    // Indicamos que se puede pasar el siguiente hito
    ObjetoActivador.GetComponentInParent().CanGoToNextMilestone = true;

    }
    }
    }
    }

    • Hola Pozo, gracias por tu comentario!

      A ver, según me comentas, para hacer que tu pájaro mire en dirección contraria, lo que haces es girar todo su objeto 180 grados en el eje Y. Normalmente, con objetos 2D lo que se suele hacer en esta situación es multiplicar directamente por -1 la escala (del eje X) del objeto en sí, de este modo conseguimos invertirlo y conseguir lo que queremos.

      Prueba a cambiar esto:
      ObjetoActivador.transform.eulerAngles = new Vector3(0,180,0);

      Por esto:
      ObjetoActivador.transform.localScale = new Vector3(
      ObjetoActivador.transform.localScale.x * -1,
      ObjetoActivador.transform.localScale.y,
      ObjetoActivador.transform.localScale.z)

      • Muchas gracias Santi por contestar.

        He probado a cambiar lo que me habías comentado y todo sigue igual. No se que puede estar ocurriendo. Lo extraño es que al Sprite de Player me funciona sin problemas pero en el Hito no tira la función y tampoco la que me has comentado tú de poner.

        Muchas gracias de nuevo por tu interés.

        • Es muy extraño… oye y has probado, en vez de girar el objeto del pájaro, acceder a su SpriteRenderer y activar/desactivar su propiedad “flipX”? Esa propiedad lo que hace es invertir el sprite en sí en el eje X, sin embargo el objeto padre seguiría igual.

          Sería algo así como esto:
          ObjetoActivador.GetComponent SpriteRenderer ().flipX = true;

  17. Buenas tardes Santi, espero que estes bien, quisiera preguntarte sobre como puedo implementar un algoritmo donde dependiendo el tiempo o la fuerza, con la que oprima una tecla (Input.GetAxis()), el valor sea alto o bajo, es decir si oprimo la tecla a penas por encima que el valor sea menor, asi la preciono mas fuerte, te Agradeceria si me escribes a cerca del asunto, Gracias

    • Hola aldair, ¿te refieres a un botón de acción (saltar, disparar, etc.) o a un joystick de dirección? Te lo digo porque el primero es digital (toma los valores 0 o 1) y el segundo es analógico (toma valores entre 0 y 1).

      Si lo que quieres es comprobar el valor de un eje analógico es tan fácil como obter su valor mediante Input.GetAxis() y cuanto más cerca esté el valor de 1, más estás “empujando” dicho joystick.

      Sin embargo si lo que quieres es comprobar la fuerza con la que se pulsa un botón digital, simplemente Unity no te deja porque se trata de valores digitales (o está pulsado o no lo está), pero lo que sí puedes hacer es comprobar cuánto tiempo llevas pulsando dicho botón. Para ello lo único que tienes que hacer es, cada vez que pulsas el botón, mediante Input.GetButtonDown(), poner un contador de tiempo en marcha sobre una variable que será la que te indique el tiempo que llevas pulsando dicho botón, y luego comprobar, mediante Input.GetButtonUp(), el valor de dicha variable de tiempo. Algo así como esto:

      private float timePressed;

      void Update()
      {
      if (Input.GetButtonDown(“Fire1”))
      {
      timePressed += Time.deltaTime;
      }

      if (Input.GetButtonUp(“Fire1”))
      {
      if (timePressed >= 3f)
      {
      // Implementar ataque fuerte…
      }
      else
      {
      // Implementar ataque flojo…
      }

      timePressed = 0f;
      }
      }

      • Gracias tus afirmaciones las comprobe y funcionaron, pero me refiero, por ejemplo establecer un salto para un personaje, salto bajo, y salto alto, como podria validar cuando entra uno, u otro al oprimir la tecla respectiva, que si apenas yo toco la tecla el salto sea bajo y si la oprimo por mas tiempo o mas fuerza el salto sea alto, cualquier informacion te agradeceria me la compartieras ¡Gracias maestro¡

        • Hoal aldair, es justo lo que te comenté en mi comentario anterior: en función de la variable de tiempo que calculas al pulsar el botón, tú ya realizas el salto de una maner u otra. Por ejemplo puedes hacer que la fuerza que usas para realizar tu salto sea proporcional al valor de la variable de tiempo calculada durante la pulsación del botón. Habría muchas formas de hacerlo, pero ya depende de cómo tengas implementada tu solución…

  18. Gracias por el tutorial, ha sido de mucha ayuda, soy nueva completamente en unity, yo lo que necesito es tener varios objectos enemigos moviendose por caminos definidos, así como planteas, pero que no choquen entre sí… Alguna idea para evitar que choquen?

    Gracias!!!

    • Hola lil, gracias por tu comentario. Hay una manera muy fácil que Unity te ofrece para evitar que tus enemigos choquen entre sí y es haciendo uso de layers:

      1. Crea un layer, por ejemplo llamado “Enemigos”, y asígnaselo a todos tus enemigos.
      2. Vete a Edit -> Project Settings -> Physics y configura el “Layer Collision Matrix” para que el layer “Enemigos” no colisione entre sí.

      Es justo lo que se comenta en este link del manual de Unity: https://docs.unity3d.com/es/current/Manual/LayerBasedCollision.html

      Espero que te sirva de ayuda, saludos!

  19. Hola!
    ¿Sabes porque me puede dar este error?
    error CS1525: Unexpected symbol bool', expecting class’, delegate', enum’, interface', partial’, or `struct’

    Es en la línea 22, donde escribimos:
    private bool IrHaciaHito(Vector3 PosicionHito, float Velocidad)
    {

    No se porqué no reconoce el bool.

    Un saludo. Gracias.

    • Hola Daniel, en principio no debería dar ningún error de compilación. ¿Puedes decirme qué tienes escrito justo antes de private “bool IrHaciaHito(…)”? A ver si el error está en la definición de algo que tengas delante.

      • Hola. Delante tengo puesto lo que describes en la primera ventana:

        using UnityEngine;
        using System.Collections;
        using System;

        public class ComportamientoPatron : MonoBehaviour
        {
        // Public variables
        public bool Terrestre = true;
        public GameObject[] HitosPatronMovimiento;
        public float[] VelocidadesPatronMovimiento;

        // Private variables
        private Transform thisTransform;
        private Rigidbody thisRigidbody;
        private int HitoSiguiente = 0;

        // Properties
        // Nos indicará si el objeto puede continuar hacia el siguiente hito del patrón definido
        public bool CanGoToNextMilestone { get; set; }
        }

        • Claro, eso es la definición de la clase ComprotamientoPatron y la función de IrHaciaHito(…) debe ir DENTRO de esa clase. Yo creo que lo que te está pasando es que has puesto la función fuera de la clase, de este modo:

          public class ComportamientoPatron : MonoBehaviour
          {

          }

          private bool IrHaciaHito(Vector3 PosicionHito, float Velocidad)
          {

          }

          Y eso es erróneo porque no puedes declarar una función fuera de una clase. Lo correcto sería así:

          public class ComportamientoPatron : MonoBehaviour
          {

          private bool IrHaciaHito(Vector3 PosicionHito, float Velocidad)
          {

          }

          }

          El hecho de que lo haya ido poniendo así en el tutorial es para describir cómo ir haciéndolo paso a paso, pero se sobreentiende que las funciones van declaradas dentro de su correspondiente clase.

          Saludos!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *