Skip to content

Commit a37b144

Browse files
cursoragent4ian
andcommitted
feat: Add spritesheet support for sprites
Co-authored-by: florian <[email protected]>
1 parent ee41a58 commit a37b144

File tree

12 files changed

+428
-8
lines changed

12 files changed

+428
-8
lines changed

Core/GDCore/Extensions/Builtin/SpriteExtension/Direction.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ void Direction::UnserializeFrom(const gd::SerializerElement& element) {
9898
Sprite sprite;
9999

100100
sprite.SetImageName(spriteElement.GetStringAttribute("image"));
101+
// Support for spritesheet frames (optional fields)
102+
if (spriteElement.HasAttribute("spritesheetName")) {
103+
sprite.SetSpritesheetName(
104+
spriteElement.GetStringAttribute("spritesheetName"));
105+
}
106+
if (spriteElement.HasAttribute("spritesheetFrameName")) {
107+
sprite.SetSpritesheetFrameName(
108+
spriteElement.GetStringAttribute("spritesheetFrameName"));
109+
}
101110
OpenPointsSprites(sprite.GetAllNonDefaultPoints(),
102111
spriteElement.GetChild("points", 0, "Points"));
103112

@@ -164,6 +173,15 @@ void SaveSpritesDirection(const vector<Sprite>& sprites,
164173
gd::SerializerElement& spriteElement = element.AddChild("sprite");
165174

166175
spriteElement.SetAttribute("image", sprites[i].GetImageName());
176+
// Save spritesheet fields if they are set
177+
if (!sprites[i].GetSpritesheetName().empty()) {
178+
spriteElement.SetAttribute("spritesheetName",
179+
sprites[i].GetSpritesheetName());
180+
}
181+
if (!sprites[i].GetSpritesheetFrameName().empty()) {
182+
spriteElement.SetAttribute("spritesheetFrameName",
183+
sprites[i].GetSpritesheetFrameName());
184+
}
167185
SavePointsSprites(sprites[i].GetAllNonDefaultPoints(),
168186
spriteElement.AddChild("points"));
169187

Core/GDCore/Extensions/Builtin/SpriteExtension/Direction.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ namespace gd {
2424
*
2525
* \todo Support UTF8 (currently convert all loaded strings from UTF8 to the
2626
* current locale)
27+
*
28+
* \note Spritesheet support: Each Sprite in a Direction can use either:
29+
* - A regular image resource (via Sprite::GetImageName())
30+
* - A frame from a spritesheet (via Sprite::GetSpritesheetName() and
31+
* Sprite::GetSpritesheetFrameName())
32+
*
33+
* \note Future enhancement: The spritesheet JSON format may include an
34+
* "animations" field that maps animation names to arrays of frame names.
35+
* This could be used to automatically populate a Direction with all frames
36+
* from a spritesheet animation. For example:
37+
* - A Direction could be configured to use a spritesheet animation name
38+
* - All frames from that animation would be automatically added to the Direction
39+
* - This would simplify importing animations from tools like TexturePacker
2740
*/
2841
class GD_CORE_API Direction {
2942
public:

Core/GDCore/Extensions/Builtin/SpriteExtension/Sprite.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ Sprite::Sprite()
1919
: fullImageCollisionMask(false),
2020
origine("origine"),
2121
centre("centre"),
22-
automaticCentre(true) {}
22+
automaticCentre(true),
23+
spritesheetName(""),
24+
spritesheetFrameName("") {}
2325

2426
Sprite::~Sprite(){};
2527

Core/GDCore/Extensions/Builtin/SpriteExtension/Sprite.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,52 @@ class GD_CORE_API Sprite {
4242
*/
4343
inline gd::String& GetImageName() { return image; }
4444

45+
/**
46+
* \brief Set the spritesheet resource name and frame name.
47+
* When both are set, the sprite will use a frame from a spritesheet instead of an image.
48+
*/
49+
inline void SetSpritesheetName(const gd::String& spritesheetName_) {
50+
spritesheetName = spritesheetName_;
51+
}
52+
53+
/**
54+
* \brief Get the spritesheet resource name.
55+
*/
56+
inline const gd::String& GetSpritesheetName() const { return spritesheetName; }
57+
58+
/**
59+
* \brief Get the spritesheet resource name.
60+
*/
61+
inline gd::String& GetSpritesheetName() { return spritesheetName; }
62+
63+
/**
64+
* \brief Set the frame name within the spritesheet.
65+
*/
66+
inline void SetSpritesheetFrameName(const gd::String& frameName_) {
67+
spritesheetFrameName = frameName_;
68+
}
69+
70+
/**
71+
* \brief Get the frame name within the spritesheet.
72+
*/
73+
inline const gd::String& GetSpritesheetFrameName() const {
74+
return spritesheetFrameName;
75+
}
76+
77+
/**
78+
* \brief Get the frame name within the spritesheet.
79+
*/
80+
inline gd::String& GetSpritesheetFrameName() {
81+
return spritesheetFrameName;
82+
}
83+
84+
/**
85+
* \brief Return true if the sprite uses a spritesheet frame instead of an image.
86+
*/
87+
inline bool UsesSpritesheet() const {
88+
return !spritesheetName.empty() && !spritesheetFrameName.empty();
89+
}
90+
4591
/**
4692
* \brief Get the collision mask (custom or automatically generated owing to
4793
* IsFullImageCollisionMask())
@@ -161,6 +207,8 @@ class GD_CORE_API Sprite {
161207

162208
private:
163209
gd::String image; ///< Name of the image to be loaded in Image Manager.
210+
gd::String spritesheetName; ///< Name of the spritesheet resource (if using spritesheet).
211+
gd::String spritesheetFrameName; ///< Name of the frame within the spritesheet (if using spritesheet).
164212

165213
bool fullImageCollisionMask; ///< True to use a bounding box wrapping the
166214
///< whole image as collision mask. If false,

Core/GDCore/Project/ResourcesContainer.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ ResourcesContainer::CreateResource(const gd::String &kind) {
130130
return std::make_shared<AtlasResource>();
131131
else if (kind == "spine")
132132
return std::make_shared<SpineResource>();
133+
else if (kind == "spritesheet")
134+
return std::make_shared<SpritesheetResource>();
133135
else if (kind == "javascript")
134136
return std::make_shared<JavaScriptResource>();
135137
else if (kind == "internal-in-game-editor-only-svg")
@@ -144,6 +146,7 @@ const gd::String Resource::audioType = "audio";
144146
const gd::String Resource::fontType = "font";
145147
const gd::String Resource::videoType = "video";
146148
const gd::String Resource::jsonType = "json";
149+
const gd::String Resource::spritesheetType = "spritesheet";
147150
const gd::String Resource::tileMapType = "tilemap";
148151
const gd::String Resource::tileSetType = "tileset";
149152
const gd::String Resource::bitmapType = "bitmapFont";

Core/GDCore/Project/ResourcesContainer.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class GD_CORE_API Resource {
3737
static const gd::String model3DType;
3838
static const gd::String atlasType;
3939
static const gd::String spineType;
40+
static const gd::String spritesheetType;
4041
static const gd::String javaScriptType;
4142
static const gd::String internalInGameEditorOnlySvgType;
4243

@@ -566,6 +567,22 @@ class GD_CORE_API AtlasResource : public Resource {
566567
gd::String file;
567568
};
568569

570+
/**
571+
* \brief Describe a spritesheet JSON file used by a project.
572+
* A spritesheet contains frame definitions that reference an image resource.
573+
*
574+
* \see Resource
575+
* \ingroup ResourcesManagement
576+
*/
577+
class GD_CORE_API SpritesheetResource : public JsonResource {
578+
public:
579+
SpritesheetResource() : JsonResource() { SetKind("spritesheet"); };
580+
virtual ~SpritesheetResource(){};
581+
virtual SpritesheetResource *Clone() const override {
582+
return new SpritesheetResource(*this);
583+
}
584+
};
585+
569586
/**
570587
* \brief Describe a JavaScript file used by a project.
571588
*

GDJS/Runtime/ResourceLoader.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ namespace gdjs {
146146
private _bitmapFontManager: BitmapFontManager;
147147
private _spineAtlasManager: SpineAtlasManager | null = null;
148148
private _spineManager: SpineManager | null = null;
149+
private _spritesheetManager: PixiSpritesheetManager | null = null;
149150
private _svgManager: InternalInGameEditorOnlySvgManager;
150151

151152
/**
@@ -206,6 +207,12 @@ namespace gdjs {
206207
);
207208
}
208209

210+
// Create spritesheet manager
211+
this._spritesheetManager = new gdjs.PixiSpritesheetManager(
212+
this,
213+
this._imageManager
214+
);
215+
209216
const resourceManagers: Array<ResourceManager> = [
210217
this._imageManager,
211218
this._soundManager,
@@ -214,6 +221,7 @@ namespace gdjs {
214221
this._bitmapFontManager,
215222
this._model3DManager,
216223
this._svgManager,
224+
this._spritesheetManager,
217225
];
218226

219227
if (this._spineAtlasManager)
@@ -814,6 +822,15 @@ namespace gdjs {
814822
return this._spineAtlasManager;
815823
}
816824

825+
/**
826+
* Get the Spritesheet manager of the game, used to load spritesheets from game
827+
* resources.
828+
* @return The Spritesheet manager for the game
829+
*/
830+
getSpritesheetManager(): gdjs.PixiSpritesheetManager | null {
831+
return this._spritesheetManager;
832+
}
833+
817834
injectMockResourceManagerForTesting(
818835
resourceKind: ResourceKind,
819836
resourceManager: ResourceManager

GDJS/Runtime/SpriteAnimator.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ namespace gdjs {
3333
export type SpriteFrameData = {
3434
/** The resource name of the image used in this frame. */
3535
image: string;
36+
/** The spritesheet resource name (if using a spritesheet frame instead of an image). */
37+
spritesheetName?: string;
38+
/** The frame name within the spritesheet (if using a spritesheet frame instead of an image). */
39+
spritesheetFrameName?: string;
3640
/** The points of the frame. */
3741
points: Array<SpriteCustomPointData>;
3842
/** The origin point. */
@@ -79,7 +83,11 @@ namespace gdjs {
7983
* Abstraction from graphic libraries texture classes.
8084
*/
8185
export interface AnimationFrameTextureManager<T> {
82-
getAnimationFrameTexture(imageName: string): T;
86+
getAnimationFrameTexture(
87+
imageName: string,
88+
spritesheetName?: string,
89+
spritesheetFrameName?: string
90+
): T;
8391
getAnimationFrameWidth(pixiTexture: T);
8492
getAnimationFrameHeight(pixiTexture: T);
8593
}
@@ -110,7 +118,11 @@ namespace gdjs {
110118
textureManager: gdjs.AnimationFrameTextureManager<T>
111119
) {
112120
this.image = frameData ? frameData.image : '';
113-
this.texture = textureManager.getAnimationFrameTexture(this.image);
121+
this.texture = textureManager.getAnimationFrameTexture(
122+
this.image,
123+
frameData?.spritesheetName,
124+
frameData?.spritesheetFrameName
125+
);
114126
this.points = new Hashtable();
115127
this.reinitialize(frameData, textureManager);
116128
}
@@ -124,7 +136,11 @@ namespace gdjs {
124136
textureManager: gdjs.AnimationFrameTextureManager<T>
125137
) {
126138
this.image = frameData.image;
127-
this.texture = textureManager.getAnimationFrameTexture(this.image);
139+
this.texture = textureManager.getAnimationFrameTexture(
140+
this.image,
141+
frameData.spritesheetName,
142+
frameData.spritesheetFrameName
143+
);
128144

129145
this.points.clear();
130146
for (let i = 0, len = frameData.points.length; i < len; ++i) {

0 commit comments

Comments
 (0)