1.11 Aprende el desarrollo de juegos de Unity desde 0: mueve tu cámara

El artículo anterior presentó cómo recibir la entrada del usuario en Unity. Debido a la necesidad de ser compatible con las diferencias de varios dispositivos, Unity ha creado un conjunto de paquetes. Puede ser difícil de usar al principio. En este artículo, usaremos directamente. Usaremos wasd Para mover la línea de visión en nuestro juego, es un poco similar al efecto de operación en el juego FPS. Al mismo tiempo, también aprenderemos cómo dejar que nuestro código controle los objetos en el escena, no solo los objetos donde se ubican los componentes mismos.

Los componentes reciben entrada WASD

Creamos un nuevo archivo de recursos de código llamado CameraController:

using UnityEngine;

public class CameraController : MonoBehaviour
{
    void Update()
    {
        if (Input.GetKey(KeyCode.W))
        {
            // Move camera forward
        }
        if (Input.GetKey(KeyCode.A))
        {
            // Move camera left
        }
        if (Input.GetKey(KeyCode.S))
        {
            // Move camera backward
        }
        if (Input.GetKey(KeyCode.D))
        {
            // Move camera right
        }
    }
}

Puede ver que usamos la clase Input mencionada en el artículo anterior para obtener qué botón presionó el usuario actual, pero también dijimos, si no estamos usando un teclado, ¿qué pasa si es un mango? Entonces será mejor que usemos la información de Axis para representar información similar a la entrada móvil, y dejemos que GPT simplemente me ayude a cambiar el código:

using UnityEngine;

public class CameraController : MonoBehaviour
{
    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
    }
}

Tanto "Horizontal" como "Vertical" pueden encontrar los nombres de configuración correspondientes en el archivo de configuración de InputManager. Obviamente, estos dos representan el volumen de entrada en las direcciones horizontal y vertical respectivamente, y el rango de valores de float es [-1, 1] .

Pero la gente cuidadosa realmente verá que InputManager no configura qué tecla del teclado para estas dos entradas. ¿Cómo puede ser WASD? Solo se puede decir que es la regla tácita de Unity, que se puede aprender de la documentación. :

Para leer un eje, use Input.GetAxis con uno de los siguientes ejes predeterminados: "Horizontal" y "Vertical" están asignados al joystick, A, W, S, D y las teclas de flecha. "Mouse X" y "Mouse Y" se asignan al delta del mouse. "Fuego1", "Fuego2" y "Fuego3" están asignados a las teclas Ctrl, Alt, Cmd y tres botones de mouse o joystick.

Añadir nuestro componente a la cámara.

Ahora queremos mover la posición de la cámara, así que obviamente necesitamos modificar la información de Posición del componente Transformar de la cámara. Primero agreguemos nuestros propios componentes a la cámara:

Luego, queremos acceder a otros componentes bajo el mismo GameObject en nuestro componente, luego podemos usar el método GetComponent directamente:

using UnityEngine;

public class CameraController : MonoBehaviour
{
    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        Transform transform = GetComponent<Transform>();
    }
}

Luego modifique el valor dentro del componente:

using UnityEngine;

public class CameraController : MonoBehaviour
{
    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        Transform transform = GetComponent<Transform>();
        transform.position += new Vector3(horizontal, vertical, 0);
    }
}

Aquí agrego directa y violentamente una entrada horizontal y vertical a los ejes x e y de las coordenadas, mientras que el eje z permanece sin cambios.

Si usa Rider como el IDE, puede ver que la transformación se dibuja con una línea ondulada.Eso se debe a que todos los GameObjects deben tener un componente de transformación, por lo que podemos acceder a él directamente con la variable miembro de transformación sin usar la función GetComponent.El código quedará así:

using UnityEngine;

public class CameraController : MonoBehaviour
{
    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        transform.position += new Vector3(horizontal, vertical, 0);
    }
}

¡La velocidad de movimiento es demasiado extraña!

ok, entonces guardemos el código y dejemos que Unity lo compile automáticamente, ejecutemos el juego y pruébelo:

Dependiendo de su máquina, puede correr muy lejos cuando presiona un botón, ser muy sensible (si no puede encontrar el cuadrado, puede detener el juego y reiniciar) o moverse muy lentamente (no hay máquinas tan malas) , ¿Entonces, dónde está el problema?

