Композиция в Godot 4

7 minute read

Перевод статьи “Composition in Godot 4 – Tutorial”

В этом руководстве мы исследуем концепцию композиции в Godot 4. Композиция - это ключевой принцип в разработке игр, который заключается в создании сложных игровых объектов путём объединения простых, многократно используемых компонентов. Понимание композиции значительно помогает в организации кода, способствуя повторному использованию и поддержанию гибкости. Такой подход экономит время и делает проект более поддерживаемым.

Композиция часто противопоставляется наследованию как что-то взаимоисключающее. Но они прекрасно работают совместно и такое смешение позволяет добиться лучших результатов. Подробнее про наследование в Godot можно почитать в документации. А в этой статье рассмотрим принципы композиции в играх на Godot. Затронем ноды, сцены, скрипты и как все эти элементы взаимодействуют для создания сложных модульных игровых объектов.

Понимаем композицию

Композиция - это принцип проектирования, который подразумевает создание сложных объектов путем соединения и комбинации простых, много кратно используемых компонентов. Что дает такой подход:

  • Повторное использование - составляющие компоненты можно использовать в разных частях игры
  • Модульность - компоненты независимы и комбинировать их можно как угодно
  • Гибкость - компоненты могут быть расширены или заменены без влияния на остальную систему
  • Согласованность - изменения в базовом компоненте применяются ко всем объектам, которые его используют, обеспечивая единообразное поведение в игре

Начинаем. Создаем первый уровень

Для этой статьи мы создадим простой 2D проект в котором будет продемонстрировано использование компонентов. Для игры будут использованы ассеты с kenney.nl

  • Создайте новый проект и в нём новую 2D сцену. Назовите её, например, «CompositionTut».
  • Добавьте Camera2D и установите масштаб до 3.
  • Затем добавьте TileMapLayer для создания небольшого уровня.

Создайте уровень по своему усмотрению. Я для примера использовал тайлмап из ассетов Kenney. В этой статье не будет подробно описываться использование TileMaps или TileMapLayer, это тема отдельной статьи. И обратите внимание, что TileMaps уже устарел и сейчас нужно использовать более продвинутую версию TileMapLayer. Что нужно сделать:

  • Вам нужно создать новый TileSet для TileMapLayer
  • В разделе Physics Layer добавьте новый элемент и оставьте значения layer и mask как есть
  • Выберите значение фильтра для текстуры Nearest
  • Перетащите тайлмап из скаченных ассетов в TileSet
  • Выберите тайлы у которых должна быть коллизия(в нашем случае это деревья и пол) и укажите область коллизии соответствующим способом
  • Осталось нарисовать сам уровень используя настроенный TileMap. Весь пол должен состоять из таилмапов с коллизиями, по краям уровня нужно нарисовать деревья с коллизией, чтобы не дать игроку вывалиться за карту

Создание компонентов

Мы создадим два компонента разных типов. Первый - это компонент движения, который будет управлять перемещением объектов. Второй - компонент спрайта, управляющий анимацией спрайтов.

Создаем компонент перемещения

Для этого компонента нужно создать новый скрипт. Правый клик на по нужной папке и выбираем меню Create New > Script. Называем скрипт movement_comp и создаем его

В созданом файле пишем код:

 1extends Node
 2class_name MovementComp
 3
 4@export var character_body : CharacterBody2D # For assigning a Character Body
 5@export_range(-1.0, 1.0, 2.0) var dir := 1.0 # Set the movement direction on start
 6@export_range(500.0, 2000.0, 100.0) var speed := 1000.0 # Set movement speed
 7
 8func _physics_process(delta: float) -> void:
 9	if character_body: # Only move, if a character body is assigned
10		character_body.velocity.x = speed * delta * dir # set the velocity of the character body
11		character_body.move_and_slide() # move and slide the character body
12		
13		if character_body.is_on_wall(): # if character body hits a wall, change direction
14			dir *= -1.0

Создаем компонент спрайта

Создайте новую сцену Sprite2D, назовите её “SpriteComp” и сохраните. Добавьте скрипт, с помощью команды attach new script.

В скрипте укажите код ниже:

 1extends Sprite2D
 2class_name SpriteComp
 3
 4@export var character_body : CharacterBody2D # assign a character body
 5@export var anim_frame_amount := 0 # the amount of frames the animation takes according to the tilemap
 6
 7var count := 0.0 # a counter for counting the time passed
 8
 9@onready var frame_default := frame # set the default frame
10
11func _process(delta: float) -> void:
12	if character_body: # only go here if character body is assigned
13		if character_body.velocity.x < 0.0: # if character body is moving left, flip the sprite
14			flip_h = true
15		elif character_body.velocity.x > 0.0:# if character body is moving right, don't flip the sprite
16			flip_h = false
17
18		if abs(character_body.velocity.x) > 0.0: # if character body is moving, animate it
19			count += delta # count up
20			if count >= 0.1: # after 100 ms switch the frame
21				count = 0.0
22				frame += 1
23				if frame > frame_default + anim_frame_amount: # reset frame, after the last frame is reached
24					frame = frame_default
25		else:
26			frame = frame_default # reset frame, if character body stands still

После этого в инспекторе нужно указать таилмап как текстура. Параметр Hframes указываем 10 и Vframes указываем 16. Фильтр выбираем Nearest

Игровой персонаж

