Creación del juego "Like Coins" en Godot Engine. Parte 2

Espero que estés cansado de esperar la segunda parte del artículo, que trata sobre el desarrollo de juegos usando el "Godot Engine", usando el ejemplo del juego "Like Coins". Se ha preparado una gran cantidad de todo lo “sabroso” y “saludable” en la agenda. Haré una reserva de inmediato para que en este artículo terminemos el juego iniciado anteriormente, cuyo comienzo puedes leer aquí: Creación del juego "Like Coins" en Godot Engine. Parte 1 , pero la serie de artículos continuará, porque había tanto material que me hizo dejar a un lado parte de él, pero definitivamente volveremos a él más tarde. ¡Que comience el "gamedev"!


Escena "Principal"


En la parte anterior del artículo, nos detuvimos en la etapa principal ( Main ), y desde allí, tal vez, continuaremos. Eliminamos todo lo que se agregó anteriormente (si agregó algo, por supuesto, para verificar cómo funciona todo), si no se cargó nada en la escena, entonces deberíamos agregar Node , que será el padre de los nodos enumerados a continuación, que a su vez También se debe agregar a la escena:


ColorRect ("Fondo"): rellena el color de fondo;
Player : el objeto "Player" (espero que no te confundas porque llamo objeto a la escena Player );
Node ("Contenedor") - "contenedor" para el almacenamiento temporal de monedas;
Position2D ("PlayerStart"): al comienzo del juego, establece la posición inicial del objeto "Player";
Timer ("GameTimer") - contador de límite de tiempo;


Seleccione ColorRect y en la barra de herramientas seleccione: Layout -> Full Rect para estirarlo a toda el área de la pantalla (en el futuro a menudo recurriremos a esta función, por lo que le aconsejo que estudie otras operaciones especificadas en la lista Layout ), para este nodo, en la propiedad "Color" especifique el color de relleno deseado. Puede hacer lo mismo con TextureRect , solo que en lugar de rellenar necesitará cargar la imagen a través de la propiedad "Texture". Para Position2D , en la propiedad "position", especifique los valores de "x" e "y"; esto servirá como la posición inicial para el Player . Por supuesto, con la ayuda del script, puede establecer los valores de posicionamiento directamente en el propio Player , pero no solo aprendemos a desarrollar juegos, sino también a estudiar "Godot", por lo que considerar diferentes opciones para resolver un problema no será superfluo.


Guión para "Principal"


Agregue un script para Node e imprima lo siguiente:


 extends Node #PackedScene        export (PackedScene) var Coin export (int) var playtime var level #  var score # var left_time #     var window_size #   var playing = false #    

Las propiedades "Moneda" y "tiempo de juego" se mostrarán en el Inspector . Arrastre la escena "Coin.tscn" a la propiedad "Moneda" y establezca "tiempo de juego" en "40" (la duración del juego en segundos).


Cuando comienza el juego, cada vez debe haber una inicialización: preparación para el trabajo, determinación de los parámetros requeridos para una operación de alta calidad y sin errores de la aplicación. Este es un paso obligatorio, por lo que debe ocuparse de esto primero.


 func _ready(): randomize() #        window_size = get_viewport().get_visible_rect().size #    $Player.window_size = window_size #    "" $Player.hide() #   

Tenga en cuenta que al especificar el nombre del objeto Player , se usa el símbolo "$"; este es "azúcar sintáctico" que le permite acceder directamente al nodo en la escena actual, una buena alternativa al get_node("Node1") (aunque no está prohibido usar este último). Si "Nodo1" tiene un descendiente de "Nodo2", también puede usar este método: $Node1/Node2 . Tenga en cuenta que el relleno automático funciona muy bien en Godot, así que no lo descuide. Usar un espacio en los nombres de los nodos no es deseable, pero sigue siendo válido, en este caso use comillas - $"My best Node1" .


Nuevo juego


Para comenzar un nuevo juego, determinaremos la función correspondiente para esto, que luego podemos llamar, por ejemplo, presionando un botón.


 func new_game(): playing = true #   level = 1 score = 0 time_left = playtime $Player.start($PlayerStart.position) $Player.show() $GameTimer.start() #    spawn_coins() #  

La función "start ()" cuyo argumento es $PlayerStart.position moverá al jugador a la ubicación inicial, y la función "spawn_coins ()" es responsable, como puede suponer, de generar monedas en el campo de juego.


 func spawn_coins(): for i in range(4 + level): var c = Coin.instance() $CoinContainer.add_child(c) c.window_size = window_size c.position = Vector2(rand_range(0, window_size.x), rand_range(0, window_size.y)) 

