2D Modular Sprite Engine (Spriter Pro)

Applications, Games, Tools, User libs and useful stuff coded in PureBasic
User avatar
IceSoft
Addict
Addict
Posts: 1738
Joined: Thu Jun 24, 2004 8:51 am
Location: Germany

Re: 2D Modular Sprite Engine (Spriter Pro)

Post by IceSoft »

D Ogre wrote: Fri Feb 20, 2026 12:22 am I'm currently working on further compatibility with Spriter's file format.
The scml file (Spriter Pro json Format) is easier using with PB writting a PB wrapper (C++)
In this case you can use the original example:
https://github.com/lucidspriter/SpriterPlusPlus
Puzzle of Mystralia (C++)
Bug Planet, Waponez III, =QONK=, PetriDish, Movie2Image
<Wrapper>4PB, PB<game>, PictureManager,...
D Ogre
User
User
Posts: 36
Joined: Tue Jan 01, 2008 3:15 am
Location: Michigan, USA

Re: 2D Modular Sprite Engine (Spriter Pro)

Post by D Ogre »

I rather not make use of any outside dependencies in my engine. I'm almost done implementing everything anyhow. It's actually not as hard as it seems. The JSON and XML libraries work just fine in reading a Papagayo and Spriter Pro files.

;==============================================================================
; SpriterPlayer.pbi - Spriter Pro SCML Parser and Animation Player
; Parses SCML XML, interpolates keyframes, applies bone hierarchy transforms
; Requires: Engine.pbi, AssetPack.pbi

I'll be working on a demo shortly. :D
D Ogre
User
User
Posts: 36
Joined: Tue Jan 01, 2008 3:15 am
Location: Michigan, USA

Re: 2D Modular Sprite Engine (Spriter Pro)

Post by D Ogre »

This is for anyone who wants to know how to read the data from a Spriter SCML file:

Code: Select all

; ### Key Data Extracted:
; 
; **Image Files:**
; - boss_0/boss_torso_0.png (127x146) pivot: 0.5357,0.0909
; - boss_0/boss_thigh_a.png (57x52) pivot: 0.7988,0.7838
; - boss_0/boss_thigh_0.png (39x48) pivot: 0.4035,0.8406
; - ... (28 total image files)
; 
; **Sound Files:**
; - sounds/EXPLOD14.wav
; - sounds/BossLaugh.wav
; - sounds/BossHit_1.wav
; - sounds/BossHit_2.wav
; - sounds/FireBreath.wav
; 
; **Bone Data:**
; - Bone_001 (boss_torso_0) size: 127.0x146.0
; - Bone_002 (boss_thigh_a) size: 57.0x52.0
; - ... (11 bones total)
; 
; **Animation Data:**
; - Animation "Idle" (2000ms)
;   - Timeline 0: bone_001 (Boss_0_boss_torso_0)
;     - Key @0ms: x=0.00, y=0.00, angle=0.00, scale=1.00
;     - Key @128ms: x=0.00, y=0.00, angle=359.00, scale=1.00
;     - ... (16 keys)
;   - Timeline 24: bone_010
;     - Key @0ms: x=19.56, y=18.11, angle=110.66, scale=0.41
;     - Key @128ms: x=19.56, y=18.11, angle=106.98, scale=0.41
;     - ... (17 keys)
;   - ... (26 timelines total)


Structure SpriterFile
  id.i
  name.s
  width.i
  height.i
  pivot_x.f
  pivot_y.f
  type.s
EndStructure

Structure SpriterFolder
  id.i
  name.s
  List files.SpriterFile()
EndStructure

Structure SpriterBone
  name.s
  realname.s
  w.f
  h.f
EndStructure

Structure SpriterAnimationKey
  time.i
  x.f
  y.f
  angle.f
  scale_x.f
  spin.i
EndStructure

Structure SpriterTimeline
  id.i
  name.s
  obj.s
  List keys.SpriterAnimationKey()
EndStructure

Structure SpriterAnimation
  name.s
  length.i
  List timelines.SpriterTimeline()
EndStructure

Structure SpriterEntity
  id.i
  name.s
  List bones.SpriterBone()
  List animations.SpriterAnimation()
EndStructure

Global NewList folders.SpriterFolder()
Global NewList entities.SpriterEntity()