Debido a que moveremos la posición cada vez que se llame a la función Actualizar, y se llamará a Actualizar en cada fotograma.Si la configuración de nuestra máquina es mejor y la velocidad de fotogramas en ejecución es relativamente alta, entonces la cantidad de llamadas de Actualización es muy grande, lo que conducirá a movimiento por segundo Cuanto mayor sea la distancia, podemos hacer clic en el botón Estadísticas en la esquina superior derecha de la ventana del Juego:

Mire el valor de FPS en el lado derecho de Gráficos adentro. En mi máquina GTX1060+i7 8700, a menudo puede ejecutarse a 300 fotogramas, lo que significa que la actualización se llama 300 veces por segundo. Si presionamos el botón un poco más, el la posición de la cámara cambiará. El movimiento es muy lejano. En circunstancias normales, definitivamente esperamos que la velocidad de movimiento se base en tiempo real. El efecto de moverse un metro por segundo, entonces el cálculo del movimiento debe estar relacionado con el juego actual. tiempo, por lo que el código se puede cambiar de la siguiente manera:

using UnityEngine;

public class CameraController : MonoBehaviour
{
    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        transform.position += (new Vector3(horizontal, vertical, 0) * Time.deltaTime);
    }
}

Puedes ver que hemos multiplicado la distancia recorrida por cada fotograma por un Time.deltaTime, que representa cuánto tiempo ha pasado realmente desde el fotograma anterior hasta este fotograma, por ejemplo, si son 300 fotogramas, entonces este tiempo es 1/ 300 segundos, que son 3,33 milisegundos más o menos.

Si cambia a la comprensión conceptual, entonces Time.deltaTime es 1 segundo/velocidad de cuadro. Y cada segundo agregaremos tantos datos de movimiento, es decir * velocidad de fotogramas, luego la distancia de movimiento en un segundo = distancia de movimiento * velocidad de fotogramas * 1 segundo / velocidad de fotogramas, tacha las dos velocidades de fotogramas, y el resultado es solo The distancia de movimiento, que es equivalente a moverse esta distancia por segundo. Ejecutémoslo de nuevo después de cambiarlo, y el efecto será mucho más controlable:

Por supuesto, puedes pensar que esta velocidad es un poco lenta, así que multipliquémosla por un coeficiente y llamémosla Velocidad:

using UnityEngine;

public class CameraController : MonoBehaviour
{
    public float speed = 1.0f;
    
    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        transform.position += (new Vector3(horizontal, vertical, 0) * (Time.deltaTime * speed));
    }
}

¿Por qué agregué una variable miembro aquí? Debido a que no podemos pensar en todas las cosas en el desarrollo de juegos con el pensamiento de los programadores, más a menudo necesitamos pensar en cómo producir contenido rápidamente, por lo que hemos dicho tantos parámetros que se pueden ajustar en tiempo real en el editor. ahora Este parámetro de velocidad es un excelente ejemplo, y nuestra velocidad aquí se mostrará en el panel Inspector:

Si modificamos este valor, se verá reflejado en el juego en ejecución normal en nuestro editor en tiempo real, y no es necesario detener el juego (por supuesto, si cambia el código, debe detener el juego y compilarlo antes). comenzando el juego), puedes probarlo.

De esta forma, podemos intentar de forma rápida e intuitiva encontrar un valor satisfactorio para la velocidad de movimiento, por ejemplo, yo lo ajusto alrededor de 11, que es más acorde con la sensación de mi mano.

Nota : cualquier valor de parámetro ajustado mientras el juego se está ejecutando volverá al valor antes de que se detenga el juego después de que se detenga el juego. En cuanto a cómo guardar estos ajustes mientras el juego se está ejecutando, lo explicaremos en un capítulo posterior.

Observa nuestro comportamiento móvil desde la perspectiva de Escena:

También podemos abrir la ventana Escena cuando el juego se está ejecutando. Podemos mantener presionada la pestaña Juego o Escena y luego arrastrarla para modificar el modo de diseño de la ventana. Mostraremos las ventanas Juego y Escena al mismo tiempo:

Luego, podemos observar la situación en la ventana Escena cuando movemos la cámara mientras se ejecuta el juego. Recuerde hacer clic en la ventana Juego para obtener el enfoque, de lo contrario, el enfoque puede estar en la ventana Escena. El ángulo de visión es más intuitivo. que la ventana del juego para ver el movimiento de la cámara:

Los componentes acceden a otros objetos