La función de range(4 + level) devolverá una matriz con un rango dado cuyo valor es igual a la suma de la cantidad de monedas y el valor del nivel actual. Un rango puede contener un argumento, como en nuestro caso, dos argumentos o tres argumentos (el tercer argumento será un paso de matriz). En esta función, creamos varias instancias del objeto "Coin" y agregamos CoinContainer como elementos secundarios (espero que no haya olvidado que ya tenemos acceso al objeto, gracias a PackedScene ). Recuerde que cada vez que crea una instancia de un nuevo nodo (el método instance() ), debe agregarse al árbol mediante add_child() . A continuación, establecemos el área para una posible generación de monedas para que no aparezcan accidentalmente detrás de la pantalla, y luego asignamos una posición al azar. La última línea no parece un poco estéticamente agradable, por lo que sugiero simplificarla recurriendo al Singleton.


Singleton


El segundo nombre de Singleton es "Inicio". Ya sugerente, ¿verdad? Te lo digo, un singleton funciona de la siguiente manera: un script en el que podemos escribir lo que queramos (comenzando por declarar variables y terminando con "interruptores" de escenas, incluyendo cargarlas y descargarlas) se carga primero, con la aplicación ejecutándose, y todos sus contenidos son accesibles desde cualquier puntos del proyecto En cierto modo, este es un tipo de repositorio global personalizado de "cualquier cosa" disponible en un momento dado.


Tenga en cuenta que el proyecto tiene su propio repositorio global, cuyo contenido también podemos usar, y puede acceder a él usando ProjectSettings.get_setting(name) , donde name es el nombre del parámetro requerido.

Ahora, para usar algo del repositorio "_G", es suficiente llamarlo por su nombre y luego especificar el método a llamar, o lo que sea que tengamos allí. Entonces, cree un script vacío y escriba en él la función que se indica a continuación:


 extends Node func rand(): var rrand = Vector2(rand_range(40, 760), rand_range(40, 540)) return rrand #   


A continuación, guárdelo y vaya a la configuración del proyecto: Project -> Project Settings -> AutoLoad . Seleccionamos nuestro script recién creado, le asignamos un nombre, por ejemplo, "_G", y volvemos a la función "spawn_coins ()" para ajustar ligeramente la fecha límite, reemplazándola con el siguiente código:


  ... c.position = _G.rand() 

Ahora vale la pena verificar qué sucedió colocando "spawn_coins ()" en el bloque "_ready ()" y ejecutando la aplicación en F5. Y no olvide seleccionar Main.tscn como la escena principal, si por alguna razón cometió un error al elegir, puede cambiar la escena principal manualmente, para esto debe ir a la configuración del proyecto: General -> Run -> MainScene . Funciona Entonces sigue adelante.


¿Cuántas monedas quedan?


Vamos a continuar A continuación, debes comprobar cuántas monedas quedan para transferir al jugador al siguiente nivel, darle una pequeña "bonificación" en forma de un aumento de tiempo de 5 segundos y luego volver a generar las monedas.


 func _process(delta): #   ?     ? if playing and $CoinContainer.get_child_count() == 0: #    level += 1 # ""   time_left += 5 #  spawn_coins() 

Interfaz de usuario


Toda nuestra interfaz constará de los siguientes elementos: indicador de puntaje, nivel actual, tiempo, nombre del juego y un botón que activará el lanzamiento del juego. Cree una escena ( HUD.tscn ) con un padre CanvasLayer (le permite dibujar una interfaz de usuario en la parte superior del campo de juego). Mirando hacia el futuro, diré que no es muy conveniente administrar los elementos de la interfaz de usuario, al menos para mí, pero una lista bastante amplia de elementos y desarrollo activo infunde un estado de ánimo positivo en el futuro brillante para el desarrollo de este aspecto del motor.



En Godot hay los llamados "nodos de control" que le permiten formatear automáticamente elementos secundarios con respecto a los parámetros especificados del elemento primario. Cada tipo de "nodos de control" tiene propiedades especiales que controlan cómo controlan la ubicación de sus descendientes. Un vívido representante de este tipo es MarginContainer , que debe agregarse a la escena. Con la ayuda de Layout -> Top Wide estiraremos en la parte superior de la ventana, y en las propiedades de este objeto, en la sección Margin , especifique las sangrías de los bordes: izquierdo, superior y derecho. MarginContainer debe tener tres Label secundarias con los siguientes nombres: ScoreLabel , LevelLabel y TimeLabel . Añádelos a la escena. Usando la propiedad Align , haga que se alineen a la izquierda, al centro y a la derecha. Queda por agregar otra Label ( Messagelabel ), colocándola en el centro, todo también con la ayuda de Layout , y un poco más abajo coloque el botón ( StartButton ).



