Редактор персонажа

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Сегодня на Godot 4.1 создадим простой редактор персонажа, как в старых рпг, когда ты выбираешь внешний вид персонажа из уже нарисованных ассетов.

Вступление

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

Графика

Что из себя должна представлять графика.

Для начала нужно создать что-то вроде манекена, на котором мы будем рисовать. То есть это будет наш персонаж, у которого есть отметены под глаза и рот(в нашем случае).

Манекен
Манекен

Дальше на нём всё рисуем и сохраняем, как отдельный спрайт.

Что-то типо такого
Что-то типо такого

Размеры изображений у вас должны быть одинаковые в каждой группе элементов. Например для всех пар глаз, размер - 100X30, для всех улыбок - 100X40 и т.д.

Персонаж

С графикой закончили, теперь перейдём к сцене персонажа в Godot.У меня она выглядит следующим образом:

Да, конечно следует ещё добавить узел коллизии, возможно ещё некоторые узлы, но в данном туториале мы просто сделаем персонажа, которого создаёт игрок.

Как правильно расставить местоположение Sprite2D(у вас может быть AnimatedSprite2D, тогда этот способ не подойдёт, если захотите, то рассмотрим это в отдельной статье). Для этого мы опять используем нашего маникена и примеряем всё на нём. То есть выбираем, как текстуру для Body, маникена и выставляем глаза и улыбку.

Сцена редактора

У меня сцена имеет примерно следующий вид

Думаю за что отвечает каждый узел, объяснять смысла нет. Это просто надписи, кнопки и элементы декора. У ColorRect(Preview), как дочерние элементы используются просто 3 спрайта, а не сцена персонажа.

Давайте перейдём к коду:

extends Node2D
#константы с путями к ассетами
const EYE_ROOT = "res://Assets/Eye/eye"
const SKIN_ROOT = "res://Assets/Skin/Skin"
const SMILE_ROOT = "res://Assets/Smile/Smile"
#Массивы которые будут хранить наши ассеты
var eye_array = []
var skin_array = []
var smile_array = []
#номер эллемента в массиве
var eye_number = 0
var skin_number = 0
var smile_number = 0
#элементы дерева
@onready var _eye = $CustomMenu/Preview/Body/Eye
@onready var _body = $CustomMenu/Preview/Body
@onready var _smile = $CustomMenu/Preview/Body/Smile

#Вспомогательные функции для получения полных путей ассетов
func get_eye_path(index):
	return EYE_ROOT + str(index) + ".png"

func get_skin_path(index):
	return SKIN_ROOT + str(index) + ".png"
	
func get_smile_path(index):
	return SMILE_ROOT + str(index) + ".png"
	
#Заполним массив ассетами глаз
func get_eye_array():
	#Счётчик
	var i = 1
	while true:
		#Если такая картинка есть, то добавляем в массив
		if load(get_eye_path(i)) != null:
			eye_array.append(load(get_eye_path(i)))
		#Иначе заканчиваем while
		#У меня все картинки идут по порядку(Eye1,Eye2...)
		#Можно сделать чуть иначе, но так проще...
		else:
			break
		i+=1

#тоже самое, но для улыбок
func get_smile_array():
	var i = 1
	
	while true:

		if load(get_smile_path(i)) != null:
			smile_array.append(load(get_smile_path(i)))
		else:
			break
			
		i+=1
		
#тоже самое, но для кожи
func get_skin_array():
	var i = 1
	
	while true:

		if load(get_skin_path(i)) != null:
			skin_array.append(load(get_skin_path(i)))
		else:
			break
			
		i+=1
#наполнили все массивы
func _ready():
	get_eye_array()
	get_smile_array()
	get_skin_array()
	
#функция получения новых глаз на превью
func get_new_eye():
	#Сделали вращалки цикличными
	if eye_number == eye_array.size():
		eye_number = 0
	if eye_number == -1:
		eye_number = eye_array.size() - 1
	#Залили новую текстурку
	_eye.texture = eye_array[eye_number]

