character editor

Today on Godot 4.1 we will create a simple character editor, like in old RPGs, when you choose the appearance of a character from already drawn assets.

Introduction

First, let’s talk a little about how this will work. We have a game character whose appearance will be set by the player, in this example we will consider what will be set: skin color, eyes, smile. The sprites chosen by the player must be saved somewhere and we will later load them onto the character. For good, you need to store this in the player’s save file and, when starting this save, pour it into a singleton, which is declared as a global variable. In this tutorial, we will store only in a singleton and will not save anywhere.

Graphic arts

What should graphics be?

First you need to create something like a mannequin on which we will draw. That is, it will be our character, who has swept under the eyes and mouth (in our case).

Dummy

Dummy

Then we draw everything on it and save it as a separate sprite.

Something like this

Something like this

You must have the same image sizes in each group of elements. For example, for all pairs of eyes, the size is 100X30, for all smiles – 100X40, etc.

Character

With the graphics done, now let’s move on to the character scene in Godot. For me it looks like this:

Yes, of course, we should also add a collision node, maybe some more nodes, but in this tutorial we will just make a character that the player creates.

How to correctly arrange the location of Sprite2D (you may have AnimatedSprite2D, then this method will not work, if you want, then we will consider this in a separate article). To do this, we again use our manikin and try everything on it. That is, we choose as a texture for the Body, a mannequin and expose the eyes and smile.

Editor Scene

My scene looks like this

I think what each node is responsible for, it makes no sense to explain. These are just inscriptions, buttons and decor elements. ColorRect(Preview) just uses 3 sprites as children, not the character scene.

Let’s get to the code:

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()

In the _ready function, we fill the arrays with pictures, and then simply fill in the corresponding Sprite2D with the corresponding texture.

Now we need to write a HeroView singleton and put it on autoload and make it a global variable.

Singleton 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

It just stores pictures and contains setters and getters for pictures.

How could you in the script for the editor scene there is no handling of pressing the accept button, and here it is:

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

We’re just updating the variables in the HeroView singleton.

finishing touch

Now we have where the pictures are stored, but what to do with them next? Next, we simply add the following function to our character:

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

And we call the operation of this function when it is necessary to update its appearance.

Result

I also added that the hero appears on the stage when the Accept button is pressed, and now we have the following:

Show result

Show result

Well, we have created a simple character editor.

Not a big deal

There were no articles for a long time, because I just don’t know what you would be interested in reading. Therefore, if you want articles to come out more often, write in the comments about what to write an article about.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *