Привет ,Habr. Сегодня мы начнём разработку своего карточного рогалика про приготовление... рогаликов! Именно эта тема победила в обсуждении под предыдущим туториалом. Работать будет на свежем Godot 4.1. В этой статье добавим карты, "Руку (Копыто) карт" и реализуем функцию Drag & Drop.



Знакомство

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

В последующих статьях он будет для нас:

  • Замешивать тесто

  • Формировать рогалики

  • Печь

  • Сервировать стол

С Эдгаром познакомились, вернёмся к нему в следующей части. Пока, Эдгар...


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

Создаём проект примерно так:

После создание проекта, заходим в настройки проекта -> Список действий и добавляем действие "click", щелчок ЛКМ

Сцена болванка для карт

Начнём как всегда с создания сцены, которую позже будем наследовать для разных карт.

Главным узлом выбираем Node2D(CardDefault), дочерний к нему Sprite2D(CardSprite) - текстурка нашей карты, дочерние к CardSprite:

  • Sprite2D(CardTitul) - Картинка рисуемая на карте

  • Label(CardName) - Заголовок карты

  • Label(CardDescription) - Описание карты

Дерево объектов:

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

Давайте загрузим шрифт в Label, на 4 версии, это делается в пару кликов. Найдите где-то шрифт, файл .ttf. Выберите в дереве объектов Label, В инспекторе на вкладке "Control", последний пункт "Theme Overrides", выбираем Fonts -> загрузить и загружаем свой шрифт, поменяйте ему цвет, размер, сделайте всё под себя. Также в инспекторе, найдите свойство Autowrap Mode и установите его в Word (Smart), для обоих Label. Создайте несколько карт. Изменяя текстуру в CardTitul и напишите для них название в CardName и описание в CardDescription. Должно получится примерно следующее:

Чтобы создать карту:

  • Создаём новую сцену

  • Главным узлом сцены выбираем "Инстанцировать дочернею сцену" (Ctrl+Shift+A) и выбираем нашу сцену, которую только-что создали.

  • CardTitul, В свойстве Texture, нажимаем на стрелочку "Сделать уникальным" и добавляем свою картинку.

  • В оба Label пишем текст.

К скрипту вернёмся чуть позже.

Сцена "Руки карт"

Главным узлом сцены выбираем Node2D(CardArm), и дочерние к нему элементы:

  • Sprite2D

  • 4 Marker2D(Card1...Card4)

Каждая метка будет указывать местоположение и угол поворота для карты. У вас может быть больше 4-х, просто у Эдгара влезает только 4 карты. Заливаем текстуру нашей руки и выставляем местоположение для Marker2D. Не большое отступление, так-же выставляйте угол поворота. Карты обычно держат под углом. Для Sprite2D выставляем Z_index = 2

Небольшой лайфхак. Вы можете добавить на сцену, 4 карты, инстанцировав дочернею сцену. Расположить их как вам нравится. Потом записать в Marker2D положения по X и Y и угол поворота

Добавляем скрипт на CardArm и переходим к его редактированию:

extends Node2D
#Объявляем 4 переменных на экспорт, это и будут наши карты
@export var card1:PackedScene
@export var card2:PackedScene
@export var card3:PackedScene
@export var card4:PackedScene

#Записываем наши паркеры и переменные
@onready var _card1_marker = $Card1
@onready var _card2_marker = $Card2
@onready var _card3_marker = $Card3
@onready var _card4_marker = $Card4
#Спрайтик тоже запишем, вдруг пригодится потом
@onready var _sprite = $Sprite2D
#Каждой карте мы будем присваивать ID, просто пропишите эту строчку
var ID = 0

#В функции _ready, создаём наши карты
func _ready():
	place_card(card1,_card1_marker)
	place_card(card2,_card2_marker)
	place_card(card3,_card3_marker)
	place_card(card4,_card4_marker)

#Функция создания карты, в качестве аргумента,
#передаём сцену, которую ставим и маркер - куда ставим.
func place_card(scene,marker):
	#Установили сцену карты
	var s = scene.instantiate()
	#Выставили карте поворот
	s.rotation_degrees = marker.rotation_degrees
	#Выставили карт местоположение
	s.position = marker.position
	#Увеличили счётчик, перед присваиванием, т.к. ID начинается с 1
	ID += 1
	#Присвоили ID
	s.ID = ID
	#Добавили сцену, как потомка
	add_child(s)

Переменная ID, потребуется в дальнейшем, чтобы управлять картами.

Синглтон CardMover

Создаём новый скрипт, обязательно проверяем, чтобы он наследовал Node. После создания скрипта, это делается на вкладке "Script" -> Файл -> Новый скрипт. Обязательно назовите его CardMover. Переходим в настройки -> Автозагрузка -> выбираем путь до нашего скрипта и нажимаем добавить. Мы будем использовать Глобальный синглтон, для управления рукой карт. Переходим к редактированию этого скрипта:

extends Node

#Выбранная карта
var selected_card_ID = null

#Установить ID
func set_ID(ID):
	selected_card_ID = ID

#Обнулить ID
func unset_ID():
	selected_card_ID = null

#Получить ID
func get_ID():
	return selected_card_ID

#Проверить ID
#Мы можем взаимодействовать только с выбранной картой 
#Или с пустой
func check_ID(ID):
	if((selected_card_ID == null) or (selected_card_ID == ID)):
		return true
	else:
		return false

Тут никаких сложных функций нет, только почти обычные гетеры и сетеры.

Скрипт болванки для карт

Навешиваем скрипт на CardDefault и переходим к его редактированию:

extends Node2D

#Объявили переменные дерева
@onready var _card_sprite = $CardSprite
@onready var _card_titul = $CardSprite/CardTitul
@onready var _card_description = $CardSprite/CardDescription

#Объявили переменную ID
@export var ID = "0"

#Объявили локальные переменные
var start_rotation:int#Переменная стартовой позиции
var start_position:Vector2#Переменная стартового поворота
var dragable = false#Выделен-ли объект, для перетаскивания
var start_z_index#Стартовый Z-index

#Объявляем все стартовые переменные
func _ready():
	start_z_index = z_index
	start_rotation = rotation_degrees
	start_position = position

#Функция обработки ввода игроком
func _input(event):
	#Если нажата ЛКМ
	if event is InputEventMouseButton and event.is_action_pressed("click"):
		#Если клик произошёл в области спрайта карты и проверили ID
		if (_card_sprite.get_rect().has_point(to_local(event.position)) and CardMover.check_ID(ID)):
			#Можно перетаскивать
			dragable = true
			#Установили ID
			CardMover.set_ID(ID)
			
	#Если отпущена ЛКМ
	if event is InputEventMouseButton and event.is_action_released("click"):
		#Если отпустили ЛКМ в области спрайта карты и проверили ID
		if (_card_sprite.get_rect().has_point(to_local(event.position)) and CardMover.check_ID(ID)):
			#Отменили перетаскивание
			dragable = false
			#Обнулили ID
			CardMover.unset_ID()
			#Если переместили карту, то
			if(position != start_position):
				#Убирам стартовый поворот
				start_rotation = 0
				#Меняем стартовое положение
				start_position = position
				#Поменяли угол
				rotation_degrees = start_rotation
			
	#Если курсор мыши двигается
	if event is InputEventMouseMotion:
		#Если при движении курсор мыши задел спрайт нашей карты и проверили ID
		if (_card_sprite.get_rect().has_point(to_local(event.position)) and CardMover.check_ID(ID)):
			#Убрали поворот
			rotation_degrees = 0
			#Установили поверх других карт
			z_index = 2
			#Задали ID
			CardMover.set_ID(ID)	
		else:
			#Проверили ID
			if (CardMover.check_ID(ID)):
				#Вернули z_index
				z_index = start_z_index
				#Вернули угол поворота
				rotation_degrees = start_rotation
				#Обнулили ID
				CardMover.unset_ID()

func _process(delta):
	#Если состояние карты перетягивание, то следуем за курсором	
	if dragable:
		set_global_position(get_global_mouse_position())

При создании карт в "Руке карт" мы добавляли каждой карте ID, чтобы была возможность отслеживать каждую карту в руке и взаимодействовать только с ней, если такого не было-бы, то в местах наслаивания карт друг на друга, мы бы брали две карты сразу или поворачивали две карты сразу при наведении.

Код в целом прокомментирован очень подробно, думаю вопросов не должно возникнуть.


Заключение

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

Комментарии (3)


  1. shaman4d
    11.07.2023 07:43

    А для тех кто опоздал, с какой статьи хорошо начать погружение в Godot?


    1. IsaacBlog Автор
      11.07.2023 07:43

      Лучше начинать от сюда ( https://habr.com/ru/articles/745716/ ) . Это самая первая статья в профиле.


  1. Kealkat
    11.07.2023 07:43

    Интересная серия статей, как раз недавно начал пробовать Godot.

    В будущем хотелось бы посмотреть, как делаются анимации.