UI text is not rendered but appearing in hierarchy — what is missing? - c#

trying to show text dynamically created to the canvas but its not rendering.
After creating canvas an empty gameObject attached as child and the following script is attached to that. After running the object appears in hierarchy but not rendered.maybe I'm missing something:
GameObject o;
void Start() {
GameObject test = new GameObject("myTextGO");
test.transform.SetParent(this.transform);
// test.transform.SetParent(o.transform);
Text myText = test.AddComponent<Text>();
myText.text = "Hello there!";
}
Also tried few other combination but not text is not rendering. Need to know solution.

Three things required to do when creating UI text dynamically and manually:
Font
Proper Parenting
RectTransform (Now, automatically attached by Unity when UI component
is attached to a GameObject)
I assume that you are using the latest Unity version so you just need to do the first two:
Supply font for the Text component to use as mentioned this answer. Also, when using transform.SetParent, you have to supply false to it. This causes the UI component to keep its local orientation.
void CreateText(GameObject canvas, string text)
{
//Create Canvas Text and make it child of the Canvas
GameObject txtObj = new GameObject("myTextGO");
txtObj.transform.SetParent(canvas.transform, false);
//Attach Text,RectTransform, an put Font to it
Text txt = txtObj.AddComponent<Text>();
txt.text = text;
Font arialFont = Resources.GetBuiltinResource<Font>("Arial.ttf");
txt.font = arialFont;
txt.lineSpacing = 1;
txt.color = new Color(50 / 255, 50 / 255, 50 / 255, 255 / 255);
}
Usage:
public GameObject canvas;
void Start()
{
CreateText(canvas, "Hello there!");
}
That's how to create a text component manually but that should no longer be used. Let Unity do this automatically. The DefaultControls should be used to create UI Objects. It reduces the chances that the creation code will fail or that you'll run into similar issues in your question. Create the Text resources with DefaultControls.Resources then the Text itself with DefaultControls.CreateText.
void CreateText(GameObject canvas, string text)
{
DefaultControls.Resources uiResources = new DefaultControls.Resources();
GameObject uiText = DefaultControls.CreateText(uiResources);
uiText.transform.SetParent(canvas.transform, false);
uiText.GetComponent<Text>().text = text;
//Change the Text position?
//uiText.GetComponent<RectTransform>().sizeDelta = new Vector2(100f, 100f);
}

There is no font attached to the Text-object.
Try adding this line
myText.font = Resources.GetBuiltinResource<Font>("Arial.ttf");

Related

How to dynamically create UI elements (journal entries) in Unity3D at runtime?

I'm making a journal app in Unity (C#) and I can't for the life of me figure out how to properly generate the UI (at runtime) of all the journal entries and have it scrollable within a viewport.
So far I've been able to :
generate an empty UI game object "Entry",
parent it to UI game object "Entries",
create a TextMeshPro component in Entry,
concatenate all entries into a string and show the text
But the size of the TextMeshPro component is huge and doesn't fit in the game window, and using scroll rect doesn't allow me to go to both extremes, as can be seen here.
This is the code I wrote to create the UI and show the journal text:
public class ShowEntries : MonoBehaviour
{
TextMeshProUGUI text;
void Start()
{
GameObject entry = new GameObject();
entry.transform.localScale = new Vector3(1, 1, 1);
entry.transform.localPosition = new Vector3(0, 0, -1000);
entry.transform.SetParent(this.transform);
entry.AddComponent<TextMeshProUGUI>();
text = entry.GetComponent<TextMeshProUGUI>();
text.rectTransform.localPosition = new Vector3(0, 0, 0);
text.alignment = TextAlignmentOptions.Center;
text.color = Color.white;
text.fontSize = 0.5f;
}
void Update()
{
DrawEntries();
}
public void DrawEntries()
{
string textFull = "";
foreach (Experience experience in Global.experienceList)
{
textFull += experience.Format();
}
text.text = textFull;
}
}
I feel like there is a more elegant approach to displaying n number of journal entries in the UI canvas but am fairly new to C# so just wanted to post of here to see if I could get some tips. Ideally, it would generate a list of UI buttons (one for each entry) with a preview, that when clicked opens the entire entry, but for now I'm just trying to figure out how to display the text properly.
Thanks a million!
Figured it out! By creating a UI element prefab I was able to instantiate all the journal entries and set the correct values.