Procedure LoadSpriterData(filename.s)
  Protected xml = LoadXML(#PB_Any, filename)
  If Not xml
    Debug "Error loading XML file: " + filename
    ProcedureReturn #False
  EndIf
  
  Protected *root = MainXMLNode(xml)
  If Not *root Or GetXMLNodeName(*root) <> "spriter_data"
    Debug "Invalid Spriter file format"
    FreeXML(xml)
    ProcedureReturn #False
  EndIf
  
  ; Load folders and files
  Protected *folder = ChildXMLNode(*root)
  While *folder
    If GetXMLNodeName(*folder) = "folder"
      AddElement(folders())
      folders()\id = Val(GetXMLAttribute(*folder, "id"))
      folders()\name = GetXMLAttribute(*folder, "name")
      
      Protected *file = ChildXMLNode(*folder)
      While *file
        If GetXMLNodeName(*file) = "file"
          AddElement(folders()\files())
          folders()\files()\id = Val(GetXMLAttribute(*file, "id"))
          folders()\files()\name = GetXMLAttribute(*file, "name")
          folders()\files()\width = Val(GetXMLAttribute(*file, "width"))
          folders()\files()\height = Val(GetXMLAttribute(*file, "height"))
          folders()\files()\pivot_x = ValF(GetXMLAttribute(*file, "pivot_x"))
          folders()\files()\pivot_y = ValF(GetXMLAttribute(*file, "pivot_y"))
          folders()\files()\type = GetXMLAttribute(*file, "type")
        EndIf
        *file = NextXMLNode(*file)
      Wend
    EndIf
    *folder = NextXMLNode(*folder)
  Wend
  
  ; Load entities, bones and animations
  *folder = ChildXMLNode(*root)
  While *folder
    If GetXMLNodeName(*folder) = "entity"
      AddElement(entities())
      entities()\id = Val(GetXMLAttribute(*folder, "id"))
      entities()\name = GetXMLAttribute(*folder, "name")
      
      Protected *obj = ChildXMLNode(*folder)
      While *obj
        Select GetXMLNodeName(*obj)
          Case "obj_info"
            Protected type.s = GetXMLAttribute(*obj, "type")
            If type = "bone"
              AddElement(entities()\bones())
              entities()\bones()\name = GetXMLAttribute(*obj, "name")
              entities()\bones()\realname = GetXMLAttribute(*obj, "realname")
              entities()\bones()\w = ValF(GetXMLAttribute(*obj, "w"))
              entities()\bones()\h = ValF(GetXMLAttribute(*obj, "h"))
            EndIf
            
          Case "animation"
            AddElement(entities()\animations())
            entities()\animations()\name = GetXMLAttribute(*obj, "name")
            entities()\animations()\length = Val(GetXMLAttribute(*obj, "length"))
            
            Protected *timeline = ChildXMLNode(*obj)
            While *timeline
              If GetXMLNodeName(*timeline) = "timeline"
                AddElement(entities()\animations()\timelines())
                entities()\animations()\timelines()\id = Val(GetXMLAttribute(*timeline, "id"))
                entities()\animations()\timelines()\name = GetXMLAttribute(*timeline, "name")
                entities()\animations()\timelines()\obj = GetXMLAttribute(*timeline, "obj")
                
                Protected *key = ChildXMLNode(*timeline)
                While *key
                  If GetXMLNodeName(*key) = "key"
                    AddElement(entities()\animations()\timelines()\keys())
                    entities()\animations()\timelines()\keys()\time = Val(GetXMLAttribute(*key, "time"))
                    entities()\animations()\timelines()\keys()\spin = Val(GetXMLAttribute(*key, "spin"))
                    
                    Protected *bone = ChildXMLNode(*key)
                    If *bone And GetXMLNodeName(*bone) = "bone"
                      entities()\animations()\timelines()\keys()\x = ValF(GetXMLAttribute(*bone, "x"))
                      entities()\animations()\timelines()\keys()\y = ValF(GetXMLAttribute(*bone, "y"))
                      entities()\animations()\timelines()\keys()\angle = ValF(GetXMLAttribute(*bone, "angle"))
                      entities()\animations()\timelines()\keys()\scale_x = ValF(GetXMLAttribute(*bone, "scale_x"))
                    EndIf
                  EndIf
                  *key = NextXMLNode(*key)
                Wend
              EndIf
              *timeline = NextXMLNode(*timeline)
            Wend
          EndSelect
          *obj = NextXMLNode(*obj)
        Wend
      EndIf
      *folder = NextXMLNode(*folder)
    Wend
    
    FreeXML(xml)
    ProcedureReturn #True
EndProcedure

; Example usage
File.s = OpenFileRequester("Open Spriter SCML", "", "Spriter (*.scml)|*.scml|All files (*.*)|*.*" ,0)
If File
  Debug "Spriter File :" + file

If LoadSpriterData(File)
  Debug "Loaded Spriter data successfully"
  
  ; List all image files
  Debug "=== Image Files ==="
  ForEach folders()
    ForEach folders()\files()
      If folders()\files()\type <> "sound"
        Debug Str(folders()\files()\id) + ": " + folders()\files()\name + 
              " (" + Str(folders()\files()\width) + "x" + Str(folders()\files()\height) + ")" +
              " pivot: " + StrF(folders()\files()\pivot_x, 4) + "," + StrF(folders()\files()\pivot_y, 4)
      EndIf
    Next
  Next
  
  ; List all sound files
  Debug "=== Sound Files ==="
  ForEach folders()
    ForEach folders()\files()
      If folders()\files()\type = "sound"
        Debug Str(folders()\files()\id) + ": " + folders()\files()\name
      EndIf
    Next
  Next
  
  ; List all bones
  Debug "=== Bone Data ==="
  ForEach entities()
    ForEach entities()\bones()
      Debug entities()\bones()\name + " (" + entities()\bones()\realname + ")" +
            " size: " + StrF(entities()\bones()\w, 1) + "x" + StrF(entities()\bones()\h, 1)
    Next
  Next
  
  ; List animation data
  Debug "=== Animation Data ==="
  ForEach entities()
    ForEach entities()\animations()
      Debug "Animation: " + entities()\animations()\name + 
            " (" + Str(entities()\animations()\length) + "ms)"
      
      ForEach entities()\animations()\timelines()
        Debug "  Timeline " + Str(entities()\animations()\timelines()\id) + 
              ": " + entities()\animations()\timelines()\name +
              " (" + entities()\animations()\timelines()\obj + ")"
        
        ForEach entities()\animations()\timelines()\keys()
          Debug "    Key @ " + Str(entities()\animations()\timelines()\keys()\time) + "ms: " +
                "x=" + StrF(entities()\animations()\timelines()\keys()\x, 2) + 
                ", y=" + StrF(entities()\animations()\timelines()\keys()\y, 2) +
                ", angle=" + StrF(entities()\animations()\timelines()\keys()\angle, 2) +
                ", scale=" + StrF(entities()\animations()\timelines()\keys()\scale_x, 2)
        Next
      Next
    Next
  Next
EndIf
EndIf

D Ogre
User
User
Posts: 36
Joined: Tue Jan 01, 2008 3:15 am
Location: Michigan, USA

Re: 2D Modular Sprite Engine (Spriter Pro)

Post by D Ogre »

^^^ Moved Spriter Demo Viewer to the top of post. ^^^
Last edited by D Ogre on Sat Feb 28, 2026 9:57 pm, edited 3 times in total.
User avatar
IceSoft
Addict
Addict
Posts: 1738
Joined: Thu Jun 24, 2004 8:51 am
Location: Germany

Re: 2D Modular Sprite Engine (Spriter Pro)

Post by IceSoft »

@D Ogre,
Good work so far. Good start point now!

I tried the orginal GreyGay example from BrashMonkey.
works:
- Animation seems to works
-SCML file is correct load as far Im can see
wrong:
- position of the sprites need fine tuning

ToDo:
Not all HUD function be implemented: Scale_Up/Down, etc

Here are the correct animation and position of the Gray Gay example:
https://youtu.be/oR4eCCZ6xCQ
Puzzle of Mystralia (C++)
Bug Planet, Waponez III, =QONK=, PetriDish, Movie2Image
<Wrapper>4PB, PB<game>, PictureManager,...
D Ogre
User
User
Posts: 36
Joined: Tue Jan 01, 2008 3:15 am
Location: Michigan, USA

Re: 2D Modular Sprite Engine (Spriter Pro)

Post by D Ogre »

There is nothing wromg with the demo. The loader and rendering system is designed for the r11 Spriter file format. The GreyGuy is actually the older b5.95 format. i am working on mods to the Demo to allow correct loading of both formats.
User avatar
IceSoft
Addict
Addict
Posts: 1738
Joined: Thu Jun 24, 2004 8:51 am
Location: Germany

Re: 2D Modular Sprite Engine (Spriter Pro)

Post by IceSoft »

D Ogre wrote: Fri Feb 27, 2026 7:38 am There is nothing wromg with the demo. The loader and rendering system is designed for the r11 Spriter file format. The GreyGuy is actually the older b5.95 format. i am working on mods to the Demo to allow correct loading of both formats.
Can you add a message about the loaded scml version?
and a hint "that scml version is currently not supported."
Puzzle of Mystralia (C++)
Bug Planet, Waponez III, =QONK=, PetriDish, Movie2Image
<Wrapper>4PB, PB<game>, PictureManager,...
User avatar
IceSoft
Addict
Addict
Posts: 1738
Joined: Thu Jun 24, 2004 8:51 am
Location: Germany

Re: 2D Modular Sprite Engine (Spriter Pro)

Post by IceSoft »

D Ogre wrote: Fri Feb 27, 2026 7:38 am There is nothing wromg with the demo. The loader and rendering system is designed for the r11 Spriter file format. The GreyGuy is actually the older b5.95 format. i am working on mods to the Demo to allow correct loading of both formats.
Can you try this scml:
https://manneko.itch.io/viking-boy
its r11 but position be wrong:
<spriter_data scml_version="1.0" generator="BrashMonkey Spriter" generator_version="r11">
Puzzle of Mystralia (C++)
Bug Planet, Waponez III, =QONK=, PetriDish, Movie2Image
<Wrapper>4PB, PB<game>, PictureManager,...
D Ogre
User
User
Posts: 36
Joined: Tue Jan 01, 2008 3:15 am
Location: Michigan, USA

Re: 2D Modular Sprite Engine (Spriter Pro)

Post by D Ogre »

New version of Spriter Demo posted above. 8)
User avatar
IceSoft
Addict
Addict
Posts: 1738
Joined: Thu Jun 24, 2004 8:51 am
Location: Germany

