So you let the player select a series of sprite sheets. And there is some order in which you overlay them to create the sprite sheet of the player.
Image manipulation at runtime
We can create an Image
:
var image = Image.new()
image.create(256,256,false,Image.FORMAT_RGBA8)
Change 256,256
with the appropriate size.
There the format is important. You want a format that has an alpha channel, otherwise, you will not be able to draw with transparency.
Now we need the image we are going to draw (the first sprite sheet). We can use load
or preload
to get it. For example:
var icon := preload("res://icon.png")
We usually import as textures, but we want an image. So we use get_data()
:
var source := preload("res://icon.png").get_data()
Then we can draw it with blit_rect
:
image.blit_rect(source, Rect2(Vector2.ZERO, source.get_size()), Vector2.ZERO)
There the Rect2
is the source area (the area of the image we are drawing that we want to draw) which goes from Vector2.ZERO
to the size of the image. The other Vector2.ZERO
is the position where we want to draw it.
Now the idea is that you will do that for all the sprite sheets that you need to draw.
Note: depending on the format of the image you want to draw, you may have to convert it first:
source.convert(Image.FORMAT_RGBA8)
Also, usually, the images are imported in a compressed format that cannot be converted directly, and thus you would have to decompress first:
source.decompress()
source.convert(Image.FORMAT_RGBA8)
If the blit_rect
line is giving you problems, try that.
Using a Viewport texture
If you found the above approach too limiting. You can add a Viewport
with a Camera2D
. Then to get the texture from the Viewport
, you can use viewport.get_texture()
(and, of course, you can get an Image
from it with .get_data()
).
Thus, you can add to the Viewport
anything you want to draw. Which means that you can add multiple nodes to compose the image, with their transforms and modulations. Or it could be a single one which uses the methods described in Custom drawing in 2D, or CanvasItem shaders (For example, you may allow the user to do a hue rotation of a sprite sheet, implemented in a shader). Or combinations of those techniques.
Creating sprite frames for animation
Once you are done, you can create an ImageTexture from the Image
, like this:
image_texture = ImageTexture.new()
image_texture.create_from_image(image)
Since we are talking of sprite sheet, we can create AtlasTexture
s:
func get_texture_frame(row, column):
var t = AtlasTexture.new()
t.atlas = image_texture
t.region = (
column * SPRITE_WIDTH,
row * SPRITE_HEIGHT,
SPRITE_WIDTH,
SPRITE_HEIGHT
)
return t
Then we can add the AtlasTexture
s to a SpriteFrames
with add_frame
to use them in an AnimatedSprite
.
Creating a SpriteFrames
in code looks something like this:
var frames := SpriteFrames.new()
frames.add_animation("animation_name")
frames.set_animation_speed("animation_name", frames_per_second)
frames.add_frame("animation_name", get_texture_frame(0, 0))
frames.add_frame("animation_name", get_texture_frame(0, 1))
# …
Persistent storage
If you want to save an Image
, you can use save_png
.
If you want to save the ImageTexture
or the AtlasTexture
s or the SpriteFrames
, you can use ResourceSaver.save
. I suggest to use path in user://
, see Data Paths.
To load them again, you can use load
or ResourceLoader.load
.