▶ I2 SmartEdge◀ [Support Thread] ---(Pixel Perfect EFFECTS and ANIMATIONS for your Texts & Images)

Hi!
This is the support thread for I2 SmartEdge!

Feel free to post any question or suggestion you have!

You can also send me an email, post in the official inter-illusion forum or add comments in the Roadmap page!

I2 SmartEdge is a plugin that integrates with Unity UI, NGUI and TextMeshPro and allows:

  • Pixel Perfect Rendering of Text and Images using SDF and MSDF assets
  • Lots of Render Effects (Glow, Reflections, Normal Maps, etc)
  • Per-Character Animations
  • Free-Transform using spliens like in Photoshop!
  • Enhanced text (Justification, Pages, Modify Case, …)


Here is a demo of the plugin in action: WebGL DEMO
More details about its features: Documentation
Roadmap of future features: Vote here on the features you like!

I’m also posting [here] updates about each new feature I implement!

We currently use TextMeshPro, but would love to remove it. Basically, we don’t really use any of it’s fancy effects, and it eats memory for breakfast, creates lots of GC allocations, polls in LateUpdate, etc. We’ve heavily modified the code to remove about 35mb of memory use, and could continue to reduce it further, but honestly it’s just frustrating.

What we would really like is the ability to use SDF and MSDF fonts in Unity’s Text components with no additional overhead. That means no additional GC allocations at runtime, no Update or LateUpdate functions, and reasonable memory use for a large number of text components (think thousands), and preferably no giant hitches when canvas’s rebuild (beyond the ones that Unity already has).

How is smart edge on this front? I notice you add a component to the UGUI text component, what does it do in terms of allocations, work, etc?

Hi!

Sorry, SmartEdge is greatly about fancy Effects :slight_smile: But they don’t eat memory for breakfast!

I have tried to pool as much as possible so that there are no GC allocations. There is even a material manager that uses and reuses the materials so that you don’t have to manually manage that.

Nonetheless, there is still another pass I want to do right after I finish the Extended RichText support to consolidate all the buffers SmartEdge needs, so that the memory used should be far far less than now (and now it doesn’t use much!)

SmartEdge behaves differently depending on to where it integrates (Unity UI, NGUI, etc).

For Unity UI, it works as a VertexModifier. So, there is no need to replace any Unity UI component. Just add an SmartEdge component and voila! You have access to lots of effects, animations, deformations,…

Said that, there are a few constraints. And the version of SmartEdge currently in the AssetStore does have a LateUpdate function!

The problem is that Unity doesn’t inform anyone whenever the component changes! When in the editor you have a validation method, but that doesn’t work in game.

So, (to improve Stability in the first couple releases) I had to temporally add an Update function to validate some settings so that if the developer changes any of them, I could enable the correct counter-part in SmartEdge (e.g. bestFit its replaced with a new one, alignment should be Left when Justification is enabled in SmartEdge, if the text changes the RichText tags should be parsed, etc).

But that was temporally! In the latest version (1.0.2a1) which is in the beta folder, the Update function was replaced by a coroutine that only runs a few times a second and the SmartEdge component register there IF they need it (e.g. if using SDF and bestfit, if enabling richtext, etc). But only then, it runs a few frames per second and early out most of the work!

In there roadmap, I have scheduled time to write a replacement for the Unity UI Text component, (that will allow me to avoid any polling! have dynamic SDF fonts, etc) Nonetheless, the option of use either the SmartEdge component or the new Text object will still be there and will be up to the developer.

BTW, if in the current AssetStore version you comment the Update function in SmartEdge.cs and avoid changing the settings validated there, it will work just as fine!

Hope that helps, and please, if you see any issue or have any suggestion on how to improve the plugin for better fit your game, just let me know! I love suggestions!!!

1 Like

BTW, I went and double checked all the validations that I needed to execute using the coroutine, and I was able to refactor the code and extend some event in a way that the validations now only run when things actually change.

No need for a coroutine validating a few times per second!!!

Now the only coroutine that its executed is for Updating the Animations. And even that, is done using a manager where the SmartEdge components register them self. No need for having a costly update method in the SmartEdge code, and only those components PLAYING an animation get registered and updated! Tons of performance savings!

Hi there,

Do you have an idea of when b2 will be available with the capitalization options? I think I may use SmartEdge’s capitalization features rather than the ones on I2 Localization’s Localize component. No rush, just checking, thanks.