A través del código anterior y la modificación, ahora podemos controlar el GameObject donde se encuentra nuestro componente, pero ¿y si queremos controlar otros GameObjects? Un problema más realista es un juego de rol, es más razonable poner el componente móvil en el personaje a controlar, pero en este momento, el componente accede directamente al personaje cuya transformación es el objeto a acceder. acceder a la cámara?

Primero, coloquemos nuestro componente CameraController en nuestro Cubo:

  1. Haga clic en los tres puntos en el lado derecho de nuestro componente CameraController en la cámara principal y seleccione Eliminar componente para eliminar los componentes que agregamos antes, para evitar que dos CameraControllers respondan a la lógica al mismo tiempo.
  2. Agregue el componente CameraController al objeto Cube:

Luego, si ejecutamos el juego directamente, aunque en la ventana del juego parece que la cámara todavía se está moviendo, pero a través de la ventana de la escena, podemos ver que lo que estamos moviendo es en realidad el bloque en sí, no la cámara, solo por la corriente. forma de moverse y la escena Solo hay este bloque dentro, y parece que la cámara se está moviendo.

Luego, para acceder a objetos en otras escenas desde componentes en el objeto Cubo, como acceder a nuestra cámara principal, podemos usar el método provisto por Unity:

using UnityEngine;

public class CameraController : MonoBehaviour
{
    public float speed = 1.0f;
    
    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        GameObject mainCamera = GameObject.Find("Main Camera");
        mainCamera.transform.position += (new Vector3(horizontal, vertical, 0) * (Time.deltaTime * speed));
    }
}

La función de búsqueda de GameObject es una función estática que se puede llamar desde cualquier lugar. El parámetro recibe una cadena, que es el nombre del objeto que se va a encontrar. Para obtener instrucciones específicas de la API, consulte la documentación oficial:

Unidad: API de secuencias de comandos: GameObject.Find https://docs.unity3d.com/ScriptReference/GameObject.Find.html

El código también es muy simple, solo busque el objeto llamado "Cámara principal" en la escena y luego modifique el valor de posición de su transformación.

Después de cambiar el código, podemos ejecutarlo y ver si el efecto está bien.

¿Pero está bien?

El documento oficial en realidad enfatiza que este método Find es relativamente lento y no es adecuado para llamar a todos los cuadros, por lo que podemos modificarlo así:

using UnityEngine;

public class CameraController : MonoBehaviour
{
    public float speed = 1.0f;

    private GameObject mainCamera;

    private void Start()
    {
        mainCamera = GameObject.Find("Main Camera");
    }

    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        mainCamera.transform.position += (new Vector3(horizontal, vertical, 0) * (Time.deltaTime * speed));
    }
}

Es decir, cuando lo buscamos en Start, Start solo lo llamará una vez antes de llamar a Update por primera vez, y luego lo almacenará en la variable miembro, para que Update pueda buscar directamente la variable miembro cada vez, evitando así repetido Encuentre la sobrecarga de rendimiento de cada cuadro.

El problema de rendimiento se ha solucionado, pero todavía tenemos un problema nuevo. Los desarrolladores con cierta experiencia deben ser conscientes de que usamos directamente el nombre del objeto para encontrar aquí. ¿Qué pasa si el nombre cambia algún día? Obviamente, no puede encontrarlo aquí, por lo que se forma una relación de acoplamiento entre la lógica del código y la denominación de recursos en la escena.

Además, si se trata de una colaboración entre varias personas, la edición de la escena a menudo está a cargo de artistas y planificadores. Si codificamos un nombre determinado en el código, o si necesitamos capacitar a todos aquellos que pueden editar la escena para decirles que estos nombres tienen significados, no se pueden cambiar, o la lógica sale mal después de que las personas que no saben la cambien.

Obviamente, estos no son aceptables, debemos evitar usar nombres directamente para referirnos a objetos en la escena, así que sigamos modificando nuestro código.

Vincular otros objetos usando referencias

using UnityEngine;

public class CameraController : MonoBehaviour
{
    public float speed = 1.0f;
    public GameObject mainCamera;

    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        mainCamera.transform.position += (new Vector3(horizontal, vertical, 0) * (Time.deltaTime * speed));
    }
}

Esta vez eliminamos la lógica de Buscar en la función Inicio y cambiamos el miembro de la cámara principal a público. Aprendimos que cuando hablamos sobre la serialización de datos, las variables de los miembros públicos se serializarán de forma predeterminada y se mostrarán en el panel Inspector. Por supuesto, el tipo de GameObject admite la serialización de forma predeterminada:

