Jump to content

How to create many color variants of a complex animated sprite and then render them all


timetocode
 Share

Recommended Posts

Hello!

 

the tldr;

  1. How do I do color replacement of sprites in pixi? (not tinting, e.g. change #ff0000 to #cccccc)
  2. How do I best render 50+ characters made out of 6-10 sprites (~100 frames total), each of which has been recolored by whatever the answer is to #1 (with performance in mind)

The long version, with images and examples:

 

I am porting a canvas-centric project of mine to pixi, and am unsure how to re-implement my former character generator.

 

I have these small pixel art characters that, in my former game, would all be recolored at runtime. Their skin, hair, gear, etc could be any color. They also had a set of animations, somewhere in the 60-100 frame range. To accomplish this, I had a base template character cut up into many pieces (head, hair, torso, feet, arms, misc gear, etc) which I would use to perform an expensive per-pixel color replacements. All in all, these were slow to generate (~250ms per character) but then I would save these recolored canvases and use them as spritesheets resulting in lagless animations and no limit on the colorization options. So rather than loading a spritesheet as most games do, I was loading several spritesheets or individual images, and then at load/runtime I was *generating* a geared+colored spritesheet per character. If someone redyed their gear I had to generate a new spritesheet for whatever they changed.

tumblr_msuypqbAcm1rnay8no1_400.png

Here's an example of a randomly generated set of characters. Keep in mind that this is just a sample of 3 frames from each sprite sheet -- every character has another 60-100 frames saved in memory representing run and attack animations in four directions. 

 

Here's one geared character in a run animation (not sure if it'll animate in the forum, so a link follows): 

tumblr_inline_mv4ch7NXVG1r61t4c.gif

 

http://timetocode.tumblr.com/post/64857650654/sample-armor-animations

 

There appear to be many options of how to accomplish something similar in pixi v3, but I keep running into performance issues so I wanted to solicit some feedback before I sink in too many more hours.

 

So far with pixi I am able to draw a basic uncolored character composed of 6 sprites with no performance problems. I can even add 15000+ of them to the screen and have them run around at 60 fps. I suppose this makes sense, I'm just drawing straight from a spritesheet in this scenario just like bunnymark.

 

Where I run into issues, is in colorization of the characters. I assume that in webgl the solution is a custom shader that replaces my template colors with whatever I want (is it?). I'm not sure how to make such a shader yet, but to test performance I tried using the InvertFilter and the SepiaFilter, as I figured these fundamentally performed the same task (recoloring pixels based on their existing value). In the scenario of randomly assigning a Sepia or Invert filter to each body part of my characters, I experienced a drop in renderable characters from 15000+ to a couple hundred. Removing shaders from some, and adding tinting to others (which I do not plan to use, because it washes out colors) further degraded the frame rate. If I were to add as much gear and variations as I have planned, the game wouldn't actually run with my current naive rendering approach. Now I know pixi has many tricks to use at this point, but I don't know which ones to use, or even if I've heard all my options yet.

 

Could someone give me some options? Or let me know if any of the following options have merit:

  1. revert to using canvas to generate a per-pixel colorized + geared in-memory spritesheet of each character, and then render the character in pixi.js via webgl (I can do that right? use an offscreen canvas and still use webgl..?)
  2. write the color-replace-shader, use some pixi feature (cache as bitmap? render texture...?) to create the spritesheets (would this even work? can i capture the color changes from rendering with the shader and then save that as an image?)
  3. generate a finite number of skin/hair/gear colors in a drawing program and save them as spritesheets, don't make full character spritesheets, just draw the bodyparts where they need to go, don't do any fancy character generation

Is it true that if I do somehow procedurally generate ~50-150 different colorized spritesheets (to represent ~50 unique characters on the same screen) that I may still have a performance problem because pixi would be rendering *from* too many different images? Or should i expect awesome FPS in this scenario?

 

