- Modificato
[UE4] Any idea how to change color?
The SpineSkeletonRender Component seems can not change material parameter due to design (I assume).
According to this, in UE4, if you want to modify parameters in runtime, you have to create MIDs in BP, then assign to materials.
But all of the materials for the Render Component are ready only.
Even if I've changed to BlueprintReadWrite it still won't work because it seems that it will create MIDs in every ticks.
So it is not possible to change parameters in runtime thus changing color is impossible.
If I was right on this, then I want to request either a MIDs compatible Render Component or a color changing feature.
If I was wrong, please tell me how to use MIDs with SpineSkeletonRender Component.
The only way I can think of is that use spine to create a new animation, change slot colors and then play it on the other track.
Thank you!
Update, so here is the workaround.
Add these line in SpineSkeletonRendererComponent.h
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
FLinearColor VectorParameter;
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
FName VectorParameterName;
Then initialize these variables in constructor of SpineSkeletonRendererComponent.cpp
VectorParameterName = FName(TEXT("TintColor"));
VectorParameter = FLinearColor(1.0f, 1.0f, 1.0f);
Now find all the MIDs created in Tick Component, add this line below every SetTextureParameterValue.
material->SetVectorParameterValue(VectorParameterName, VectorParameter);
Finally, add these lines below UpdateMesh(skeleton->GetSkeleton()); inside TickComponent so it can update parameters every frame.
for (int i = 0; i < skeleton->Atlas->atlasPages.Num(); i++)
{
atlasScreenBlendMaterials[i]->SetVectorParameterValue(VectorParameterName, VectorParameter);
atlasNormalBlendMaterials[i]->SetVectorParameterValue(VectorParameterName, VectorParameter);
atlasAdditiveBlendMaterials[i]->SetVectorParameterValue(VectorParameterName, VectorParameter);
atlasMultiplyBlendMaterials[i]->SetVectorParameterValue(VectorParameterName, VectorParameter);
}
Then it'll work.
I've attached both .h and .cpp file in case someone else encountered this problem.
Documents.zip
Here is my BP setup:
And the video:
12.zip
Thanks for your feedback! You are right that it's not obvious how to set a skeleton's tint color. In C++, you can fetch the spSkeleton and modify its color, which as I said, is none obvious. In BP, it's worse, as spSkeleton is not a UCLASS or USTRUCT, so you can't directly access it.
I've created an issue for this here [ue4] Expose skeleton color for tinting in BP & C++ API · #857
Please keep the feedback on the UE4 runtime coming! It's a very young runtime, and we have to rely on your feedback to make it better!
I've now implemented this feature. The SpineSkeletonRendererComponent
has a new UPROPERTY Color
, a simple FLinearColor
. Its value is set on every tick on the underlying spSkeleton
of the SpineSkeletonComponent
and allow you to tint the skeleton. The property is exposed for read/write in both the editor as well as in C++ and BP.
See this commit [ue4] Added Color property to SkeletonRendererComponent. The value of…@6d9dcae
Thanks for the feedback, keep it coming!
So apologies for the necro here, but this is becoming a real thorn in my side too.
I've got chromakeying built into the materials for more granular color control, but without the ability to generate and swap to MIDs, this is ultimately useless. Even modifying the material properties to BlueprintReadWrite does not allow the materials to be swapped with MIDs at runtime, which is tremendously frustrating if you don't want to change the entire skeleton's color. I'm not looking to rewrite the plugin here, so a little insight into where else the source needs to be modified to bubble even a simple setter up to the Blueprint editor would be most welcome.
Everything related to materials is contained in SpineSkeletonRendererComponent
spine-runtimes/SpineSkeletonRendererComponent.cpp at 3.7-beta-cpp
The UE4 Spine plugin comes with a bunch of predefined materials for the blend modes that are supported.
On every tick, the component checks if there was a change in the texture atlas the rendering component references and re-generates a new dynamic material instance:
spine-runtimes/SpineSkeletonRendererComponent.cpp at 3.7-beta-cpp
If the atlas has not changed, we check if the current texture parameter of each of the dynamic material instances has changed and re-generate the dynamic instance.
spine-runtimes/SpineSkeletonRendererComponent.cpp at 3.7-beta-cpp
All this might actually not be a UE4 best practice. If you have a suggestion how to make this more idiomatic I'd be super happy. I've opened an issue here [ue4] Support dynamic material instances on SkeletonRendererComponent · #1210
Haven't had my metaphorical coffee yet, but this seems fairly sensible knowing what has to go on behind the scenes; it just doesn't reconcile very nicely with the "usual" workflow for modifying material parameters at runtime. I'll give it some thought today and report back on that issue page.
Thanks for the quick response!
Alright, having gotten a chance to review the plugin source clear-headed, I'm going to discuss here before taking proposals to the issue page.
The current implementation of the sprite materials is about as sensible as doing 2D in Unreal is anyway (joking, joking), so I see no good cause to change it much. And rather than exposing the arrays of generated dynamic instances directly to the editor, what I'd suggest is a handful of new functions (one each for scalar, vector, and texture parameters) in the Renderer component, adapting the typical workflow to the circumstances. I've mocked one up, in Blueprint for simplicity's sake:
This approach seems like the path of least impact and resistance to me. Set X Parameter Value calls fail silently if the named parameter doesn't exist, and passing a map naturally mitigates any risk of accidentally double-setting a parameter value in a single call, while also allowing the updating of as many parameters as is required.
Thoughts?
I think we can work with this. I'm undecided on whether we want to set a list of parameters, or single parameters in one call. I'm leaning towards the latter.
The SpineSkeletonRendererComponent would "cache" the set parameters and apply them on each tick. That way you only have to set the parameters once, and if the MIDs inside the component get recreated, the parameters will still be applied.
I'd probably call the function "SetMaterialParameter(BlendMode, Name, Value)".
There should also be a "ClearMaterialParameter(BlendMode, Name)" that will remove that parameter from being applied to the MID(s).
Thoughts?
The only hitch I can see in that implementation is the fact that a single "monolithic" function would need a way to take in and intelligently pass different data types to the MID's Set Scalar/Vector/Texture Parameter Value methods. Granted, Texture could potentially be skipped, because I'm having a hard time imagining a practical use case for it in this context, but that doesn't necessarily mean there isn't one.
Either approach works for me, since they both get the job done; it's just a question of how many inputs you want to manage and much programmatic switching you want to do. Not insurmountable by any means, but that's why a wrapper around each struck me as the quickest way over the mountain.
I think we could just mirror the API that's exposed by MIDs, with the additional BlendMode parameter to select specific material(s).
The alternative is to expose the MIDs we generate directly. Upside: full API access to whatever MIDs provide. Downside: you'd have to reset the parameters when the MIDs change. In 99% of cases, they shouldn't change at all, as it's unlikely that you'll change the atlas at runtime.
Not sure which is the best approach. The second option is far more powerful, but with that also comes some responsibility
Maybe a hybrid approach, exposing the MIDs and implementing a callback for whenever they change to catch anything that might slip through the cracks? I'd pass an array of blend modes (any MIDs that changed) through this event to allow automatic handling of parameter value setting.
That leaves the majority of this to be handled by the engine API natively, and cuts down special case risk, I think.
Yeah, that was my thinking as well. The more I can push on the UE4 API on the better. Let's go with that! It will be some time before I can get to work on this. The corresponding issue on the tracker is here [ue4] Support dynamic material instances on SkeletonRendererComponent · #1210
I've been working on this some myself. I've gotta do a proper build-out of part of our pipeline to test my implementation, but this is promising:
This looks great! Sorry, I didn't find the time yet to implement this, as there are so many things in our pipeline at the moment. I'd be happy if you send us a pull request on GitHub!
No worries, still working myself. I'll ship it over when I'm satisfied that it actually works the way we expect it to.
Cheers!
In the interest of thorough testing, can you think of a scenario that would surely trigger a material regeneration? So far, material parameter values have persisted across art swaps spanning different atlas pages, but I want to be sure about this.
Changing the reference to a skeleton data or atlas data asset should do the trick. However, I don't see any real world usage scenarios where you'd actually want to do that.
Agreed completely. It'd most likely crash the engine, and that, in my mind, would actually make the callback overkill.
And if that's the case, all that's really necessary is moving the material instance arrays to public namespace. I'll ship out a pull request after work.
Awesome, thanks!