I just uploaded 1.0.1b2 to the beta folder. Which contains TONS of other things as well! (Convert Case, Pages, Optimizations and a few other things!)

Also, I will be uploading 1.0.2a1 later tomorrow. The big change in this new alpha is that RichText tags are working:

Hope that helps!
Frank

1 Like

Hi Frank,

I’m trying to get some animations to play and am having some problems. I want text to be transparent until I start the animation, which fades in the alpha of each character using the separation value in the text animation.

The problem is, I can’t set the text or SmartEdge face color to be transparent at scene start, as otherwise the animating the alpha values via the animation asset has no effect. The only way I can think of getting this to work is to create another animation where the text alpha goes from 0 to 0, and play that at scene start, then play the fading in animation whenever I want. Is this the only way to do this?

If I do that, I have two animations, set to play Single:
Anim1 = Alpha from 0 to 0.
Anim2 = Alpha from 0 to 1 with some separation, duration 0.5f

If the duration of the Anim1 is 0.001f, the alpha stays at 1 and doesn’t move towards 0. If I set it to 0.01f, it works as intended, staying transparent. Is this a bug?

Even if I start Anim2 much later, let’s say a couple of seconds, after Anim1 (which is set to Single), do I still need to call StopAnimation(Anim1) before playing Anim2?

Another option I guess is setting a longer duration for Anim1 and using loop, and then stop the animation before playing the second.

I’m also having a little difficulty loading SE_AnimationPresets by code. I’m not sure how to set the animation asset at runtime into the SmartEdge component. Do I need to have an inspector reference to the animation asset in the SmartEdge component, then another reference in the script that I’m using to start the animation via PlayAnimation(SE_AnimationPreset)?

Thanks.

Hi,
As you know in the last week I have been refactoring how the animation system works and making it faster, simpler to use and consume less memory.
I just uploaded v1.0.2b1 to the beta folder, which contains a lot of new changes related to animation:

Please, try that version as it fixes some of the issues you mentioned.

That’s fixed now, in previous releases, if the alpha of the text was 0, then it skipped everything. However, animations could override that alpha.
In the new version, the processing is only skipped if the alpha is 0 AND there are no animations playing.

See it here in action: The text has alpha 0 initially, and the button is still able to play the animation.
2947587--218415--StartAnimation.gif

To play an animation you should use the PlayAnimation(…) method in SmartEdge.
That method can either accept a string with the name of the animation, or an animationPreset.

If you use the name, then the animation should be added to the Animations list.

If you use the AnimationPreset, then you should load manually the preset. You can do that by either having your preset object exposed in your script and assigning that preset, or by loading it from the resources.

Here are two examples of how to do that in code:

Exposing the animation in your script:

public class PlayAnimation : MonoBehaviour
{
 public SE_AnimationPreset _Preset;

 public void Start()
 {
 var se = GetComponent<SmartEdge>();
 se.PlayAnimation(_Preset);
 }
}

Loading the animation from resources:

public class PlayAnimation1 : MonoBehaviour
{
 public void Start()
 {
 var se = GetComponent<SmartEdge>();

 var preset = Resources.Load<SE_AnimationPreset>("MyAnimation");
 se.PlayAnimation(preset);
 }
}

Nonetheless, if you just want to run an animation when a button is pressed, do this:
2947587--218416--upload_2017-2-4_21-39-26.png

There you can see that the button, plays 2 animations, one by using the “Custom1” animation from the smartedge animations list, and the other by using an animation Preset.

Hope that helps,
Frank

1 Like

Hi Frank, thanks for the previous post. I’m using the latest beta now.

As far as playing animations via code, what I meant was it only works if I have the animations already set in the animation tab list in the SmartEdge component when calling via PlayAnimation(SE_AnimationPreset). I was inquiring about how to set that particular animation list at runtime. Sorry for not being specific.

Is there a way to retain/persist a value after the animation finishes? For example, in your previous post you showed that the text alpha is 0, and stays at 1 after the animation finishes.
Did you set the value manually in code via the OnAnimation_Finished callback? I could do that, but then if I have other animations on the same component, wouldn’t that callback be invoked whenever ANY animation in the list is finished?
It would be nice to have an option for the animation alpha to override and set the text component alpha after it finishes.

Also whenever changing some values in the animation asset, I tend to get Debug.Log messages about XML stuff.

Edit: I’m also getting 16B of garbage every frame as a result of SmartEdgeManager.UpdateAnimations() coroutine. Can you confirm?

Lastly, the latest beta has some unused variables (message in the console).

Thanks.

The behavior in the initial versions was that if the animationPreset was not found in an slot, a new slot was created automatically. But that its not a good idea, if you are applying random animations to the texts, as they will end up with lots of slots.

I modified the code to have internally a list of animations currently playing. That animation could be from an slot (one of the animations you define in the inspector) or one that you load dynamically and call PlayAnimation(preset)

The new changes are in 1.0.2 b2 ( I just uploaded it to the beta folder)

You can use either of the example codes from the previous post to play an AnimationPreset (even if its not in the Animations list of SmartEdge)

In the previous example, I just called an method in the OnFinish event. That event receives the animation that finishes so that you could filter which ones behave differently.
However, I agree an easier way of handling this will be … easier… :slight_smile: so I went and added a toggle that allows you set that.
OnFinish: SetAlphaToFinalValue


When that toggle is checked, the animation will override the Text.color value with the value at the end of the animation (including the blending mode and easing curve)
Notice that this is not available for all Sequence types, as some operate in the specific vertices (e.g. Alpha Outline, Scale, etc). Currently the sequences that has it are: Basic\Alpha\Global and Basic\Color. I will add this behavior to other sequences later on.

Its fixed now (1.0.2b2)

I’m not seeing that behavior, could you please send me some of those errors. Could it be that those are from old animations? If you modify them and save them, do you still get those errors?

1 Like

Frank, you’re awesome as usual with adding fixes and features.

I’ll be updating to 1.0.2b2 tomorrow, but just wanted to post one of the console logs, which happens when moving the “From” or “To” alpha value sliders even on a newly created animation, or when altering any value in the animation asset:

<?xml version="1.0" encoding="utf-16"?>

<SE_Animation xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=“http://www.w3.org/2001/XMLSchema” Name=“AnimTextFadeIn” _Playback=“Single” _Backwards=“false” _PlaybackTimes=“-1” _ExtraTimeFinal=“0” _ExtraTimePerLoop=“0”>

Thanks for letting me know, that wasn’t happening when moving the sliders of an animation stored in SmartEdge, but was indeed happening when editing an Animation Preset.

I just fixed that and an initialization issue I saw while testing it :slight_smile:
I re-uploaded v1.0.2b2.
Hope that helps,
Frank

1 Like

Hi Frank,
Been working with the animations some more, and have one possible bug report and one request:

  • I find that after editing an animation asset (for example a simple alpha fade), on first Play in the editor it doesn’t work as it should, often just disappearing without fading out, as if the changes weren’t registered. If I stop the editor and then hit Play again, it works as intended. I spent a while fiddling with various values before I discovered this method of having to press Play twice for the animations to work correctly.

  • When using separation and fading, currently, text fades in or out from left to right. It would be nice to have the ability for the characters to fade in or out from right to left as well.

Thanks.

Btw, all previous feature requests and fixes are working great so far.

Hi
I have been trying to reproduce the first issue you mentioned, but haven’t been able to.

I tried this:
1 Assigned an Animation Preset to one of the SmartEdge components.
2 Selected the Animation Preset
3 changed a few parameters
4 Clicked the Unity Play button to start playing the scene
5 Verified that the new parameters are applied
6 Stopped the game
7 Repeated 3 to 6 a few times but always the parameters where detected correctly.

Am I missing any step? What unity version are you trying?

The animation can be played left->right or right->front by selecting the “Backward” toggle.
2958261--219529--upload_2017-2-13_10-12-40.png

I just added that setting Per-Sequence as well, so that you could have two Alphas, one coming from the left and one from the right. Both at the same time.
But I have done soo many changes in the latest beta that I need a couple more days to verify that everything else works as expected. And will release the new beta right after!

I updated the inspector, and now there is Backward settings for the animation, and one for each of the sequences.


2958294--219537--BackwardsSequence.gif
Hope that helps,
Frank

1 Like

Hi Frank,

I’m using Unity 5.5.1p2. I deleted the animation presets and recreated them, and I’m no longer having the “Play twice” problem once I did that, thanks.

As far as the “backwards” option which I’ve played with a lot, I can’t get the effect to happen as I want, due to OnFinish: SetAlphaToFinalValue setting the alpha back to 1 I believe when the animation is played backwards.