Sorry for the complicated question, but I would really love to leave my old canvas game behind and move to pixi!

 

Thanks for reading

 

Link to comment
Share on other sites

How are you colorizing things, using the `tint` property? That should just be recoloring in the shader.

 

but to test performance I tried using the InvertFilter and the SepiaFilter, as I figured these fundamentally performed the same task

 

No, they do not perform similar tasks, using a filter will remove that sprite from the batch and use a custom shader. The normal sprite shader has support for `tint`, and doesn't break batching.

 

http://pixijs.github.io/docs/PIXI.Sprite.html#tint

Link to comment
Share on other sites

Hello xerver :)

 

 

Well for the images of my little characters, those were recolored via a 2d context. This comes from the 'old' version of my character generator which has a template spritesheet with key for which colors from the template represent which parts of the character (e.g. hairColor 255, 255, 255, skinBaseColor 100,100,100, skinHighlightColor, etc etc). I then generate a new color theme, with stuff like.. newSkinColors = generateOrcSkin() // randomly generates a green orc color theme. It all boils down to procedurally creating a new spritesheet (a canvas) where every color from the template has been replaced with new colors, and all of the gear has been pasted onto the character. In the end this means that my game generates a unique spritesheet for every player, because their character is very customizable. Maybe that is ridiculous?

 

Now I'd like to do something very similar via pixi.js and webgl. From what I've been reading... perhaps I can write a shader like this one:

 

Color replacement shader (is it webgl? i don't know.. found it on a regular open gl forum)

uniform sampler2D texture;uniform vec3 color1;uniform vec3 replace1;uniform vec3 color2;uniform vec3 replace2;void main(){	vec4 pixel = texture2D(texture, gl_TexCoord[0].xy);	vec3 eps = vec3(0.009, 0.009, 0.009);	if( all( greaterThanEqual(pixel, vec4(color1 - eps, 1.0)) ) && all( lessThanEqual(pixel, vec4(color1 + eps, 1.0)) ) )		pixel = vec4(replace1, 1.0);		if( all( greaterThanEqual(pixel, vec4(color2 - eps, 1.0)) ) && all( lessThanEqual(pixel, vec4(color2 + eps, 1.0)) ) )		pixel = vec4(replace2, 1.0);	gl_FragColor = pixel;}

And then maybe make use of pixi's render texture? Logistically this sounds like it would produce the same result as my old 2d canvas system but wit the added benefit that the GPU did some of the work. Instead of doing color replacements in canvas, I would use the shader, and instead of using an offscreen canvas as the new spritesheet, I would use a render texture. I guess I would also have to define where each frame ended up on this texture for use in animations, so that I could control the characters with functions like faceLeft() and playWalkLeftAnimation()... which would be a bit tedious but I'd only have to do it once and then it would work for everything.

 

As it stands currently, if a player dyes their armor in my (old) canvas version of the game, the game will lag for about 250ms (a visible stutter) as it generates a new spritesheet. I hope that a pixi+webgl version would reduce this character generation lag. I'm also open to using a webworker, if I can figure out which part of the calculation would best benefit from it (maybe none... no access to the dom from a worker).

 

Does this sound doable? I'll be prototyping this across the next few days. Tips/recommendations appreciated

 

Another note, even though my spritesheet ends up with ~60 frames on it... the whole thing is pretty tiny.. like the character's head for example is about 20x14 pixels, and each frame is made out of ~6 segments this size layered together.

 

Here's 150 characters rendered in 6 pieces each with a random sepia or invert filter applied to each piece (60 fps! not bad!). None of it was cached/spritesheeted in this test.

tumblr_nrrcgdK4Na1rnay8no1_400.png

Link to comment
Share on other sites

You could do a color replacement filter, but it will break batching. Instead you could have your sprite sheet be all the "parts" of a character then just compose a character of multiple tinted sprites. That removes the need to generate dynamic spritesheets and keeps batching working properly.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...