Puede ver que para el objeto GameObject, lo que aparece aquí es algo así como un cuadro de entrada, pero no podemos ingresarlo manualmente. Haga clic en el pequeño círculo a la derecha para mostrar una nueva ventana:

Puede ver que hay dos pestañas, Activos y Escena. Ahora se muestra debajo de Escena, y se muestran todos los GameObjects debajo de la Escena actual. Podemos hacer doble clic en MainCamera para seleccionarlo, y luego la pantalla en el Inspector se vuelve así:

Puede ver que este cuadro de entrada es bastante especial, solo puede ingresar GameObject, que es un poco como un cuadro de selección desplegable. Ahora ejecutemos el juego nuevamente, y podemos encontrar que podemos usar con éxito los componentes en el cubo para controlar el movimiento de la cámara principal.

Además de hacer clic en el círculo pequeño para asignar el GameObject, también podemos hacer clic y arrastrar directamente el GameObject en la ventana Jerarquía a este cuadro de entrada para la asignación.

Y este proceso de edición es en realidad el método en el que nos referimos a recursos externos a través de las variables miembro de los componentes en circunstancias normales. Además de GameObject, también podemos reemplazarlo con tipos de componentes, e incluso las clases de componentes escritas por nosotros mismos pueden serializarse y Dichas asignaciones se pueden realizar en el panel de visualización.

La ventaja de esto es obvia, nos referimos al objeto en sí, no al nombre, no importa cómo modifiquemos el nombre de la cámara principal, nuestra relación de referencia sigue siendo correcta.

Comprensión profunda de las referencias de GameObject

La mejor manera de entender una función es comprender profundamente su principio. Antes analizamos profundamente cómo se serializan las variables generales y cómo almacenarlas en archivos de escena, por lo que obviamente es lo mismo para GameObject. Abrimos el archivo de escena nuevamente como un archivo de texto y encontramos los componentes debajo de nuestro Cubo:

El método de serialización de este GameObject es un ID de archivo, que obviamente es una referencia, por lo que si buscamos directamente este valor de ID, podemos encontrarlo en la escena:

Puede ver que Unity usa "--- !u!1 &" como prefijo, seguido del valor de fileID para indicar el comienzo de un GameObject en los datos de la escena. Más tarde, puede ver que el m_Name serializado por la clase GameObject es lo que hemos estado usando en dicha cámara principal.

A partir de esto, se puede ver que Unity asigna una identificación única a todos los GameObjects en la escena. Si tenemos variables miembro en cualquier componente que deba referirse a este GameObject, esta identificación única se completa. Cuando el componente se deserializa de nuevo Al crear una clase real, encuentre el GameObject y asígnelo nuevamente a través de esta identificación única.

preguntas de pensamiento

Cuando movemos la cámara arriba, modificamos violentamente directamente las coordenadas X e Y. De hecho, esto es problemático. Cuando nos movemos hacia la izquierda y hacia la derecha, en realidad tiene algo que ver con la orientación de nuestra cámara, pero nuestra cámara predeterminada está mirando hacia el Eje Z. Entonces, las direcciones izquierda y derecha son los ejes positivo y negativo del eje X, por lo que necesitamos usar la dirección izquierda o derecha de la cámara actual para calcular cuánto X y Z deben modificarse, así que cómo hacerlo queda como pregunta para pensar.

Siguiente capítulo

Este capítulo comienza con la necesidad de mover una cámara, explica en detalle cómo implementar una respuesta de entrada y los problemas prácticos encontrados en el proceso de implementación, y presenta cómo hacer referencia a otros componentes de GameObject. A través de esta capacidad de referencia, puede mantener mejor el lógica y evitar agregar una carga mental innecesaria al personal que edita la escena.

Los elementos básicos que componen el juego siguen siendo la presentación en pantalla. En el próximo capítulo, comprenderemos el contenido básico de la representación del juego de la manera más sencilla. Debido a que la parte de representación es muy grande y el umbral de entrada es relativamente alto, solo explicará algunas descripciones de los componentes y el proceso general y la explicación detallada de la representación específica se explicarán en un capítulo especial en el futuro.

Supongo que te gusta

Origin blog.csdn.net/z175269158/article/details/129832622
Recomendado
Clasificación