- Home /
Best way to position prefabs in between texts?
Hello all,
I am trying to position some prefabs in between text but I have no idea how to do it. I am using EZ GUI and SM2 so I have SpriteTexts and PackedSprites. I want to position the PackedSprites in between certain substrings of the SpriteText. How I can accomplish this I have no idea. I've been pondering this for a long time and haven't come up with a solution.
I am trying to do this for tutorials that I am building. I have a banner come up on the screen with a text: Example - "Tap the lilypad to move the frog." I, then, remove the buzzwords in this case lilypad and frog. Now at this point I want to place a lilypad sprite where the "lilypad" text was and a frog sprite where the "frog" text was. But, as I said before I have no idea how to accomplish this.
Here is some relevant code:
// Adjusts the current string
private string _Text;
public string Text {
set {
textBuilder = new StringBuilder(value);
textString = value;
//remove buzzwords with whitespaces
replaceBuzzWords();
//create buzzword prefabs
createBuzzWordPrefabs();
//place them correctly in whitespace TODO: how?
setPrefabPositions();
_Text = textBuilder.ToString();
_infoText.Text = textBuilder.ToString();
}
get { return _Text; }
}
private void replaceBuzzWords() {
prefabsToBeMade = new List<string>();
foreach(BuzzWord buzzWord in Enum.GetValues(typeof(BuzzWord))) {
if(textString.Contains(buzzWord.ToString())) {
if(buzzWord == BuzzWord.Dragonfly) {
textBuilder.Replace(buzzWord.ToString(), dragonFlyWhiteSpace);
prefabsToBeMade.Add(buzzWord.ToString());
}
else if(buzzWord == BuzzWord.Tablet) {
textBuilder.Replace(buzzWord.ToString(), tabletWhiteSpace);
prefabsToBeMade.Add(buzzWord.ToString());
}
else {
textBuilder.Replace(buzzWord.ToString(), whiteSpace);
prefabsToBeMade.Add(buzzWord.ToString());
}
}
}
}
private void createBuzzWordPrefabs() {
prefabs = new List<GameObject>();
foreach(String s in prefabsToBeMade)
prefabs.Add( Instantiate(Resources.Load("TutorialTitlePrefabs/"+s+"Prefab")) as GameObject );
}
private void setPrefabPositions() {
foreach(GameObject go in prefabs) {
go.transform.parent = _infoText.transform;
positionPrefab(go);
go.transform.position = _infoText.transform.position;
}
}
private void positionPrefab(GameObject toBePositioned) {
}
Answer by ATMEthan · Jan 25, 2013 at 07:45 PM
Hey all,
I figured out the solution for my problem. Here's my code if anyone is interested. So, basically I just split up the strings and added them individually to a string builder object. When I hit a buzzword I added whitespace with a $. I also record the buzzword at this time. Then I search the string for my $ characters and when I hit one I that the substring up to that character and use the SpriteText.GetWidth() to get a number offset. I record the offset and remove the $ from the text. Now, I use the list of buzzwords and create the prefabs and position them with their offset. It seems I just needed some rest to figure this out; yesterday I had a killer headache and just couldn't think. If anyone has a better solution please let me know! I'm always looking for ways to improve my code.
Oh. and the giant wall of commented out code was my first implementation. But that way of doing it didnt take into account the order of the buzzwords. That resulted in the width offset getting askew because sometimes I would replace the buzzword with whitespace and that pushed all the text which made the width off by like 100 pixels or more.
//use the $ to stop count when finding the width of the TutorialTitle Text
private string whiteSpace = "$ "; //11 spaces all include the '$' as a space
private string dragonFlyWhiteSpace = "$ "; //13 spaces
private string tabletWhiteSpace = "$ "; // 11spaces
private StringBuilder textBuilder;
private string textString;
private List<string> prefabsToBeMade;
private List<GameObject> prefabs;
private List<float> prefabOffSets;
private string _Text;
public string Text {
set {
textBuilder = new StringBuilder();
textString = value;
//remove buzzwords with whitespaces
replaceBuzzWords();
//get the widths needed
getTextWidths();
//create buzzword prefabs
createBuzzWordPrefabs();
//place them correctly in whitespace TODO: how?
setPrefabPositions();
_Text = textBuilder.ToString();
infoText.Text = textBuilder.ToString();
}
get { return _Text; }
}
private void replaceBuzzWords() {
prefabsToBeMade = new List<string>();
prefabOffSets = new List<float>();
string[] ORDERWENEEDORDER = textString.Split(' ');
foreach(string s in ORDERWENEEDORDER) {
if(stringIsBuzzword(s)) {
textBuilder.Append(whiteSpace);
prefabsToBeMade.Add(s);
} else {
textBuilder.Append(s + " ");
}
}
// foreach(BuzzWord buzzWord in Enum.GetValues(typeof(BuzzWord))) {
// if(textString.Contains(buzzWord.ToString())) {
// if(buzzWord == BuzzWord.Dragonfly) {
// //remove buzzword with whitespace and special character '$'
// textBuilder.Replace(buzzWord.ToString(), dragonFlyWhiteSpace);
// Debug.Log("TextBuild after replace: " + textBuilder);
// //get the length up to the buzzword
// getTextWidth();
// Debug.Log("TextBuild after width: " + textBuilder);
// //remove special character '$'
// textBuilder.Replace("$", " ");
// Debug.Log("TextBuild after special: " + textBuilder);
// //make the prefab
// prefabsToBeMade.Add(buzzWord.ToString());
// }
// else if(buzzWord == BuzzWord.Tablet) {
// textBuilder.Replace(buzzWord.ToString(), whiteSpace);
// Debug.Log("TextBuild after replace: " + textBuilder);
// //get the length up to the buzzword
// getTextWidth();
// Debug.Log("TextBuild after width: " + textBuilder);
// //remove special character '$'
// textBuilder.Replace("$", " ");
// Debug.Log("TextBuild after special: " + textBuilder);
// //make the prefab
// prefabsToBeMade.Add(buzzWord.ToString());
// }
// else {
// textBuilder.Replace(buzzWord.ToString(), whiteSpace);
// Debug.Log("TextBuild after replace: " + textBuilder);
// //get the length up to the buzzword
// getTextWidth();
// Debug.Log("TextBuild after width: " + textBuilder);
// //remove special character '$'
// textBuilder.Replace("$", " ");
// Debug.Log("TextBuild after special: " + textBuilder);
// //make the prefab
// prefabsToBeMade.Add(buzzWord.ToString());
// }
// }
// }
}
private bool stringIsBuzzword(string toBeChecked) {
foreach(BuzzWord buzzWord in Enum.GetValues(typeof(BuzzWord)))
if(buzzWord.ToString() == toBeChecked)
return true;
return false;
}
private void createBuzzWordPrefabs() {
destoryPrefabs();
prefabs = new List<GameObject>();
foreach(String s in prefabsToBeMade)
prefabs.Add( Instantiate(Resources.Load("TutorialTitlePrefabs/" + s + "Prefab")) as GameObject );
}
private void getTextWidths() {
string toBeMeasured = "";
char[] widthFinder = textBuilder.ToString().ToCharArray();
foreach(char c in widthFinder) {
toBeMeasured += c;
if(c == '$') {
prefabOffSets.Add(infoText.GetWidth(toBeMeasured));
//remove the $ we dont need its marker anymore
textBuilder.Replace("$", " ");
}
}
}
private void setPrefabPositions() {
int index = 0;
foreach(GameObject go in prefabs) {
go.transform.parent = infoText.transform;
go.transform.position = Vector3.zero;
go.transform.localPosition = new Vector3(-((infoText.GetWidth(textBuilder.ToString())) / 2.0f), infoText.transform.localPosition.y, infoText.transform.localPosition.z);
positionPrefab(go, index);
index++;
}
}
private void positionPrefab(GameObject toBePositioned, int index) {
toBePositioned.transform.localPosition = new Vector3((toBePositioned.transform.localPosition.x + prefabOffSets[index]),
toBePositioned.transform.localPosition.y, toBePositioned.transform.localPosition.z);
}
Answer by robertbu · Jan 25, 2013 at 05:57 PM
There are a couple of different ways to approach this problem. The simplest is to break your sentence into words and do the layout yourself.
Create and EZGUI Label (SpriteText) game object with the font, character size, character spacing and the like setup.
Break your title or sentence into individual words.
For each word, Instantiate() a copy of your Label, set the text and position the text leaving space between between the words.
Here is some starter code:
public class SptxLayout : MonoBehaviour {
public GameObject goParent;
public GameObject goSptxPrefab;
public Vector3 v3Position;
private string stLayout = "This is a title with some stuff in it";
private Vector3 v3ExampleAnchor;
void Start () {
float fSpaceWidth;
SpriteText sptx = goSptxPrefab.GetComponent<SpriteText>();
sptx.Text = " ";
fSpaceWidth = sptx.TotalWidth;
string[] arst = stLayout.Split (new char[] {' '});
for (int i = 0; i < arst.Length; i++) {
GameObject go = (GameObject)Instantiate (goSptxPrefab);
go.transform.parent = goParent.transform;
go.transform.localPosition = v3Position;
sptx = go.GetComponent<SpriteText>();
sptx.Text = arst[i];
if (arst[i] == "stuff") {
v3ExampleAnchor = v3Position;
v3ExampleAnchor.x += sptx.TotalWidth / 2.0f;
}
v3Position.x = v3Position.x + sptx.TotalWidth + fSpaceWidth;
}
}
}
With this code, you now have individual SpriteText objects for each word which you can disable the renderer to hide. Note the calculation here assume you've set Anchor property in the SpriteText to MiddleLeft. The v3ExampleAnchor will be in local coordinates to the parent. This is where you will show your icon.
I'm not sure how you are using the PackedSprite. But for things like this, I typically build a base PackedSprite that has all the icons/images in a single animation. Then at runtime, I set the frame in the animation to the one I want to show. It makes keeping track of things easier.
I actually just finished my version of a solution for this. I will post it shortly. I would have used your method of displaying the PackedSprite(all images set to different animations) but my problem was some images need certain specific rotations. I figured it would be easier to make them separate prefabs so I can get the desired rotation and what not through the inspector. I could have done it through code but that would have been a lot more work.