2
\$\begingroup\$

I would like to animate two of my UI images in a similar manner to the red and blue images below: enter image description here

I'd like to accomplish this through script, but I'm not certain if it's possible.

Does anyone have insight on how I can recreate this effect? Thanks!

\$\endgroup\$

1 Answer 1

7
\$\begingroup\$

For color, this is basically an additive blending:

enter image description here

Unity has made an additive shader for the particle system, for convenience you can use it directly:

enter image description here

For shapes, this basically draws two quads on the UI and updates their vertex positions via script. Now let's see how to draw polygons on the UI. Here is a simple code. For convenience, I specify that all children of the script transform are the vertices of the polygon, and agree that they are in order.

using UnityEngine;
using UnityEngine.UI;

public class UIPolygon : Graphic
{
    private void OnGUI()
    {
        SetAllDirty();
    }
    protected override void OnPopulateMesh(VertexHelper vh)
    {
        if (transform.childCount < 2)
        {
            return;
        }
        vh.Clear();
        foreach (Transform child in transform)
        {
            vh.AddVert(child.localPosition, color, Vector2.zero);
        }
        for (int i = 0; i < transform.childCount - 2; i++)
        {
            vh.AddTriangle(i + 1, i + 2, 0);
        }
    }
}

Now, mount this script on a UI object and create 4 children for it:

enter image description here

Then, create a new material, set its shader to additive, and attach it to the polygon.

Then set the color of this polygon to red, duplicate a polygon and set its color to cyan, and adjust the positions of their vertices:

enter image description here

Finally, we attach another script to the polygon to make all its children move randomly within a certain range.

**Due to the frame rate of the gif, I don't know whether the movement of the vertices is continuous or discrete. Here I simply let it change the position randomly. You can modify it however you want.

using System.Collections;
using UnityEngine;

public class UIPolygonChildrenMove : MonoBehaviour
{
    void Start()
    {
        foreach (RectTransform child in transform)
        {
            StartCoroutine(UpdateChildPos(child, child.anchoredPosition));
        }
    }
    IEnumerator UpdateChildPos(RectTransform child, Vector2 prePos)
    {
        while (true)
        {
            child.anchoredPosition = prePos + RandomOffset(30);
            yield return new WaitForSeconds(0.1f);
        }
    }
    private Vector2 RandomOffset(float max)
    {
        return new Vector2(Random.Range(-max, max), Random.Range(-max, max));
    }
}

Result:

enter image description here

And you can add some lerp:

using System.Collections;
using UnityEngine;

public class UIPolygonChildrenMove : MonoBehaviour
{
    void Start()
    {
        foreach (RectTransform child in transform)
        {
            StartCoroutine(UpdateChildPos(child));
        }
    }
    IEnumerator UpdateChildPos(RectTransform child)
    {
        Vector2 prePos = child.anchoredPosition;
        Vector2 fromPos = prePos;
        Vector2 targetPos = prePos + RandomOffset(30);
        float duration = 0.2f;
        float pastTime = 0;
        while (true)
        {
            pastTime += Time.deltaTime;
            if (pastTime >= duration)
            {
                fromPos = targetPos;
                targetPos = prePos + RandomOffset(30);
                pastTime = 0;
            }
            var t = pastTime / duration;
            child.anchoredPosition = Vector2.Lerp(fromPos, targetPos, t);
            yield return null;
        }
    }
    private Vector2 RandomOffset(float max)
    {
        return new Vector2(Random.Range(-max, max), Random.Range(-max, max));
    }
}

Result:

enter image description here

Edit:

Sorry I haven't played Persona 5, I thought the effect would only render on a black background. Now let's try to make it work in any background.

First find the source code of Mobile/Particles/Additive. We can see that it simply does an additive blend by Blend SrcAlpha One:

Shader "Mobile/Particles/Additive" {
    Properties {
        _MainTex ("Particle Texture", 2D) = "white" {}
    }

    Category {
        Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
        Blend SrcAlpha One
        Cull Off Lighting Off ZWrite Off Fog { Color (0,0,0,0) }
        
        BindChannels {
            Bind "Color", color
            Bind "Vertex", vertex
            Bind "TexCoord", texcoord
        }
        
        SubShader {
            Pass {
                SetTexture [_MainTex] {
                    combine texture * primary
                }
            }
        }
    }
}

We can use Stencil to mark the stencil buffer, indicating that this pixel is rendered by a specific shader, and then the rendered pixel will read this mark and process it. In general, we need to output the normal color and mark it during the first rendering, and then perform additive blend if we read this mark during the second rendering.

Shader "Custom/myAdditive" 
{
    Properties {
        _MainTex ("Particle Texture", 2D) = "white" {}
    }

    Category {
        Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
        Cull Off Lighting Off ZWrite Off Fog { Color (0,0,0,0) }
        
        BindChannels {
            Bind "Color", color
            Bind "Vertex", vertex
            Bind "TexCoord", texcoord
        }
        
        SubShader {
            Pass {
                Stencil 
                {
                    Ref 1
                    Comp NotEqual
                    Pass Replace
                }
                SetTexture [_MainTex] {
                    combine texture * primary
                }
            }
            Pass {
                Stencil {
                    Ref 1
                    Comp Equal
                }
                Blend SrcAlpha One
                SetTexture [_MainTex] {
                    combine texture * primary
                }
            }
        }
    }
}

If ref Not Equal 1, Execute the first pass, output the color directly, and set ref to 1. If ref Equal 1, Execute the second pass, output the color after additive blend.

Result:

enter image description here

\$\endgroup\$
8
  • 2
    \$\begingroup\$ Note that this effect is sometimes drawn over a white background too, so it would need a backing or a modified approach to not get washed out in that context. \$\endgroup\$
    – DMGregory
    Feb 5, 2023 at 17:16
  • \$\begingroup\$ Wow, thank you so much for taking the time to provide me with such a detailed answer! It's so cool how you were able to replicate the effect so closely. Though, I'm not certain if the answer is 1:1 with exactly what I'm trying to do. Rather than draw two quads, is it possible to do this with the UI shapes I've already created? I have a text box, and I want to move the brown and the pink shape exactly how you did in the final result of the answer you posted above. Here's an example: i.ibb.co/6R39zfx/image.png I apologize for not being as specific as I should have been! \$\endgroup\$ Feb 5, 2023 at 23:35
  • 1
    \$\begingroup\$ @PayasoPrince I'm afraid it can't. the UI shapes are images, We cannot change their vertex positions separately. Maybe they can be used as textures on quad shapes, but just pictures won't accomplish the effect in the answer. : ) \$\endgroup\$
    – Mangata
    Feb 6, 2023 at 2:28
  • 1
    \$\begingroup\$ This script inherits from UI.Graphic. OnPopulateMesh There is no need to call it manually, it just overloads the parent class method and is called by the engine itself, just like Start and Update. Color is also a member of the parent class, it will show in inspector. And the VertexHelper can aid in the generation of meshes for the UI. \$\endgroup\$
    – Mangata
    Feb 6, 2023 at 3:19
  • 1
    \$\begingroup\$ Yes, it should be the same. \$\endgroup\$
    – Mangata
    Feb 6, 2023 at 5:00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .