Hello,
The context here is a 2D game using the paperZD plugin. My overall goal is to change the color of an actor (represented by sprites as a paperzd character) to change from color to black and white, from top to bottom, based on some input. For example, as the health of the actor goes from 100% (full color) to 75%, the top 25% of the actor would be black and white, while the bottom 75% would remain color.
I am able to get this to work for a single sprite with a single texture, where the sprite takes up the entire texture. The material to do this is below.
For this simple case, ignore the vMin and vMax inputs, those stay as defaults. The only parameter I pass in is Progress
, which is between 0 and 1. So that’s fairly straight forward and works fine.
Now I have a sprite map. That is, a texture with many rows, where each row contains a number of sprites as an animation. Example:
The problem is that that UV coordinates coming into the above material are based on the entire texture, not the individual sprite that is currently showing. So for the first row of a texture with 3 rows, my V coordinates will go from 0 to 0.33. Of course the material doesn’t know this, and it doesn’t know how many rows there are, so it can’t remap 0 → 0.33 to 0 → 1 (because it doesn’t know the 0 and the 0.33 parts).
So we can tell it what this are. These are the vMin and vMax parameters coming into this material. We pass in vMin = 0 and vMax = 0.33, then we remap the Vgradient from the range {0, 0.33} to the range {0, 1}. And everything works again.
(Note that I am basically ignoring the U parameter here, as that doesn’t matter.)
Now, each row in a sprite map has different minV and maxV parameters. For the sake of arguments, let’s say my first row is the idle
animation and my second row is the walk
animation. (And let’s assume there are no others.)
Since the idle
animation is set on the character, the OnBegin
I get the flipbook, I get the sprite in the first frame (all sprites in one row have the same vMin and vMax), I extract the relevant data to calculate the minV and maxV (this had to be written as a C++ function).
FVector2D USnowHelperFunctionLibrary::GetSpriteUVMinMax(UPaperSprite* Sprite)
{
// Texture containing all sprites
const UTexture2D* Texture2D = Sprite->GetSourceTexture();
// Height of the texture
const int32 TextureY = Texture2D->GetSizeY();
// Sprite size
FVector2d SourceSize = Sprite->GetSourceSize();
// int x = SourceSize.Component(0);
// Sprite height
const int y = SourceSize.Component(1);
// This should always divide evenly
// Number of rows in texture; each row contains a set of sprites
const int Rows = TextureY / y;
// Height of each row in UV space
const float RowHeight = 1 / static_cast<float>(Rows);
// Source UV
FVector2d SourceUV = Sprite->GetSourceUV();
const int SourceV = SourceUV.Component(1);
// Row = sourceV / source Y dimension + 1
const int Row = SourceV / y + 1;
// MaxV = RowHeight * Row
const float MaxV = RowHeight * Row;
// minV = maxV - RowHeight
const float MinV = MaxV - RowHeight;
return FVector2d(MinV, MaxV);
}
Then I set these parameters on a dynamic material instance:
Now my idle
animation works as intended. As soon as the character begins to walk though, this breaks down because now the minV and maxV have changed. The problem I am having is that I can’t seem to figure out how to update the minV and maxV values for the currently playing animation (within the context of using paperzd). I can’t seem to find how to get the currently playing flipbook so I can update the parameters.
This function provided in the Paper ZDAnim Instance looked promising:
but, as above, I can’t seem to get the sprite so that I can update the material.
I have two questions then. The first is, for this specific case, how can I get the currently playing sprite so I can update the material parameters?
The second is, how can I do this better?
Obviously I think I can create a dynamic material instance per sprite, and, at startup, populate the parameters for all of them. Perhaps this is the right way to do it as it’s done once. It just requires keeping track of all the flipbooks being used… and eating whatever the time is to do this. Of course then I end up with as many dynamic material instances as I have characters on the screen, but I think this is already true in my case and the number of characters if < 30 in general.
But perhaps there is a better way to do all of this that I am missing enitirely?
Thank you for reading