In your example above with only the text “This is a new” fading in left to right, let’s say I duplicate just that part, and set the duplicate version to “Backwards”. Now, if I play the duplicated “fade-out” version, once the text fades out from right to left, the alpha doesn’t stay at 0 and returns to 1 when the animation is finished. This is not in the editor, but when the game is playing and the fade out animation is played. This happens regardless of whether I set “On Finish: SetAlphaToFinalvalue” or not, as I’m thinking the final value is “To” rather than “From”, although when it is set to “Backwards”, the final value should be “From”, no?

That’s now fixed.
I’m going to be uploading a new beta in a couple days, once I finish testing that all the shader improvements are working fine. Sorry for the delay!

1 Like

Hi Frank, on building my game, I’m getting the following error:

Shader error in ‘Hidden/Unlit/I2NGUI SmartEdge/SDF 3’: Program ‘frag’, error X4506: ps_4_0_level_9_3 input limit (8) exceeded, shader uses 9 inputs. (on d3d11_9x)

Compiling Fragment program
Platform defines: UNITY_NO_SCREENSPACE_SHADOWS UNITY_ENABLE_REFLECTION_BUFFERS UNITY_PBS_USE_BRDF1 UNITY_SPECCUBE_BOX_PROJECTION UNITY_SPECCUBE_BLENDING SHADER_API_MOBILE SHADER_API_DESKTOP UNITY_COLORSPACE_GAMMA

Also some obsolete warnings in 5.6 now.

I purchased this asset earlier and am attempting to integrate it into my project, but I am running across some errors. This in particular is with the animation system. None of the presets have any sequences saved in them and I am unable to save any sequences on my text objects. I am getting the following errors:

System.InvalidOperationException: There was an error reflecting type ‘I2.SmartEdge.SE_AnimSequence_Position’. —> System.InvalidOperationException: There was an error reflecting field ‘_AnimBlend_From’. —> System.InvalidOperationException: There was an error reflecting type ‘I2.SmartEdge.SE_AnimSequence_Position.eAnimBlendMode’. —> System.InvalidOperationException: Types ‘I2.SmartEdge.SE_AnimSequence_Position.eAnimBlendMode’ and ‘I2.SmartEdge.SE_AnimSequence_Float.eAnimBlendMode’ both use the XML type name, ‘eAnimBlendMode’, from namespace ‘’. Use XML attributes to specify a unique XML name and/or namespace for the type.
at System.Xml.Serialization.XmlReflectionImporter.GetTypeMapping (System.String typeName, System.String ns, System.Xml.Serialization.TypeDesc typeDesc, System.Xml.Serialization.NameTable typeLib, System.Type type) [0x00081] in :0
at System.Xml.Serialization.XmlReflectionImporter.ImportEnumMapping (System.Xml.Serialization.EnumModel model, System.String ns, System.Boolean repeats) [0x00072] in :0
at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping (System.Xml.Serialization.TypeModel model, System.String ns, System.Xml.Serialization.XmlReflectionImporter+ImportContext context, System.String dataType, System.Xml.Serialization.XmlAttributes a, System.Boolean repeats, System.Boolean openModel, System.Xml.Serialization.RecursionLimiter limiter) [0x00162] in :0
— End of inner exception stack trace —
at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping (System.Xml.Serialization.TypeModel model, System.String ns, System.Xml.Serialization.XmlReflectionImporter+ImportContext context, System.String dataType, System.Xml.Serialization.XmlAttributes a, System.Boolean repeats, System.Boolean openModel, System.Xml.Serialization.RecursionLimiter limiter) [0x00393] in :0
at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping (System.Xml.Serialization.TypeModel model, System.String ns, System.Xml.Serialization.XmlReflectionImporter+ImportContext context, System.String dataType, System.Xml.Serialization.XmlAttributes a, System.Xml.Serialization.RecursionLimiter limiter) [0x00000] in :0
at System.Xml.Serialization.XmlReflectionImporter.ImportAccessorMapping (System.Xml.Serialization.MemberMapping accessor, System.Xml.Serialization.FieldModel model, System.Xml.Serialization.XmlAttributes a, System.String ns, System.Type choiceIdentifierType, System.Boolean rpc, System.Boolean openModel, System.Xml.Serialization.RecursionLimiter limiter) [0x00fc0] in :0
at System.Xml.Serialization.XmlReflectionImporter.ImportFieldMapping (System.Xml.Serialization.StructModel parent, System.Xml.Serialization.FieldModel model, System.Xml.Serialization.XmlAttributes a, System.String ns, System.Xml.Serialization.RecursionLimiter limiter) [0x00083] in :0
at System.Xml.Serialization.XmlReflectionImporter.InitializeStructMembers (System.Xml.Serialization.StructMapping mapping, System.Xml.Serialization.StructModel model, System.Boolean openModel, System.String typeName, System.Xml.Serialization.RecursionLimiter limiter) [0x00235] in :0
— End of inner exception stack trace —
at System.Xml.Serialization.XmlReflectionImporter.InitializeStructMembers (System.Xml.Serialization.StructMapping mapping, System.Xml.Serialization.StructModel model, System.Boolean openModel, System.String typeName, System.Xml.Serialization.RecursionLimiter limiter) [0x003b6] in :0
at System.Xml.Serialization.XmlReflectionImporter.ImportStructLikeMapping (System.Xml.Serialization.StructModel model, System.String ns, System.Boolean openModel, System.Xml.Serialization.XmlAttributes a, System.Xml.Serialization.RecursionLimiter limiter) [0x0015b] in :0
at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping (System.Xml.Serialization.TypeModel model, System.String ns, System.Xml.Serialization.XmlReflectionImporter+ImportContext context, System.String dataType, System.Xml.Serialization.XmlAttributes a, System.Boolean repeats, System.Boolean openModel, System.Xml.Serialization.RecursionLimiter limiter) [0x002af] in :0
— End of inner exception stack trace —
at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping (System.Xml.Serialization.TypeModel model, System.String ns, System.Xml.Serialization.XmlReflectionImporter+ImportContext context, System.String dataType, System.Xml.Serialization.XmlAttributes a, System.Boolean repeats, System.Boolean openModel, System.Xml.Serialization.RecursionLimiter limiter) [0x00393] in :0
at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping (System.Xml.Serialization.TypeModel model, System.String ns, System.Xml.Serialization.XmlReflectionImporter+ImportContext context, System.String dataType, System.Xml.Serialization.XmlAttributes a, System.Xml.Serialization.RecursionLimiter limiter) [0x00000] in :0
at System.Xml.Serialization.XmlReflectionImporter.IncludeType (System.Type type, System.Xml.Serialization.RecursionLimiter limiter) [0x00037] in :0
at System.Xml.Serialization.XmlReflectionImporter.IncludeType (System.Type type) [0x00007] in :0
at System.Xml.Serialization.XmlSerializer…ctor (System.Type type, System.Xml.Serialization.XmlAttributeOverrides overrides, System.Type[ ] extraTypes, System.Xml.Serialization.XmlRootAttribute root, System.String defaultNamespace, System.String location, System.Security.Policy.Evidence evidence) [0x0002a] in :0
at System.Xml.Serialization.XmlSerializer…ctor (System.Type type, System.Xml.Serialization.XmlAttributeOverrides overrides, System.Type[ ] extraTypes, System.Xml.Serialization.XmlRootAttribute root, System.String defaultNamespace, System.String location) [0x00000] in :0
at System.Xml.Serialization.XmlSerializer…ctor (System.Type type, System.Type[ ] extraTypes) [0x00000] in :0
at I2.SmartEdge.SE_Animation.LoadFromSerializedData (System.String data) [0x0002a] in /Users/alanthomas/Documents/unity/Faster/Faster0.1.1/Faster/Assets/I2/SmartEdge/Scripts/Vertex Effects/SmartEdge/Animation/SE_Animation.cs:197
UnityEngine.Debug:Log(Object)
I2.SmartEdge.SE_Animation:LoadFromSerializedData(String) (at Assets/I2/SmartEdge/Scripts/Vertex Effects/SmartEdge/Animation/SE_Animation.cs:201)
I2.SmartEdge.SE_AnimationPreset:CreateAnimation() (at Assets/I2/SmartEdge/Scripts/Vertex Effects/SmartEdge/Animation/SE_AnimationPreset.cs:15)
I2.SmartEdge.SE_AnimationPreset_Inspector:OnEnable() (at Assets/I2/SmartEdge/Editor/Inspectors/Animation/SE_AnimationPreset_Inspector.cs:24)

This is if I attempt to animate position. Any ideas on what is happening? I am running this on Unity 2019.1.7.