How to create an Animation Sprite Clip with programmatically created Sprite?

I have been trying to create an Animation Sprite Clip from a programmatically created sprite. The problem is that the animation sprite clip requires an asset id to create the animation. The sprite component has no name attribute so I cannot search with asset.find(). Setting the sprite after creating the clip is just ignored by the engine.

I tried load/add the asset in case it wasn’t done by default. Got errors doing that so I assume that it is.
And with the off chance that shoving the sprite into the slot would work, it put the sprite into the sprite asset spot. Does not render anything. and the _Sprite is null. The only way I can assume that it works is with the asset Id which I cannot seem to get.

I did create an animation clip and got it running without a programmatically created sprite but doesnt seem to work with the extra step.

For completeness sake error messages for add, and load.
add:


load:

Example json values:

	"frame": {"x":0,"y":0,"w":201,"h":324},
	"rotated": false,
	"trimmed": true,
	"spriteSourceSize": {"x":704,"y":351,"w":1149,"h":675},
	"sourceSize": {"w":1149,"h":675}

Currently have:

    var piv = new pc.Vec2(0.5, 0.5);
    var Atlas = new pc.TextureAtlas();
    Atlas.texture = this.SpriteSheet;
    
    var i = 0;
    
    Atlas.frames = {};
    var FrameKeys = [];
    
    
    frames.forEach(function(element)
    {
        FrameKeys.push(i.toString());
        Atlas.setFrame(FrameKeys[i], 
        {
            //element.frame is from a json file.  Can just fill in any set of values here to make it work.
            rect: new pc.Vec4(element.frame.x, element.frame.y, element.frame.w, element.frame.h),
            pivot: piv,
            border: new pc.Vec4(5, 5, 5, 5)// Left as example since wasnt 100% certain what it does for me.
        });
        
         i++;
    });
    
    var Sprite = new pc.Sprite(this.app.graphicsDevice, 
        {
            pixelsPerUnit: 1,
            renderMode: "SPRITE_RENDERMODE_SIMPLE", //Doesn't seem to exist on pre-generated sprites.
            atlas: Atlas,
            frameKeys: FrameKeys
        });
    

    console.log(Sprite);
    

      var clip = new pc.SpriteAnimationClip(this.Test.sprite, {
        name: "g",
        fps: 2,
        loop: true,
        spriteAsset: Sprite
    });  
    
    clip.sprite = Sprite;
     this.Test.sprite.addClip(clip);
    this.Test.sprite.play("g");

Having a very quick look at the code, you may be able to set the sprite property directly: https://developer.playcanvas.com/en/api/pc.SpriteAnimationClip.html#sprite

Thanks for the help, but I tried that previously before as you can see at the bottom of the sample code. But went back to check the clip data again as I checked the current playing data to see if I missed something. It is applied on the clip data, but as soon as it is added to the animation clip of the test character it seems to disappear. Which is why I thought adding the sprite to the variable ignored it before since I only checked the end result.

Console Log of clip:

Console log of the entity that has the clip:

Update on the issue, add the sprite to the clip after it was added to the entity.

The sprite could not be rendered due to rendering errors right now.
image

In the playcanvas-stable.dbg.js file on line 15217

It is showing for the error that the numTextures is being set incorrectly.
image

As it has my texture listed here under file:

Which might be a bug due to not having a length variable attached to sampleValue.
image

So for right now at least I do not think it is possible to create a sprite animation programmatically.

Updated code section:

      var clip = new pc.SpriteAnimationClip(this.Test.sprite, {
        name: "g",
        fps: 2,
        loop: true,
     //   spriteAsset: Sprite
    });  
    
    //clip.sprite = Sprite;
    
    console.log(clip);
    
    this.Test.sprite.addClip(clip);
    this.Test.sprite.clip("g").sprite = Sprite;
    this.Test.sprite.play("g");
    console.log(this.Test.sprite);

I seem to solved my issue with the WebGL rendering. Turns out the texture in the texture atlas cannot take a texture asset as well as the sprite also couldnt take a texture atlas asset. Only a texture data / texture atlas structure. So I had to create a work around to get around these issues. Had to take in a texture atlas asset through attributes. And transfer its data to the texture atlas object. As it had direct access to the texture object. Than passing down that data until the sprite asset. Since the sprite data was not saved it is required to be saved into the clip directly.

Final Code to fix this issue:


AnimationFromSheet.attributes.add('Atlas', {type: 'asset', assetType: 'textureatlas',});
AnimationFromSheet.attributes.add('json', {type: 'asset'});
AnimationFromSheet.attributes.add('Test', {type: 'entity'});