How to create new parent child gamobjects at runtime?

For a button click two new gameObjects are created and the second one is setting as a child of first one and first one is child of attached object. But it is not working / showing on hierarchy but the created object is not appearing in game window.
Here is the code:
public void CreateTextButtonClick(string text)
{
Debug.Log("Hey starting of this..");
//Create Canvas Text and make it child of the Canvas
GameObject txtObj = new GameObject("myText");
txtObj.transform.SetParent(this.transform, false);
GameObject pan = new GameObject("text");
pan.transform.SetParent(txtObj.transform);
//Attach Text,RectTransform, an put Font to it
Text txt = pan.AddComponent<Text>();
txt.text = text;
Font arialFont = Resources.GetBuiltinResource<Font>("Arial.ttf");
txt.font = arialFont;
txt.lineSpacing = 1;
txt.color = Color.blue;
//image
//GameObject back = new GameObject("image");
//back.transform.SetParent(txtObj.transform);
//Image i = back.AddComponent<Image>();
Debug.Log("Hey its done..");
}
What can be a solution to achieve this multi level object making?
You used SetParent properly in the first Object you want to be child of your Canvas by passing false to it which causes it to keep its local orientation:
GameObject txtObj = new GameObject("myText");
txtObj.transform.SetParent(this.transform, false);
But you did not do this for the "text" Object that will be child of the object above:
GameObject pan = new GameObject("text");
pan.transform.SetParent(txtObj.transform);
You can fix that by also passing false to it so that the UI component to keep its local orientation. Unrelated but I also think you should add a RectTransform the parent object too. Add RectTransform to anything that will be under the Canvas.
Your new code:
public void CreateTextButtonClick(string text)
{
Debug.Log("Hey starting of this..");
//Create Canvas Text and make it child of the Canvas
GameObject txtObj = new GameObject("myText");
txtObj.AddComponent<RectTransform>();
txtObj.transform.SetParent(this.transform, false);
GameObject pan = new GameObject("text");
pan.transform.SetParent(txtObj.transform, false);
//Attach Text,RectTransform, an put Font to it
Text txt = pan.AddComponent<Text>();
txt.text = text;
Font arialFont = Resources.GetBuiltinResource<Font>("Arial.ttf");
txt.font = arialFont;
txt.lineSpacing = 1;
txt.color = Color.blue;
}
In your commented code, it looks like you plan to add another child object with Image component too and I can already see that mistake there. Do not forget to do this too. You must otherwise, you will have issues. This applies to everything you plan to place under the Canvas.

How to add spacing dynamically on unity gameObject added to scrollView?

I was trying to add text elements on scrollview each having its own space.
For the code below after creating object added to the empty gameObject that is child of scrollview. Now if I add manually hight and width it works but if the string size grows it will become zagged again. So need to know what its previous elements height is or add space/padding to the top.
Heres the code from other question to create and adding those textObjects:
void CreateText(GameObject canvas, string text)
{
//Create Canvas Text and make it child of the Canvas
GameObject txtObj = new GameObject("myTextGO");
txtObj.transform.SetParent(canvas.transform, false);
//Attach Text,RectTransform, an put Font to it
Text txt = txtObj.AddComponent<Text>();
txt.text = text;
Font arialFont = Resources.GetBuiltinResource<Font>("Arial.ttf");
txt.font = arialFont;
txt.lineSpacing = 1;
txt.color = new Color(50 / 255, 50 / 255, 50 / 255, 255 / 255);
//requriing a solution like this only dynamically.
txt.GetComponent<RectTransform>().sizeDelta = new Vector2(100f, 200f);
}
How can I add spacing dynamically here for following loop? So that they are readable as different text objects.
for (i;i<10;i++)
//CreateText(canvas, UserText+" Hello there! "+i);
CreateText(canvas, "Hello there! "+i);
Require the text object to increase size automatically which is not achieved with content fitter attached to the empty gameObject. Default GridLayoutGroup cell and col size is 100x100, need to increase it dynamically.
Thanks.

