Godot 4. Добавляем меню.

Предисловие.

Этот урок является продолжением предыдущего, в нем мы подробнее познакомимся с узлами и сигналами, а также, разберем создание меню.

Про наследование.

Наследование в программировании, почти ничем не отличается от наследования в реальной жизни.

  • Прадед купил квартиру и завещал её деду
  • Дед имея квартиру, решил обзавестись машиной, и по наследству все передал отцу
  • Отец имея квартиру и машину, решил обзавестись дачей, которую, по наследству передаст сыну.
  • Сын владеет квартирой, машиной и дачей переданных по наследству.

В программировании это выглядит так:

extends — переводится как, наследует (англ).

  • Объект Praded имеет переменную kvartira
  • Ded extends Praded имеет переменные mashina и kvartira
  • Otec extends Ded имеет переменные dacha, mashina, kvartira
  • Sin extends Otec

Тот же принцип используется у всех узлов Godot.

Про наследование узлов Godot

При создании узла, видно дерево узлов.

По нему легко определить, что от чего наследуется. Например:

  • Node2D — наследуется от CanvasItem
  • CanvasItem — наследуется от Node

Т.е. Node2D владеет всеми переменными и функциями из CanvasItem и Node, а также, своими собственными.

Это можно увидеть в инспекторе, нажав на узел типа Node2D.

Сигналы, также, наследуются:

А еще, это видно в коде:

extends Node2D

Получаем узел через код

Для получения узла используется функция get_node

где path — относительный путь до ноды (узла)

Например, чтобы получить путь к Player из ноды World

Нужно использовать.

get_node("Player")

Для получения CanvasLayer->ColorRect

Для получения родителя, т.е. той ноды в которой находится текущая нода, используется get_parent

Либо .. в пути get_node

Ну и в качестве примера, допустим, к узлу ColorRect привязан скрипт, в которой нужно получить доступ к узлу Player, для этого пишем следующее.

Также, get_node можно заменить на $, и получать узел так:

что куда проще.

Меняем параметры узла через код

Меняя параметры в инспекторе, по факту, мы меняем значение переменной данного узла. В большинстве случаев, название параметра в инспекторе = название переменной в коде, но если это не так. То, вводите тип узла в коде.

С зажатым Ctrl, нажимаем данный текст

В итоге, вы получите справку по данному типу узла.

Доступ к переменным узла, осуществляется через точку.

$Player.position.x = 0

Функции которые вызывает движок Godot.

Godot сам вызывает функцию _ready(), один раз, при создании сцены.

pass — ничего не делает, поставил что-бы компилятор не ругался на пустой блок.

func _ready():
  pass

Функцию _process(delta), которая вызывается каждый кадр, частота вызова менее стабильна чем у _physiscs_process (delta больше отклоняется)

func _process(delta):
  pass

Функцию _physics_process(delta), которая вызывается в равные промежутки времени, т.е. delta почти не меняется, что позволяет ей надежно просчитывать физику.

func _physics_process(delta):
  pass

Это основные, есть еще _draw, _input, но без них можно жить.

Ок, вернемся к практике, отобразив количество собранных предметов.

Добавляем сигнал предмету

Перепишите код предмета на следующее:

extends Area2D

signal got_item #Создал сигнал, который позже, можно будет отправить слушателям

func _on_body_entered(body):
  emit_signal("got_item") #Отправляем сигнал got_item всем слушателям.
  queue_free()

Код я объяснил в комментариях. Ок, осталось связать этот сигнал со слушателями.

Откройте сцену world, и реорганизуйте дерево узлов, что-бы все предметы находились внутри узла Items, типа Node2D.

Сделали это для удобства, позже поймете.

Теперь, откройте код сцены world, и замените на следующее:

extends Node2D

func _ready():
  #В момент загрузки сцены выполняем init_items
  init_items() 

func _on_area_2d_4_body_entered(body):
  get_tree().change_scene_to_file("res://world2.tscn")

func init_items():
  #Получили массив детей предмета
  var item_arr = $Items.get_children()
  #Проходимся по всем предметам массива детей
  for item in item_arr:
    #Соединяем сигнал got_item из предмета, с функцией _got_item из уровня
    item.connect("got_item", _got_item)
		
func _got_item():
  GlobalVals.items_num += 1
  #Меняем текст ноды Label на количество предметов, преобразовав в строку str()
  $CanvasLayer2/Label.text = str(GlobalVals.items_num)

Дети, в данном случае, это те кто находятся под кем-то.

В данном случае, у Items всего 1 ребенок, это Area2D.

Далее, связали (connect) сигнал got_item с функцией _got_item. Т.е. функция вызовется когда предмет выпустит сигнал (emit_signal) got_item.

В функции got_item, мы увеличили счетчик и изменили текст Label, на значение счетчика. Но т.к. счетчик это число, то его нужно преобразовать в строку, вот так.

str(GlobalVals.items_num)

В итоге, все предметы будут вызывать одну и ту-же функцию _got_item, при столкновении с игроком.

То же самое сделайте для 2-го уровня.

В итоге, при столкновении предмета с игроком, счетчик в углу экрана обновился.

Ок, давайте создадим графический интерфейс т.е. GUI.

Создание главного меню.

Создайте новую сцену, нажав + около вкладок, в качестве корневого узла, выберите зеленый.

У зеленых узлов есть общие черты:

  • Они подгоняют позицию детей под размеры контейнера — например, Control будет центрировать вложенный спрайт независимо от размеров экрана (если вы это укажете).
  • Связаны с GUI

У зеленых узлов, позиция и их относительный размер задается якорями. Для простоты, будем пользоваться пресетами якорей.

Ок, у узла Control, измените размер контейнера на весь экран, взяв в качестве пресета якорей — полный прямоугольник.

Границы контейнера помечаются оранжевым прямоугольником.

Теперь, вложите в него узел Label, указав ему любой текст. В качестве пресета якорей, используйте центр.

Теперь, проиграйте текущую сцену нажав на кнопку с изображения. Вам предложат сохранить сцену, назовите её MainMenu

Попробуйте поменять размер окна, независимо от него, надпись останется по центру.

Ок, создайте узел VBoxContainer, туда вложите Label и Button

  • VBoxContainer — группирует вложенные в него ноды, друг за дружкой, по вертикали
  • HBoxContainer — то же самое, только по горизонтали
  • Button кнопка, которая при нажатии, испускает сигнал.

Для VBoxContainer выбрал пресет на заполнение по ширине и по центру.

Для Label, выбрал прижать к центру по горизонтали.

У Button, выбрал заполнение по горизонтали.

Далее, пропишем код меню.

Прописываем код меню.

Привяжите скрипт к корневому узлу (Control), у узла Button, соедините сигнал button_down, с корневым узлом.

button_down — сигнал, который срабатывает в момент нажатия на кнопку.

В скрипте, смените текущую сцену на сцену 1-го уровня, должно получится следующее.

extends Control

func _on_button_button_down():
  get_tree().change_scene_to_file("res://world.tscn")

Переход готов, осталось заменить начальную сцену на сцену меню. Для этого, задите в Проект->Настройки проекта, там выберите Запустить->Главная сцена, и замените сцену на MainMenu.

Готово.

Итоговый результат.

Пожалуйста отключи блокировщик рекламы, или внеси сайт в белый список!

Please disable your adblocker or whitelist this site!