Tabla de contenidos

Introducción

Ha sido mi percepción persistente de que el diseño de videojuegos es en un cruce de arte única y técnico. Si uno se las arregla para engranar código impecable con un arte sublime, los resultados pueden ser bastante novedoso – y la naturaleza fluida de este cruce crea posibilidades ilimitadas. Uno nunca está seguro de que cuando se emplea una habilidad o si está simplemente encapsulado y deriva de la otra. Es precisamente esta unión que hace juego diseñar un mundo tan fascinante para explorar. ¿Qué nueva combinación de arte y la tecnicidad ¿veremos hoy?

En este tutorial, será la construcción en el proyecto anterior que hicimos en la parte 1 utilizando la vista ortográfica y componentes de navegación de la Unidad. Para la Parte 1, el lado artístico de nuestro juego fue arruinado por nuestra mala manipulación técnica de interpolación. No pudimos sin problemas la transición entre las plataformas de oclusión y, como tal, la ilusión se vino abajo. Estaremos arreglando que en este tutorial.

Además, vamos a hacer desencadenantes y plataformas móviles. Simplemente no podía ser llamada una “ilusión juego de puzzle” si no tenemos este componente. El resultado final no es sólo un proyecto de funcionar, sino un sistema en el que podemos utilizar para construir un juego completo.

Proyecto Archivos

Puede descargar el proyecto completo de este tutorial a través de este enlace:. Código fuente
No se pierda! extremos oferta en

  • Accede a los más de 200 cursos
  • Nuevos cursos añadió mensual
  • Cancelar en cualquier momento
  • Los certificados de terminación

    ACCESO ahora

    Fijación de interpolación

    inicio de dejar fuera de este tutorial, examinando primero la forma en que estamos interpolación entre plataformas.


    Probablemente, usted ya sabe cuál es el problema. Este método de interpolación es demasiado repentina. movimiento instantáneo no se va a mantener la ilusión. En cambio, necesitamos una manera de llegar a algún tipo de movimiento a través de los enlaces fuera de la malla en lugar de “teletransportación” recta. Sin embargo, ya que estamos utilizando herramientas de navegación nativos de la unidad, no vamos a conseguir que el 100% perfecto, pero podemos conseguir que el 99% de precisión.

    La Función Lerp

    Es en este punto nos damos cuenta de que necesitamos algo que se llama “interpolación lineal.” En matemáticas, interpolación lineal se utiliza para cosas aproximadas como curvas mediante la adopción de dos puntos y conectándolos con una línea recta (de ahí el término “lineal”). En la Unidad, que es un poco más específico. Podemos utilizar una función de línea de interpolación (llamado “Lerp funciones” para abreviar) para mover un objeto en algún lugar entre dos puntos. Hacer esto de forma recursiva significa que podemos interpolar un objeto entre dos puntos de una manera suave. En nuestro código, vamos a utilizar la función de “Vector3.Lerp” para lograr esto. Cesta de la secuencia de comandos “PlayerController” y vamos a crear un nuevo método llamado “CompleteLink” donde vamos a poner todo nuestro código para nuestro algoritmo de interpolación.
    utilizando System.Collections; usando System.Collections.Generic; usando UnityEngine.AI; usando UnityEngine; [RequireComponent (typeof (NavMeshAgent))] public class PlayerController: {MonoBehaviour agente NavMeshAgent privado; privada RaycastHit clickInfo = new RaycastHit (); // Start se llama antes de la primera actualización del marco vacío Inicio () {= agente getComponent (); } // actualización se llama una vez por trama de actualización void () {if (Input.GetMouseButtonDown (0)) {var ray = Camera.main.ScreenPointToRay (Input.mousePosition); si (Physics.Raycast (ray.origin, ray.direction, fuera clickInfo)) agent.destination = clickInfo.point; } If (agent.isOnOffMeshLink) {agent.CompleteOffMeshLink (); }} void CompleteLink () {}} 1234567891011121314151617181920212223242526272829303132333435363738usingSystem.Collections; usingSystem.Collections.Generic; usingUnityEngine.AI; usingUnityEngine; [RequireComponent (typeof (NavMeshAgent))] publicclassPlayerController: agente MonoBehaviour {privateNavMeshAgent; privateRaycastHit clickInfo = newRaycastHit (); // inicio se llama antes de que el primer cuadro updatevoidStart () {= agente getComponent ();} // actualización se llama una vez por framevoidUpdate () {if (Input.GetMouseButtonDown (0)) {VARRAY = Camera.main.ScreenPointToRay ( Input.mousePosition), si (Physics.Raycast (ray.origin, ray.direction, outclickInfo)) agent.destination = clickInfo.point;} if (agent.isOnOffMeshLink) {agent.CompleteOffMeshLink ();}} voidCompleteLink () { }}

    Vamos a echar un vistazo más de cerca a este método “CompleteLink”.
    CompleteLink vacío () {} 1234voidCompleteLink () {}

    Lo primero que vamos a querer hacer es asignar un par de variables. Vamos a necesitar las posiciones de inicio y fin de la relación fuera de la malla, que vamos a tener que compensar esta ligeramente para coincidir con el tamaño de nuestro carácter, y luego vamos a necesitar la distancia entre esos dos puntos . Esto se resuelve en nuestro código como el siguiente:
    void CompleteLink () {Vector3 StartLink = agent.currentOffMeshLinkData.startPos; Vector3 ENDLINK = agent.currentOffMeshLinkData.endPos; flotar linkDistance = Vector3.Distance (StartLink, ENDLINK); endLink.y = agent.currentOffMeshLinkData.endPos.y + 1; } 1234567voidCompleteLink () {Vector3 StartLink = agent.currentOffMeshLinkData.startPos; Vector3 ENDLINK = agent.currentOffMeshLinkData.endPos; floatlinkDistance = Vector3.Distance (StartLink, ENDLINK); endLink.y = agent.currentOffMeshLinkData.endPos.y + 1;} < p> mediante el uso de algunas variables en el agente de nav-malla, estamos en condiciones de captura y compensar las posiciones de inicio y fin. Estamos compensando el eje y en “ENDLINK” porque no queremos que el carácter interpolación a una posición exactamente en la parte superior de la plataforma, que debe ser compensado por encima de ella.

    Ahora, tenemos que ir hasta la parte superior de nuestro script y declarar algunas variables allí también. Vamos a necesidad de dos variables float pública denominados “interpolantValue” y “disconnectMargin.” En segundo lugar, vamos a necesitar un Vector3 privado llamado “storedTarget.”
    usando System.Collections; usando System.Collections.Generic; usando UnityEngine.AI; usando UnityEngine; [RequireComponent (typeof (NavMeshAgent))] public class PlayerController: MonoBehaviour {float público interpolantValue = 100; flotador disconnectMargin pública = 1.5f; storedTarget Vector3 privado; 123456789101112usingSystem.Collections; usingSystem.Collections.Generic; usingUnityEngine.AI; usingUnityEngine; [RequireComponent (typeof (NavMeshAgent))] publicclassPlayerController: MonoBehaviour {publicfloatinterpolantValue = 100; publicfloatdisconnectMargin = 1.5f; privateVector3 storedTarget;

    Go por delante y dar a estas variables el valor por defecto se muestra. He experimentado con esto un poco y encontré estos valores a ser la mejor combinación.

    A continuación, vamos a querer asignar la variable “storedTarget” inmediatamente en la función de actualización.
    // actualización se llama una vez por cuadro de actualización de vacío () {storedTarget = agent.pathEndPosition; 1234 // actualización se llama una vez por framevoidUpdate () {storedTarget = agent.pathEndPosition;

    Esto actualizará constantemente nuestro Vector3 con lo que la posición de la el jugador ha hecho clic en. Ahora, en el método “CompleteLink”, vamos a hacer realidad la función Lerp ahora. La forma en que funciona es la siguiente:
    void CompleteLink () {Vector3 StartLink = agent.currentOffMeshLinkData.startPos; Vector3 ENDLINK = agent.currentOffMeshLinkData.endPos; flotar linkDistance = Vector3.Distance (StartLink, ENDLINK); endLink.y = agent.currentOffMeshLinkData.endPos.y + 1; transform.position = Vector3.Lerp (transform.position, ENDLINK, linkDistance / interpolantValue); // El formato es «startPosición», «endPosition», porcentaje entre ellos} 123456789voidCompleteLink () {Vector3 StartLink = agent.currentOffMeshLinkData.startPos; Vector3 ENDLINK = agent.currentOffMeshLinkData.endPos; floatlinkDistance = Vector3.Distance (StartLink, ENDLINK); endLink.y = agent.currentOffMeshLinkData.endPos.y + 1; transform.position = Vector3.Lerp (transform.position, ENDLINK, linkDistance / interpolantValue); // El formato es «startPosición», «endPosition», porcentaje entre ellos}

    Estamos estableciendo la posición de nuestro jugador igual a un Vector3 que está siendo generada entre dos puntos ( “transform.position” y “ENDLINK”). Por el último argumento, se indica a la función en la que nos gustaría esta posición para estar entre esos dos puntos. Si ponemos un simple 1 como último argumento, tendríamos un resultado similar a lo que tenemos ahora. Nuestro jugador acaba de teletransportarse instantáneamente a través. Si lo ponemos a 0,5, queremos mover el carácter a medio camino entre esos dos puntos, a continuación, a medio camino entre la distancia restante, a continuación, a medio camino entre esa distancia, etc. La manera que tenemos ahora toma en cuenta la distancia espacial mundial entre los puntos y lo divide por un valor constante (que hemos definido).

    La prueba de dejar que esto salga ahora! Reemplazar el método “CompleteOffMeshLink” con nuestro método “CompleteLink”.
    si (agent.isOnOffMeshLink) {CompleteLink (); } 1234if (agent.isOnOffMeshLink) {CompleteLink ();.}

    Guardar la secuencia de comandos, diríjase a la Unidad, y hit juego


    Como se puede ver, es una enorme mejora en nuestro anterior método de interpolación. Es suave, es a la velocidad correcta, sólo hay un problema, que en realidad nunca llega al final del enlace! Esto es en realidad una excelente ilustración de un acertijo matemático clásico. “Si estás de pie a cierta distancia de una puerta, y reducir a la mitad la distancia entre usted y la puerta cada segundo, ¿cuánto tiempo pasará antes de que llegue a la puerta?” La respuesta es, ¡nunca! Y podemos confirmarlo mediante el registro de la distancia entre el jugador y el enlace final.


    La distancia se hace muy pequeña, pero nunca es igual a cero! Y aquí es donde empleamos nuestra variable “disconnectMargin.” Vamos a crear una sentencia if que simplemente establecer la transformada de que el jugador igual a la posición de enlace final, cuando la distancia entre ellos se hace menor que “disconnectMargin.”
    void CompleteLink () {Vector3 StartLink = agent.currentOffMeshLinkData.startPos; Vector3 ENDLINK = agent.currentOffMeshLinkData.endPos; flotar linkDistance = Vector3.Distance (StartLink, ENDLINK); endLink.y = agent.currentOffMeshLinkData.endPos.y + 1; transform.position = Vector3.Lerp (transform.position, ENDLINK, linkDistance / interpolantValue); si (Vector3.Distance (transform.position, ENDLINK) Estamos utilizando una función del agente de navegación de malla llamada “Warp”, que simplemente “urdimbres” el jugador a la posición. Y entonces estamos fijando el objetivo igual a la variable almacenada previamente. Guardar la secuencia de comandos y exitosa obra en la Unidad.


    No somos. Por último, una forma de interpolación que mantiene la ilusión. Fantástico!

    Plataformas animados

    En este punto, tenemos un buen mecánico. Pero como es, todo lo que tenemos es algo que dice: “Oye, mira lo que podemos hacer.” Necesitamos una más mecánico con el fin de estar en condiciones de diseñar realmente niveles jugables. Aquí es donde introducimos “Plataformas de animación.” No sólo necesitamos esta mecánica, pero lo necesitamos para ser generalizado para que podamos utilizarlo en múltiples escenarios.

    Creación de la animación

    Es preferible utilizar animaciones, ya que permite un mayor control sobre cómo y dónde se mueven las plataformas. En este caso, vamos a necesitar dos animaciones, uno en una posición “OnTop” y uno donde se va hacia un lado (la posición “inactiva”) (que la posición “activa” que llamaremos). Crear una nueva carpeta llamada “Animaciones”.


    Seleccione la plataforma superior y abrir la pestaña de animación.


    Crea una nueva secuencia de animación para la posición “activa”. Es una idea buena, si se va a construir más en este nivel, para desarrollar algún tipo de nomenclatura para mantener las cosas organizadas. Dado que esta es la única plataforma de animación en mi escena, sólo voy a nombre de “OccludingPlatformActive.”


    Esto sólo necesita ser una trama de largo con la rotación actual y la posición como los únicos fotogramas clave. Añadir una posición y una propiedad de rotación que va a crear dos nuevos fotogramas clave con la posición y rotación actual.


    Arrastre estos fotogramas clave de manera que no hay marco entre ellos.



    A continuación, vamos a crear una nueva animación llamada “OccludingPlatformInactive.”



    Crea una propiedad de rotación y posición, golpea el botón de grabación, y la reposición de la plataforma de la siguiente manera:


    Esto creará un fotograma clave que podemos entonces duplicado con Ctrl-C y Ctrl-V (Comando-C / Comando-V para usuarios de Mac) y el lugar en el extremo.


    Al arrastrar estos juntos completa esta animación.


    Ahora, tenemos que ir a la pestaña animador y configurar estos estados.


    Para una mirada más profunda en el animador de la Unidad, echa un vistazo a este tutorial sobre el Dev Academia Juego (Unidad Integral Guía animador). En este caso, no vamos a ir muy a fondo. El “OccludingPlatformActive” debe ser de color naranja. Esto significa que va a jugar tan pronto como se inicia el juego. Crear un nuevo parámetro booleano denominado “Activo”.



    Cree dos transiciones, que va desde “OccludingPlatformActive” a “OccludingPlatformInactive” y uno que va al revés.



    Seleccione la primera transición (la que va desde el activo para animaciones inactivos) y hacer su condición de ser cuando el parámetro “Activo” es igual a falsa. Diable “Tiene tiempo de salida” también.


    necesita esta transición a ser considerablemente más larga. Abrir “Configuración” y cambiar “Duración de la transición” a cerca de un segundo de duración.


    do exactamente el mismo para la otra transición excepto el cambio de la condición de “falso” a “true”.


    Prueba para ver si esto está funcionando al golpear el juego y alternar el booleano “Activa”.


    fantástico! ¡Esta funcionando! Ahora, tenemos que activar este a través de los objetos del juego en la escena.

    Scripting un disparador

    Hay un par de retos cuando se trata de este tipo de animación. La primera está disparando a través de los objetos en el juego. La segunda es asegurarse de que el jugador no se caiga cuando se está moviendo. Superamos el primer problema por el simple uso colisionadores. Podemos resolver el segundo por algunos crianza estratégica.

    Pero primero, vamos a configurar un objeto de activación. Crear un nuevo cubo (llamado “disparador”) y colocarlo sobre la plataforma superior. Esto debe ser emparentada al cubo superior de modo que se mueve allí donde se gira la plataforma.


    El disparador se ve demasiado brillante. Crear un nuevo material para arreglar eso.


    Ahora, crear un nuevo guión C # denominado “Trigger” y se arrastra sobre el objeto del juego de disparo.


    Por dónde empezamos de codificación, sin embargo, comienza en el guión “PlayerController”. Vamos a crear dos nuevos métodos públicos llamados “EnableCleanMove” y “DisableCleanMove.” Tienen el siguiente código en ellos:
    // a ser llamado por el medio ambiente cuando el movimiento se activa EnableCleanMove public void (Transform parentTransform) {transform.SetParent (parentTransform); agent.enabled = false; } // Cuando el movimiento se terminó DisableCleanMove public void () {transform.SetParent (null); agent.enabled = true; } 123456789101112131415 // a ser llamado por el medio ambiente cuando el movimiento es triggeredpublicvoidEnableCleanMove (Transform parentTransform) {transform.SetParent (parentTransform); agent.enabled = false;} // Cuando el movimiento es endedpublicvoidDisableCleanMove () {transform.SetParent (null); agent.enabled = true;}

    Esto, básicamente, los padres del jugador a cualquier objeto del juego es mover (o lo que se suministra a “parentTransform” para ser exactos) y desactiva el agente de navegación de manera que no hay cálculos de ruta tienen lugar durante la transición.

    A continuación, tenemos que llenar el guión “Trigger”. Básicamente, lo que tenemos que hacer esto es poner en funcionamiento la transición cuando el jugador está solapando el gatillo, llamada “EnableCleanMove” en el “PlayerController,” y revertir todo eso cuando la animación está en la transición se haga. Para ello, vamos a acceder necesidad de cinco variables diferentes.
    utilizando System.Collections; usando System.Collections.Generic; usando UnityEngine; public class disparador: MonoBehaviour {target GameObject pública; triggerVariable cadena pública; El jugador PlayerController privado; targetAnimator animador privado; bool privada isPlayerRestored; 123456789101112usingSystem.Collections; usingSystem.Collections.Generic; usingUnityEngine; publicclassTrigger: MonoBehaviour {target publicGameObject; publicstringtriggerVariable; privatePlayerController jugador; privateAnimator targetAnimator; privateboolisPlayerRestored;

    El uso de éstos se pondrán de manifiesto en un momento. En este momento, tenemos que asignar a las variables privadas:
    vacío Inicio () {targetAnimator = target.GetComponent (); El jugador = GameObject.FindGameObjectWithTag ( «Jugador») getComponent (.); si (triggerVariable == null) {Debug.LogWarning (nombre + «no tiene ninguna variable de activación suministrado Nada se activará.»); }} 12345678910voidStart () {targetAnimator = target.GetComponent (); jugador = GameObject.FindGameObjectWithTag ( «Jugador») getComponent (.), Si (triggerVariable == null) {Debug.LogWarning (nombre +» tiene ninguna variable de activación suministrada Nada se activará. «);}}

    la advertencia es para que el diseñador sabe si existe una mala conexión entre el gatillo y la plataforma. Después de esto, vamos a crear dos nuevos métodos en el guión “Trigger” llamada “TriggerAction” y “RestorePlayer.”
    TriggerAction vacío () {} void RestorePlayer () {} 123456789voidTriggerAction () {} voidRestorePlayer () {}

    El código para cada aspecto del método de esta manera:
    void TriggerAction () {bool targetVar = targetAnimator.GetBool (triggerVariable); targetAnimator.SetBool (triggerVariable, targetVar!); player.EnableCleanMove (target.transform); isPlayerRestored = false; RestorePlayer} void () {player.DisableCleanMove (); isPlayerRestored = true; } 1234567891011121314voidTriggerAction () {booltargetVar = targetAnimator.GetBool (triggerVariable); targetAnimator.SetBool (triggerVariable, targetVar!); Player.EnableCleanMove (target.transform); isPlayerRestored = false;} voidRestorePlayer () {player.DisableCleanMove (); isPlayerRestored = verdadera;}

    “TriggerAction” es alternar el animador de parámetros que hemos creado y llamando al método “EnableCleanMove” del jugador, mientras que decirle al jugador a los padres en sí al objeto juego en movimiento. Esto hará que el jugador “pegarse” a la plataforma a medida que avanza. El método “RestorePlayer” básicamente sólo invierte todo “TriggerAction” lo hizo.

    “TriggerAction” es la elección natural cuando se trata de elegir un método para llamar cuando el jugador se solapa con el gatillo. Podemos llamar a este método a través de un “OnTriggerEnter.”
    OnTriggerEnter private void (Colisionador de otra) {if (other.CompareTag ( «Jugador»)) {TriggerAction (); }} 1234567privatevoidOnTriggerEnter (Colisionador de otra) {if (other.CompareTag ( «Jugador»)) {TriggerAction ();}}

    Estamos scripting casi hecho! La última cosa que necesitamos hacer es determinar si la animación se realiza la transición. Para ello, se utiliza una función de actualización especial llamado “LateUpdate.” Esta función es la última función de actualización para ser llamado. Esto es perfecto para la determinación de si el jugador está listo para ser activado. Esto se resuelve en nuestro código como el siguiente:
    LateUpdate private void () {if (targetAnimator.IsInTransition (0)) {debug.log ( «jugador activa el gatillo» + nombre); } Else {if (isPlayerRestored!) {RestorePlayer (); } Else {debug.log ( «Jugador se restaura mediante» + nombre); }}} 1234567891011121314151617181920privatevoidLateUpdate () {if (targetAnimator.IsInTransition (0)) {debug.log ( «jugador activa el gatillo» + nombre);} else {if (isPlayerRestored!) {RestorePlayer ();} else {debug.log ( «El jugador es restaurada por el» nombre +);}}}

    Implementar el sistema de disparo

    Un par de cosas tienen que suceder antes de que el código va a funcionar. La primera es que nuestro jugador tiene que tener una etiqueta de “Jugador”. Tenemos que crear una etiqueta y nuestro jugador con él.




    En segundo lugar, hay que asegurarse de que el jugador tiene un componente de cuerpo rígido. No va a ser detectado por el gatillo si no lo hace. Asegúrese de que “Utilizar la gravedad” está desactivado y “es cinemática” está habilitado.



    En tercer lugar, colisionador del disparador debe tener “disparador” habilitada.


    Y por último, tenemos que llenar el campo de la escritura en el gatillo. Set “Target” igual a la “plataforma superior.”


    Este campo se supone que debe contener cualquier objeto que tiene el componente animador. A continuación, tenemos que suministrar una variable de activación. Este es el nombre del parámetro en el animador. En nuestro caso, es el booleano “Activa”. Escriba el nombre en el campo y que va a terminar nuestro sistema de disparo.


    Hit juego y probarlo!


    Conclusión

    Eso es todo! Ahora tenemos nuestra completa juego completo ilusión con plataformas móviles, disparadores, y más!

    Creo que el resultado final es bastante satisfactorio. Es increíble lo cerca que podemos llegar por el simple uso de herramientas integradas. Por supuesto, esto también se hizo posible con la ayuda no sólo de nuestra vista ortográfica, pero el sistema de navegación componente de la Unidad también. Espero que a través de la parte 1 y parte 2 de esta serie de tutoriales, que haya aprendido no sólo a utilizar el sistema de navegación por unidad, sino también la forma de adaptarlo a situaciones únicas. Por supuesto, los componentes de navegación nunca fueron diseñados para ser utilizados como esta, pero aguantan bastante bien, y estas ideas se pueden ampliar para sus propios proyectos!

    Use estos principios bien a …

    Seguir haciendo grandes juegos!

    Mensajes relacionados

Deja una respuesta

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