Draw "EditorGUILayout.FloatField" with a custom label after the field?

I'm making a custom editor window and I want to draw EditorGUILayout.FloatField on it. if I write this:
EditorGUILayout.FloatField("change val", someFloatValue);
a label appears in front of the field. But I want it to appears behind the field. Also, I'd like to change simple text with a texture.
What I have now:
What I'd like to achieve:
UPD. I missed important information:
A mouse behavior over a texture must be the same as over a FloatField label, i.e. if I click on the texture and start dragging a cursor - the float value in the field must changes
I couldn't find an option to do it in a simple way. Maybe I miss something.
Is it possible not to create a lot of auxiliary classes for that simple action? If yes, then how?
Just put a label field after the floatfield
EditorGUILayout.BeginHorizontal();
someFloatValue = EditorGUILayout.FloatField(someFloatValue);
EditorGUILayout.LabelField("Change Val")
EditorGUILayout.EndHorizontal();
If you want to put a texture, then just create GUIStyle with the texture you want as background, and use the style for the label
GUIStyle myStyle = new GUIStyle();
myStyle.normal.background = myTexture;
EditorGUILayout.BeginHorizontal();
someFloatValue = EditorGUILayout.FloatField(someFloatValue);
EditorGUILayout.LabelField("", myStyle)
EditorGUILayout.EndHorizontal();
Now, to handle widths, just use GUILayout.Width()
GUIStyle myStyle = new GUIStyle();
myStyle.normal.background = myTexture;
EditorGUILayout.BeginHorizontal();
someFloatValue = EditorGUILayout.FloatField(someFloatValue, GUILayout.Width(150));
EditorGUILayout.LabelField("", myStyle)
EditorGUILayout.EndHorizontal();
It's very possible, but you shouldn't be GUILayout / EditorGUILayout classes. The Layout in the class names means that those variants handle positioning themselves, they each take up a default amount of space and move to the next row, its very conveinient but to do a little bit more advanced stuff, you should use versions of the GUI that take Rect as a position for a draw, so take from GUI/ EditorGUI classes (without Layout in the class name)
I recommend you start by doing a custom PropertyDrawer first, the API for a PropertyDrawer calls for manual element placement, so theres a ton of good examples. Once you start drawing using Rects instead of auto-Layouts there's no limit to how many layers of stuff to paint where, its actually trivial to start painting outside your own inspector (as long as its in the same window)
Well, I've made an UGLY workaround for the field (also for IntField).
If remove all redundant code that I used for content filling - it will be pretty short...
A simplified example looks like this:
using UnityEditor;
using UnityEngine;
public class MainWindow : EditorWindow {
private float floatFieldVal;
private Rect groupFloatFieldRect;
[MenuItem("Examples/Test")]
static void Init() {
MainWindow window = (MainWindow)GetWindow(typeof(MainWindow), false, "My Empty Window");
window.Show();
}
void OnGUI() {
EditorGUILayout.BeginHorizontal(); {
EditorGUILayout.BeginVertical(); {
GUILayout.Button("Button 1");
GUILayout.Button("Button 2");
GUILayout.Button("Button 3");
} EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical(GUILayout.Width(300)); {
// never load asset in the loop :)
string assetFullPath = "Assets/Editor/Test.guiskin";
var fakeFieldGUISkin = (GUISkin)AssetDatabase.LoadAssetAtPath(assetFullPath, typeof(GUISkin));
GUIStyle fakeFieldStyle = fakeFieldGUISkin.GetStyle("test");
// place fake floatField right over a real field texture button
Rect test = new Rect(groupFloatFieldRect);
test.position = new Vector2(test.x + groupFloatFieldRect.width - 20, test.y + 3);
test.size = new Vector2(20, 20);
floatFieldVal = EditorGUI.FloatField(test, "fake", floatFieldVal, fakeFieldStyle);
// Draw FloatField And Texture as a Group
Rect groupRect = EditorGUILayout.BeginHorizontal(); {
// never create GUIStyle in the loop :)
GUIStyle floatIconStyle = new GUIStyle(EditorStyles.toolbarButton) {
fixedWidth = 20f,
margin = new RectOffset(0, 0, 0, 0),
padding = new RectOffset(0, 0, 0, 0)
};
floatFieldVal = EditorGUILayout.FloatField("", floatFieldVal);
// It emulates a texture
GUILayout.Label("◀▶", floatIconStyle)
// save group rect in a variable
if (Event.current.type == EventType.Repaint)
groupFloatFieldRect = groupRect;
} EditorGUILayout.EndHorizontal();
} EditorGUILayout.EndVertical();
} EditorGUILayout.EndHorizontal();
}
void OnInspectorUpdate() {
Repaint();
}
}
Of course all magic numbers are just for a quick example. Also guiskin loading and a GUIStyle creating shouldn't be in a loop (in this case in OnGUI). It's for a quick example too.
Test.guiskinis for removing default skin data and regulating other params if needed.
That's how the code above looks:
screenshot of a window.jpg
animated demonstation of fake field.gif

In Unity, how to access children component of the prefab using script?

Prefab hierarchy:
Button
1 Text
2 Image
I wish to access Image child component of the prefab.
I tried the following code:
GameObject addTypeButton = (GameObject)Instantiate(prefabButton);
addTypeButton.transform.SetParent(ParentPanel, false);
//set text
addTypeButton.GetComponentInChildren<Text>().text ="Some string";
//get image
WWW www = new WWW("someImagelink");
yield return www;
//set image
addTypeButton.GetComponentInChildren<Image>().sprite = Sprite.Create(www.texture, new Rect(0, 0, www.texture.width,www.texture.height), new Vector2(0, 0));
However, the above code is accessing Image script of Button (inbuilt).
Not the Image UI component.
How do I access UI Image (child) component?
Please help.
Thanks!
The Cause
GetComponentInChildren will also return component on the gameObject itself.
public Component GetComponentInChildren(Type t);
Returns the component of Type type in the GameObject or any of its children using depth first search.
Solutions
If the index of child GameObject 1 Text and 2 Image is fixed. You can get them by Transform.GetChild(index).
var buttonTransform = addTypeButton.transform;
var text = buttonTransform.GetChild(0);
var image = buttonTransform.GetChild(1);
If the order is not fixed, use Transform.Find(childName).
var buttonTransform = addTypeButton.transform;
var text = buttonTransform.Find("1 Text");
var image = buttonTransform.Find("2 Image");
The safest solution:
Drag your prefab to scene and attach a script to your Button GameObject:
using UnityEngine;
using UnityEngine.UI;
public class MyButton : MonoBehaviour
{
public Text text;
public Image image;
}
Then drag 1 Text and 2 Image to text and image field in the inspector of the Button.
Remember to press apply button and Ctrl+S to save that into your prefab.
In this way you can access the text and image like:
var mybutton = addTypeButton.GetComponent<MyButton>();
mybutton.text.text = "Some string";
mybutton.image.sprite = Sprite.Create(...);
Before accessing the child object, please make sure that that child object is enabled in prefab before enabling or disabling it after getting instantiated.
Like in my case, I had a UI Text game object with an image child object. I disabled it in the prefab with the hope to enable it by reference in the script after instantiating it. It only worked if I kept the image(child) component enabled in the prefab.
Try changing
addTypeButton.GetComponentInChildren<Image>().sprite = Sprite.Create(www.texture, new Rect(0, 0, www.texture.width,www.texture.height), new Vector2(0, 0));
To
addTypeButton.GetComponent<Image>().sprite = Sprite.Create(www.texture, new Rect(0, 0, www.texture.width,www.texture.height), new Vector2(0, 0));

Categories