Re: 2D Modular Sprite Engine (Spriter Pro)

Post by IceSoft »

D Ogre wrote: Fri Feb 27, 2026 7:31 pm New version of Spriter Demo posted above. 8)
Great work. Works now.
Thanks for sharing
Puzzle of Mystralia (C++)
Bug Planet, Waponez III, =QONK=, PetriDish, Movie2Image
<Wrapper>4PB, PB<game>, PictureManager,...
User avatar
IceSoft
Addict
Addict
Posts: 1738
Joined: Thu Jun 24, 2004 8:51 am
Location: Germany

Re: 2D Modular Sprite Engine (Spriter Pro)

Post by IceSoft »

@D Ogre
I created a scml with sound.
Works as far as I can say but plays the whole animation.

seems <soundline> is currently not supported

Code: Select all

  ... 
            <timeline id="3" obj="1" name="bone_001" object_type="bone">
                <key id="0">
                    <bone x="3" y="7" angle="152.987335"/>
                </key>
                <key id="1" time="199" spin="-1">
                    <bone x="3" y="7" angle="236.700579"/>
                </key>
            </timeline>
            <soundline id="-1" name="sample-3s">
                <key id="0" time="199" curve_type="instant">
                    <object folder="1" file="0" panning="-0.656"/>
                </key>
            </soundline>
        </animation>
    </entity>