#тоже самое для кожи
func get_new_skin():
	if skin_number == skin_array.size():
		skin_number = 0
	if skin_number == -1:
		skin_number = skin_array.size() - 1
	_body.texture = skin_array[skin_number] 

#тоже самое для улыбок
func get_new_smile():
	if smile_number == smile_array.size():
		smile_number = 0
	if smile_number == -1:
		smile_number = smile_array.size() - 1
	_smile.texture = smile_array[smile_number]

#Обработка сигналов для кнопок Skin
func _on_skin_next_pressed():
	skin_number+=1
	get_new_skin()

func _on_skin_prew_pressed():
	skin_number-=1
	get_new_skin()

#Обработка сигналов для кнопок Eye
func _on_eye_next_pressed():
	eye_number+=1
	get_new_eye()

func _on_eye_prew_pressed():
	eye_number-=1
	get_new_eye()

#Обработка сигналов для кнопок Smile
func _on_smile_next_pressed():
	smile_number+=1
	get_new_smile()

func _on_smile_prew_pressed():
	smile_number-=1
	get_new_smile()

В функции _ready, мы заполняем массивы картинками, и дальше просто заливаем в соответствующий Sprite2D, соответствующую текстурку.

Теперь нам потребуется написать синглтон HeroView и поставить его на автозагрузку и сделать глобальной переменной.

Синглтон HeroView:

extends Node

#Объявляем переменные хранящие картинки(не должны быть пустыми, чтобы не было ошибок)
var skin = load("res://Assets/Skin/Skin1.png")
var eye = load("res://Assets/Eye/eye1.png")
var smile = load("res://Assets/Smile/Smile1.png")

#Обычные сеттеры и геттеры
func set_skin(new_skin):
	skin = new_skin

func set_eye(new_eye):
	eye = new_eye

func set_smile(new_smile):
	smile = new_smile
	
func get_skin():
	return skin
	
func get_eye():
	return eye

func get_smile():
	return smile

Он просто хранить картинки и содержит сеттеры и геттеры для картинок.

Как вы могли в скрипте для сцены редактора нет обработки нажатия на кнопку accept, а вот и она:

#Обработка сигнала для кнопки принять
func _on_accept_pressed():
	HeroView.set_skin(_body.texture)
	HeroView.set_eye(_eye.texture)
	HeroView.set_smile(_smile.texture)

Мы просто обновляем переменные в синглтоне HeroView.

Завершающий штрих

Теперь у нас есть где хранятся картинки, но что с ними делать дальше? Дальше мы просто добавляем нашему персонажу следующую функцию:

func get_new_look():
	_body.texture = HeroView.get_skin()
	_eye.texture = HeroView.get_eye()
	_smile.texture = HeroView.get_smile()

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

Результат

Я ещё добавил, что герой появляется на сцене, при нажатии кнопки Accept, и теперь мы имеет следующее:

Демонстрация результат
Демонстрация результат

Ну вот мы и создали простенький редактор персонажа.

Не большое обращение

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

Источник: https://habr.com/ru/articles/749532/


Интересные статьи

Интересные статьи

Разработка приложения или игры требует от разработчика знания доступных инструментов и умения ими пользоваться. В данной статье описаны основы работы с редактором Unity. Статья ориентирована на начина...
Всё началось, когда вышла очередная версии TkProE — интегрированной среды разработки программ на tcl/tk. Мне очень пришлось по нраву наличие в ней встроенного графического редактора. Но этот редактор ...
Прошло шесть месяцев с тех пор, как мы анонсировали первую предварительную версию нового экспериментального редактора Razor для Visual Studio, основанного на общем языков...
На днях создатели редактора векторной графики Akira, работа над которым ведется вот уже пару лет, опубликовали первые тестовые выпуски. Графический пакет предназначен для создания ...
Когда в Sports.ru понадобился свой WYSIWYG-редактор, мы решили сделать его на основе библиотеки ProseMirror. Одной из ключевых особенностей этого инструмента является модульность и широкие возмож...