Ahora hagamos que la interfaz sea receptiva, necesitamos actualizar el tiempo, la cantidad de monedas recolectadas y resaltar el nivel actual. Agregue un script para el nodo HUD .


 extends CanvasLayer signal start_game func update_score(value): $MarginContainer/ScoreLabel.text = str(value) func update_level(value): if len(str(value)) == 1: $MarginContainer/TimeLabel.text = "0: 0" + str(value) else: $MarginContainer/TimeLabel.text = "0: " + str(value) func update_timer(value): $MarginContainer/TimeLabel.txt = str(value) 

Para MessageLabel necesitamos un temporizador para cambiar el texto del mensaje por un período corto. Agregue un nodo Timer y reemplace su nombre con MessageTimer . En el inspector, configure el tiempo de espera en 2 segundos y seleccione la casilla de verificación en el campo One Shot . Esto garantiza que el temporizador se ejecute solo una vez al inicio.


 func show_message(text): $MessageLabel.text = text $MessageLabel.show() $MessageTimer.start() 

Conecte la señal de timeout() a "MessageTimer" y agregue lo siguiente:


 func _on_MessageTimer_timeout(): $MessageLabel.hide() 

En la pestaña "Nodo" para StartButton , conecte la señal pressed() . Cuando hace clic en el botón StartButton debería desaparecer junto con MessageLabel , luego enviar una señal a la escena principal, donde posteriormente la interceptaremos con éxito al mismo tiempo deslizando la función para ejecutar - "new_game ()". Implementamos esto usando el siguiente código. No olvides que el botón en la propiedad Text establece cualquier llamada de texto para iniciar el juego.


 func _on_StartButton_pressed(): $StartButton.hide() $MessageLabel.hide() emit_signal("start_game") 

Para terminar finalmente con la interfaz, escribiremos la última función final: la función de mostrar un mensaje sobre el final del juego. En esta función, necesitamos que se muestre la inscripción "Game Over" durante no más de dos segundos, y luego desaparecer, lo cual es posible gracias a la función "show_message ()". Sin embargo, debe volver a mostrar el botón de inicio de un nuevo juego, tan pronto como desaparezca un mensaje que informa que el juego ha terminado. yield() pausará la ejecución de la función hasta que se reciba una señal de MessageTimer , y al recibir una señal de MessageTimer sobre su ejecución, la función continuará ejecutándose, volviendo a su estado original para que podamos comenzar un nuevo juego nuevamente.


 func show_game_over(): show_message("Game Over") yield($MessageTimer, "timeout") $StartButton.show() $MessageLabel.text = "LIKE COINS!" $MessageLabel.show() 

¿Terminando?


Configuremos los comentarios entre el HUD y Main . Agregue la escena HUD a la escena principal y conecte la señal GameTimer través del timeout() en la escena principal agregando lo siguiente:


 func _on_GameTimer_timeout(): time_left -= 1 #  $HUD.update_timer(time_left) #   if time_left <= 0: game_over() #     

Luego conecte las señales pickup() y die() del jugador.


 func _on_Player_pickup(): score += 1 $HUD.update_score(score) func _on_Player_die(): game_over() 

Al final del juego, deben suceder varias cosas más que no deben pasarse por alto. Escribe el siguiente código y te lo explicaré.


 func game_over(): playing = false $GameTimer.stop() for coin in $CoinContainer.get_children(): coin.queue_free() $HUD.show_game_over() $Player.die() 

Esta función detendrá el juego, y luego se volverán a show_game_over() las monedas restantes y se show_game_over() monedas restantes, luego se show_game_over() para el HUD . El siguiente paso es iniciar la animación y detener el proceso de ejecución del nodo Player .


Finalmente, debe activar StartButton , que debe estar conectado a la función new_game() . Haga clic en el nodo HUD y en el cuadro de diálogo de conexión debe hacer clic en Make Function to Off esté Make Function to Off (esto evitará la creación de una nueva función) y en el campo Method In Node , especifique el nombre de la función conectada: new_game . Esto conectará la señal a una función existente, en lugar de crear una nueva.


El toque final es eliminar new_game() de la función _ready() y agregar las siguientes dos líneas a la función new_game() :


 ... $HUD.update_score(score) $HUD.update_timer(time_left) 

Ahora podemos decir con confianza que el juego está listo, ahora es bastante "jugable", pero sin efectos. Consideraremos esto último en el próximo artículo, dedicando una enorme atención a varias "decoraciones" para diversificar el juego y explorar aún más las posibilidades de "Godot". Por lo tanto, no olvide seguir el lanzamiento de los artículos. Buena suerte

Source: https://habr.com/ru/post/es430800/


All Articles