Дальше настроим игрового персонажа. Для этого будем использовать шаблонный код для примера

Создайте новую сцену CharacterBody2D и назовите сцену Player. В сцену жобавляем CollisionShape2D и выбираем форму RectangleShape2D. Сохраняем сцену и добавляем новый скрипт к ноде Player. Во время создания скрипта выбираем шаблон Basic Movement Template

В код нужно внести несколько изменений, чтобы он выглядел вот так:

 1extends CharacterBody2D
 2class_name Player # give the class a name
 3
 4const SPEED = 100.0 # change the speed
 5const JUMP_VELOCITY = -200.0 # change the jump velocity
 6
 7
 8func _physics_process(delta: float) -> void:
 9	# Add the gravity.
10	if not is_on_floor():
11		velocity += get_gravity() * delta
12
13	# Handle jump.
14	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
15		velocity.y = JUMP_VELOCITY
16
17	# Get the input direction and handle the movement/deceleration.
18	# As good practice, you should replace UI actions with custom gameplay actions.
19	var direction := Input.get_axis("ui_left", "ui_right")
20	if direction:
21		velocity.x = direction * SPEED
22	else:
23		velocity.x = move_toward(velocity.x, 0, SPEED)
24
25	move_and_slide()

Теперь начнется вся магия. Начнем добавлять наши только что написанные компоненты. Для этого нажмите на кнопку рядом с плюсом, которая называется Instantiate Child Scene, найдите SpriteComp.tscn и добавьте в дерево сцены

Кликните по SpriteComp и в инспекторе для Character Body в качестве значения выберите Player. Также укажите Anim Frame Amount равному 1, так как у нас всего 2 кадра анимации. И не забудьте указать первый Frame правильно, в нашем случае это 45. Осталось проставить правильно форму коллизии и все готово.

Пчелы

Оживим наш мир и создадим различную живность. Первым существом будет пчела. Создайте новую сцену CharacterBody2D и назовите ее “Bee”. Добавьте туда CollisionShape2D и RectangleShape2D. Теперь можно добавить SpriteComp как мы делали это для игрового персонажа

Настройте его соответствующим образом, укажите Bee как Character Body. Anim Frame Amount снова укажите 1, стартовый Frame укажите 51

Следующий компонент, который мы добавляем, это MovementComp. Нажмите Plus и найдите нужный компонент. После добавления компонента появится новая нода в дереве. В инспекторе укажите Character Body, а также Direction и Speed. Устанавливаем правильную форму коллизии и на этом наша пчела готова

Препятствия

Теперь добавим препятствия для игрока, через которые он сможет прыгать. Создайте ноду StaticBody2D и назовите ее “Obstacle”. Добавьте CollisionShape2D и RectangleShape2D к этой ноде. Затем добавьте компонент SpriteComp но не указывайте значения Character Body и Anim Frame Amount оставьте равным 0. Замените значение Frame на 36 чтобы препятствие отображало картинку клети. Уже привычно укажите форму коллизии

Червячки

Давайте добавим больше живности в нашу игру для демонстрации работы компонентов. Как и в прошлый раз, создайте ноду CharacterBody2D и назовите ее “Worm”. Добавьте форму коллизии и оба компонента, как это сделано для пчелы. Установите в SpriteComp параметр Frame в 55. В компоненте MovementComp укажите параметр Dir равным -1 и чуть уменьшим скорость в параметре Speed. Последним шагом установите нужную форму и размер коллизии.

Тут необходимо небольшое пояснение.

SpriteComp это сцена, а значит при изменении базовой сцены, то компонент поменяется везде(если в ручную не изменили ее). Для нас это удобно потому что мы можем добавить TileMap в базовую цену SpriteComp и сразу настроить Hframes и Vframes

Для MovementComp мы использовали скрипт. Это значит, что при добавлении компонента в новый объект, MovementComp всегда стартует с дефолтными значениями, указанными в классе. Это больше похоже на поведение обычной ноды.

В зависимости от ситуации вы можете выбрать один из подходов.

Собираем вместе

Давайте быстро добавим слои коллизии и масок для разных объектов чтобы коллизии в принципе работали. Первым делом давайте дадим нормальные названия для слоев, чтобы они нам не путали. 1й слой назовем world, 2й player и 3й animal. Теперь для Player указываем слой “player” и маску “world”. Таким образом, игрок будет сталкиваться только с препятствиями на слое “world”

Для пчелы Bee указываем слой “animal”, а маску “world” и “animal”. Таким образом, пчелы будут сталкиваться с окружением и другой живностью. Для Worm укажем такие же значения, как и для пчел. Для препятствий Obstacle указываем слой “world” и маску “world”. Также настраиваем значения слоев и масок для TileMapLayer в главной сцене.

Осталось разместить все наши объекты на нашем уровне:

Запускаем игру и пробуем в нее поиграть. Перемещение на стрелки, прыжки на пробел:

Заключение

Композиция в Godot 4 это мощный инструмент для построения сложных, модульных и переиспользуемых игровых объектов. Освоив его, вы сможете создавать хорошо организованные, гибкие игровые проекты, которые легче поддерживать и расширять.

В этом руководстве рассматриваются основы композиции в Godot 4. Это неплохой старт для изучения более продвинутых концепций и техник. Независимо от того, создаете ли вы простую 2D-игру или сложный 3D-мир, принципы композиции помогут вам эффективно структурировать ваш проект.