</spriter_data>
Puzzle of Mystralia (C++)
Bug Planet, Waponez III, =QONK=, PetriDish, Movie2Image
<Wrapper>4PB, PB<game>, PictureManager,...
D Ogre
User
User
Posts: 36
Joined: Tue Jan 01, 2008 3:15 am
Location: Michigan, USA

Re: 2D Modular Sprite Engine (Spriter Pro)

Post by D Ogre »

I'm still ironing out bugs and implementations. As I do so, test away! I want to completely nail this... :)

New Update above. Here's and update with some bug fixes. Well, hopefully.
There are still some problems to work out, but I'll get there...
User avatar
IceSoft
Addict
Addict
Posts: 1738
Joined: Thu Jun 24, 2004 8:51 am
Location: Germany

Re: 2D Modular Sprite Engine (Spriter Pro)

Post by IceSoft »

Sounds works better now.
New issue:
Pressing [SPACE] => The sound is still running

@D Ogre,
Please copy all new versions into the first entry.
Its easier to find and please remove/delete the wrong code snippets too.
Good work as far.
Puzzle of Mystralia (C++)
Bug Planet, Waponez III, =QONK=, PetriDish, Movie2Image
<Wrapper>4PB, PB<game>, PictureManager,...
D Ogre
User
User
Posts: 36
Joined: Tue Jan 01, 2008 3:15 am
Location: Michigan, USA

Re: 2D Modular Sprite Engine (Spriter Pro)

Post by D Ogre »

I'll take a look at the sound handling code later this evening. I think I may have simply left out a SoundStatus() and PauseSound()/ResumeSound(). Lol.
Post Reply