Page 2 of 2

Re: 2D Modular Sprite Engine (Spriter Pro)

Posted: Fri Feb 20, 2026 9:07 am
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

Re: 2D Modular Sprite Engine (Spriter Pro)

Posted: Fri Feb 20, 2026 7:35 pm
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

Re: 2D Modular Sprite Engine (Spriter Pro)

Posted: Sat Feb 21, 2026 6:22 pm
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


Re: 2D Modular Sprite Engine (Spriter Pro)

Posted: Fri Feb 27, 2026 12:59 am
by D Ogre
^^^ Moved Spriter Demo Viewer to the top of post. ^^^

Re: 2D Modular Sprite Engine (Spriter Pro)

Posted: Fri Feb 27, 2026 6:53 am
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

Re: 2D Modular Sprite Engine (Spriter Pro)

Posted: Fri Feb 27, 2026 7:38 am
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.

Re: 2D Modular Sprite Engine (Spriter Pro)

Posted: Fri Feb 27, 2026 8:43 am
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."

Re: 2D Modular Sprite Engine (Spriter Pro)

Posted: Fri Feb 27, 2026 9:27 am
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">

Re: 2D Modular Sprite Engine (Spriter Pro)

Posted: Fri Feb 27, 2026 7:31 pm
by D Ogre
New version of Spriter Demo posted above. 8)

Re: 2D Modular Sprite Engine (Spriter Pro)

Posted: Fri Feb 27, 2026 8:53 pm
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

Re: 2D Modular Sprite Engine (Spriter Pro)

Posted: Sat Feb 28, 2026 6:09 am
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>

Re: 2D Modular Sprite Engine (Spriter Pro)

Posted: Sat Feb 28, 2026 4:05 pm
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...

Re: 2D Modular Sprite Engine (Spriter Pro)

Posted: Sat Feb 28, 2026 6:17 pm
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.

Re: 2D Modular Sprite Engine (Spriter Pro)

Posted: Sat Feb 28, 2026 8:44 pm
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.