- Home /
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 : http://answers.unity3d.com/questions/1116633/change-alpha-color-of-specific-character-in-a-text.html.
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 http://docs.unity3d.com/Manual/StyledText.html 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?
Answer by JoshuaMcKenzie · Feb 13, 2016 at 05:00 PM
@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)
Thank you @Joshua$$anonymous$$c$$anonymous$$enzie but I get some errors with your code. Copied/pasted in a new cs, added using UnityEngine; on top and fixed "$$anonymous$$onoBehavior" with "$$anonymous$$onoBehaviour", and this is what the console prints:
I've also tried to change the variables with the ones i'm currently using, but it doesn't work. Here's my code with your changes:
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.UI;
using System.Text.RegularExpressions;
public class ColorfulText: $$anonymous$$onoBehaviour
{
static IDictionary<string,string> character$$anonymous$$ap = 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>"},
{"$$anonymous$$","<color=#FFFFFFFF>$$anonymous$$</color>"}
};
static Regex regex = new Regex (System.String.Join ("|", character$$anonymous$$ap.$$anonymous$$eys));
public GameObject letterText;
string alphabetLetter;
void Awake()
{
Text myText = letterText.GetComponent<Text> ();
alphabetLetter = myText.text;
Format ();
}
public void ChangeText(string newText)
{
alphabetLetter = newText;
Format ();
}
void Format()
{
myText = myText.text.regex.Replace (alphabetLetter, m => character$$anonymous$$ap [m.Value]);
}
}
And here's the full code I was using when the problem occurred:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class ColorText : $$anonymous$$onoBehaviour
{
public string alphabetLetter;
public GameObject letterText;
void Start ()
{
letterText = GameObject.FindWithTag ("LetterText");
}
void OnTriggerEnter(Collider other)
{
Text myText = letterText.GetComponent<Text> ();
myText.text = myText.text.Replace(alphabetLetter, "<color=#3D3D3DFF>"+alphabetLetter+"</color>");
}
}
Okay, now I don't get any error, but the result is different from what I need. In fact, it replaces the specific letter to the whole text. The mechanic is: I have a hidden text in a scene, and I change alpha of specific letters to reveal that gradually. $$anonymous$$y text gameobject contains the whole sentence, that's why I need to act on the single letters' alpha color.
I've tried to change this: myText.text = regex.Replace (baseString, m => character$$anonymous$$ap [m.Value]);
into this: myText.text = myText.text.regex.Replace (baseString, m => character$$anonymous$$ap [m.Value]); But it doesn't work.
@$$anonymous$$0rrigan seems the full intent of the class still hasn't been fully disclosed. However, since the original question was answered (it was breaking not because of some limit but due to bad parsing from reusing Replace multiple times), this is getting off-topic. Addressing this issue would be better handled in the Unity Forums (and there i don't have to worry about character limits in the comments). Please provide a link to the created thread here for posterity.
There we can ask for some more specifics like where/when/how are you setting the alphabetLetter, and can the class be a singleton. All of which would deter$$anonymous$$e how to solve this issue. From what i'm reading here you're looking to only change one letter at a time, not all instances of that letter. I can think of a number of ways of solving this problem but they all depend on how you intend to use the class.
Okay, thank you. See you there: http://forum.unity3d.com/threads/help-with-my-script-to-change-text-alpha-color-via-regex.386034/
Your answer
Follow this Question
Related Questions
Ui text to string ? 1 Answer
[SOLVED] Why is text not appearing? 2 Answers
How to change color of animated Text component 2 Answers
Inputfield text to String variable 1 Answer