// initialize code called once per entity
AnimationFromSheet.prototype.initialize = function() 
{
    var frames = Object.values(this.json.resources[0].frames);
    
    var piv = new pc.Vec2(0.5, 0.5);
//--- Final fix
    var Atlas = new pc.TextureAtlas();
    Atlas.texture = this.Atlas.resources[0]._texture;
//----
    
    var i = 0;

 
    Atlas.frames = {};
    var FrameKeys = [];

    frames.forEach(function(element)
    {
        FrameKeys.push(i.toString());
        console.log(element.frame.x);
        
        Atlas.setFrame(FrameKeys[i], 
        {

            rect: new pc.Vec4(element.frame.x, element.frame.y, element.frame.w, element.frame.h),
            pivot: piv,
            border: new pc.Vec4(5, 5, 5, 5)
        });
        
         i++;
    });
        
    var Sprite = new pc.Sprite(this.app.graphicsDevice, 
        {
            pixelsPerUnit: 100,
            atlas: Atlas,
            frameKeys: FrameKeys
        });

      var clip = new pc.SpriteAnimationClip(this.Test.sprite, {
        name: "g",
        fps: 30,
        loop: true,
    });  
    
   //Final fix
    this.Test.sprite.addClip(clip);
    this.Test.sprite.clip("g").sprite = Sprite;
//---
    this.Test.sprite.play("g");

 
};
1 Like

Nice work, thanks for posting your solution!

Cross reference of a similar issue: How to spawn a sprite/animated sprite using code?

This is how I’ve created all of the needed animation clips and assigned their values. I can add X amount of clips with no problem now.

// Initialize all of the clips and assign their animations
AnimationComponent.prototype.initializeClips = function () {
    this.animPlayer = this.entity.findComponent('sprite');  // Find the sprite component inside entity

    let charAnimationsJSON = this.characterJSON.resources;  // Load all animations from JSON file
    for (const i in charAnimationsJSON) {
        let charAnimationJSON = charAnimationsJSON[i]; // Load a single JSON animation
        let charSpriteAnim = this.app.assets.get(charAnimationJSON.animation).resource; // Set sprite animation

        // Create animation clip
        let spriteClip = pc.SpriteAnimationClip(charSpriteAnim, {
            name: charAnimationJSON.name,
            fps: charAnimationJSON.frames,
            loop: charAnimationJSON.loop,
        });

        this.animPlayer.addClip(spriteClip); // Add the clip
        this.animPlayer.clip(charAnimationJSON.name).sprite = charSpriteAnim; // Set the clip sprite (again?)
    }
};

The JSON file just holds the animation name, frames, loop and id of the sprite animation.

1 Like

Should line 11 be this instead?:

// Added `new`
let spriteClip = new pc.SpriteAnimationClip(charSpriteAnim

@yaustar I thought so too but that generates some weird errors that can be seen in an image below (this is with: let spriteClip = new pc.SpriteAnimationClip(charSpriteAnim).

Without it it works just fine. Even put a console log to show all the animations that the sprite has and it did generate all of them. Here is an image without new.

Would need to see a test project to really check. I can’t see anything offhand that would trigger that error in the engine code

@yaustar Here you go, created a new blank project with only AnimationComponent and ‘Idle’ sprite.

AnimationComponent

Fixed version: PlayCanvas 3D HTML5 Game Engine

// Initialize all of the clips and assign their animations
AnimationComponent.prototype.initializeClips = function () {
    this.animPlayer = this.entity.findComponent('sprite');  // Find the sprite component inside entity

    let charAnimationsJSON = this.characterJSON.resources;  // Load all animations frm JSON file
    for (const i in charAnimationsJSON) {
        let charAnimationJSON = charAnimationsJSON[i]; // Load a single JSON animation
        let charSpriteAnim = this.app.assets.get(charAnimationJSON.animation).resource; // Set sprite animation

        // Create animation clip
        let spriteClip = new pc.SpriteAnimationClip(this.animPlayer, {
            name: charAnimationJSON.name,
            fps: charAnimationJSON.frames,
            loop: charAnimationJSON.loop,
        });
        
        this.animPlayer.addClip(spriteClip); // Add the clip
        this.animPlayer.clip(charAnimationJSON.name).sprite = charSpriteAnim; // Set the clip sprite (again?)
    }

    console.log(this.animPlayer.clips);
};

You have to pass the sprite component to the constructor of pc.SpriteAnimationClip

1 Like

Ahhh, I thought that I have to pass the sprite and not the spriteComponent. Don’t know why my version also worked though but thanks for fixing it :slight_smile: .

Have a great day!