Text UI broken after changing alpha color of single letters

Hi everybody, I’m using a script to change the alpha color of specific letters in a Text UI. Refer to this : Change alpha color of specific letter in a Text UI with C# - Questions & Answers - Unity Discussions.

 myText.text = myText.text.Replace(alphabetLetter, "<color=#ffffffxx>"+alphabetLetter+"</color>");

When I change more than six letters at runtime the text disappears and if I select the Text UI I can see this thing inside Text box:

<color=#3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>FF>H</color><color=#3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>FF>E</color><color=#3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>FF>L</color><color=#3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>FF>L</color>O WOR<color=#3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>FF>L</color><color=#3D3D3DFF>D</color>

<color=#3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>FF>T</color><color=#3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>FF>H</color>IS IS <color=#3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>FF>M</color><color=#3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>FF>E</color>

The text should be “HELLO WORLD THIS IS ME”. The bug occurs when I raise the alpha of the “D” letter.

I’ve checked the documentation Redirect to... title of new-page but found nothing about a limit and it seems that nobody else has experienced a similar situation. I suppose this could be a bug, what do you think?

@M0rrigan You’re calling text.replace more than once on the string

for example you swap “H” with <color=#3D3D3DFF>H</color>
but then you call it again for the “D”, and the Replace function is detecting the letters within the color property. giving you:

color=#3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>3<color=#3D3D3DFF>D</color>FF>H</color>

all of that is just for the H character, very ugly. and this is whats breaking it as now it can’t be parsed correctly

String.Replace is NOT what you want to use for this!
Try and use RegEx (aka regular expressions). then you can simply run the regex on the base string for the new string of colors. I would recommend that you save the base string somewhere so that if you need to swap colors again you can just use the base (trying to regex a string that already went through a replace is just working hard, not smart) .


Edit: After seeing your class in the comment I see how you were trying to use it. so I rewrote the class to be a little more to how you want it;

First off you want to add this component class to the gameobject that is firing the OnTriggerEnter:

using UnityEngine;
using UnityEngine.Events;
public class TriggerRelay: MonoBehaviour
{
    private class Delegator<T> : UnityEvent<T>{}

    private Delegator<Collider> OnTriggerEnter_ = new Delegator<Collider>();
    private Delegator<Collider> OnTriggerExit_  = new Delegator<Collider>();

    public void AttachEnter(UnityAction<Collider> listener)
    {
        OnTriggerEnter_.AddListener(listener);
    }

    public void DetachEnter(UnityAction<Collider> listener)
    {
        OnTriggerEnter_.RemoveListener(listener);
    }

    public void AttachExit(UnityAction<Collider> listener)
    {
        OnTriggerExit_.AddListener(listener);
    }

    public void DetachExit(UnityAction<Collider> listener)
    {
        OnTriggerExit_.RemoveListener(listener);
    }


    void OnTriggerEnter(Collider other)
    {
        OnTriggerEnter_.Invoke(other);
    }

    void OnTriggerExit(Collider other)
    {
        OnTriggerExit_.Invoke(other);
    }
}

This class is just a relay to pass on trigger collision events to other gameobjects. From my experience in design patterns such as MVC, I’ve found that its easier if your game logic doesn’t explicitly know that a UI exists (but it can have public variables and methods that some classes like a UI might use). the UI can know about the Game logic, but the Game Logic shouldn’t know about the UI. Keeping this in mind will help a lot in preventing you from coding into some traps later on (cause you’re minimizing dependency, and allows the game logic to have complete control over the game play as it should).

now as for that other class that will be going on Gameobject that has the Text Component:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.UI;


public class ColorText: MonoBehaviour
{
    public TriggerRelay relay;

    public IDictionary<string,string> characterMap = new Dictionary<string,string>()
    {
        {"H","<color=#003D3DFF>H</color>"},
        {"E","<color=#3D003DFF>E</color>"},
        {"L","<color=#3D3D00FF>L</color>"},
        {"O","<color=#DD3D3DFF>O</color>"},
        {"W","<color=#3DDD3DFF>W</color>"},
        {"R","<color=#3D3DDDFF>R</color>"},
        {"D","<color=#8D3D3DFF>D</color>"},
        {"I","<color=#3D8D3DFF>I</color>"},
        {"S","<color=#3D3D8DFF>S</color>"},
        {"M","<color=#FFFFFFFF>M</color>"}
    };
    
    Text myText;
    string baseString;
    bool isFormatted;

    void Awake()
    {
        myText = GetComponent<Text>();
        isFormatted = false;
    }

    void OnEnable()
    {
        if(relay)
        {
            relay.AttachEnter(WhenTriggerEnter);
            relay.AttachExit(WhenTriggerExit);
        }
        isFormatted = false;
        baseString = myText.text;
    }

    void OnDisable()
    {
        if(relay)
        {
            relay.DetachEnter(WhenTriggerEnter);
            relay.DetachExit(WhenTriggerExit);
        }
        isFormatted = false;
        myText.text = baseString;
    }


    public void ChangeText(string newText)
    {
        myText.text = baseString = newText;
        if(isFormatted)
        {
            Format();
        }
    }

    void Format()
    {
        Regex regex 
            = new Regex (String.Join ("|", characterMap.Keys.Select(c => Regex.Escape(c)).ToArray()));
        myText.text = regex.Replace (baseString, m => characterMap [m.Value]);
    }
    
    void WhenTriggerEnter(Collider o)
    {
        Format();
        isFormatted = true;
    }
    
    void WhenTriggerExit(Collider o)
    {
        myText.text = baseString;
        isFormatted = false;
    }

}

just drag in the reference of the TriggerRelay onto the component in the inspector (or if you are instantiating it at runtime you can set it then)