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

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.

Related

Instantiate a Object below another object instead of above

First of all, i created a GIF to show what is currently happen.
GIF with my current problem
and
Awhat I want
I have a List of GameObject which add the bodyParts temp and Instantiate it in the correct time and position.
Now this is working like expected, but i want this new bodyParts below another object instead of above.
As you can see the Head is "under" the new body parts, but it should always on Top and every new part should spawn under the next. (only should looks like! I dont want to change the Z position.)
i tried :
bodyParts.transform.SetAsFirstSibling();
to change the Hierarchy, but this do nothing. I also can drag and drop the Clones to a other position in Hierarchy but they just stay at the same position (above another).
Is this possible and what should i have to do?
Here some of my Code which makes the process:
private void CreateBodyParts()
{
if (snakeBody.Count == 0)
{
GameObject temp1 = Instantiate(bodyParts[0], transform.position, transform.rotation, transform);
if (!temp1.GetComponent<MarkerManager>())
temp1.AddComponent<MarkerManager>();
if (!temp1.GetComponent<Rigidbody2D>())
{
temp1.AddComponent<Rigidbody2D>();
temp1.GetComponent<Rigidbody2D>().gravityScale = 0;
}
snakeBody.Add(temp1);
bodyParts.RemoveAt(0);
}
MarkerManager markM = snakeBody[snakeBody.Count - 1].GetComponent<MarkerManager>();
if (countUp == 0)
{
markM.ClearMarkerList();
}
countUp += Time.deltaTime;
if (countUp >= distanceBetween)
{
GameObject temp = Instantiate(bodyParts[0], markM.markerList[0].position, markM.markerList[0].rotation, transform);
if (!temp.GetComponent<MarkerManager>())
temp.AddComponent<MarkerManager>();
if (!temp.GetComponent<Rigidbody2D>())
{
temp.AddComponent<Rigidbody2D>();
temp.GetComponent<Rigidbody2D>().gravityScale = 0;
}
snakeBody.Add(temp);
bodyParts.RemoveAt(0);
temp.GetComponent<MarkerManager>().ClearMarkerList();
countUp = 0;
}
}
Finally i found the working Solution.
It has nothing to do with which hierarchy order GameObjects spawn in.
Just the Layer and the LayerOrder are responsible for it.
So I give my parent object a specific layer name (manually in the inspector under "Additional Settings" or programmatically)
I chose the programmatic way...
Any newly spawned GameObject that is Child would get a lower number
yourGameObject.GetComponent<Renderer>().sortingLayerID = SortingLayer.NameToID("Player");
yourGameObject.GetComponent<Renderer>().sortingOrder = -snakeBody.Count;

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

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");

Unity c# Image not appearing in UI canvas panel

I have written a method MakeCard() that instantiates a card prefab (having a button) and adds an image to it through the script that uses an array of sprites but the image does not appear in the panel ("Card Pool") under which I'm setting its transform to. I have another method having the same code and it displays the card (with image) in some other panel (works perfectly fine). But the code isn't working for "Card Pool" panel. I am even displaying the index and the name of the card in logs to check if its fine and its fine. pls help!
EDIT: If i change the parent panel of a card from "Hand" to "Card Pool" it displays fine but if i instantiate the card and set its Parent as "Card Pool" it does not display. Hope this info makes any sense!
public void MakeCard(int index)
{
print("Making Card..." + index);
var cardCopy = Instantiate(cardObj, new Vector3(0, 0, 0),
Quaternion.identity);
var newCard = cardCopy.GetComponent<Card>();
newCard.SetFace(myData.shuffleIndex[index]);
print(newCard.GetName());
cardCopy.transform.SetParent(GameObject.Find("Card Pool").transform);
cardCopy.GetComponent<Button>().interactable = false;
}
the other code thats works perfectly fine is:
public void DistributeMyCards()
{
for(int temp = myData.shuffleIndexToStartWith + 12; myData.shuffleIndexToStartWith < temp; myData.shuffleIndexToStartWith++)
{
//localHand.Add(myData.shuffleIndexToStartWith);
Debug.Log("inside for: " + myData.shuffleIndexToStartWith);
var cardCopy = Instantiate(cardObj, new Vector3(0, 0, 0), Quaternion.identity);
var newCard = cardCopy.GetComponent<Card>();
newCard.SetFace(myData.shuffleIndex[myData.shuffleIndexToStartWith]);
cardCopy.transform.SetParent(GameObject.Find("Hand").transform);
var owner = cardCopy.GetComponent<CardOwner>();
owner.photonPlayer = PlayerNetwork.Instance.me;
owner.setCardId(myData.shuffleIndexToStartWith);
if (!PhotonNetwork.isMasterClient)
{
DisableThrowLocal(cardCopy);
}
localHand.Add(cardCopy);
}
}
PS. Its a MP game but I guess that doesn't have anything to do with this problem.
ok so I found the problem. I had to tick the child controls size (height and width) under horizontal layout group component attached to the panel just as the "Hand panel".

How can I display an element relative to an other in Unity without "moving effects"?

I'm starting with unity since a few weeks and i'm stuck with some displaying problems.
You can see what I want to achieve here :
On several of my pages I have a title with an icon on the left and I want it to be at 40px on the left of the title, no matter the size of it.
So I did a prefab with an empty Text for the title, an empty RawImage for the icon and I put that prefab on the center top of the screen.
I link the following C# script on my prefab with the 'string' parameter for the title and the 'Texture' parameter for the icon to display.
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
public class TitlePage : MonoBehaviour {
public string title;
public Texture texture;
private Text titlePageText;
private RawImage iconPage;
void Awake()
{
titlePageText = gameObject.GetComponentsInChildren<Text>().First();
iconPage = gameObject.GetComponentsInChildren<RawImage>().First();
}
void Start()
{
titlePageText.text = title;
}
void Update()
{
iconPage.rectTransform.anchoredPosition = new Vector2(-titlePageText.rectTransform.rect.width / 2 - 40, iconPage.rectTransform.anchoredPosition.y);
iconPage.texture = texture;
}
}
The title is set well in the "Start" function, the problem is with the icon. The code in the "Update" function put the icon at the wanted position if it's in the "Update" function because in the "Start", 'titlePageText.rectTransform.rect.width' give 0. But with that code in the "Update" function, the icon start with a display by default in the center and nearly instantly it moves on the left but we can see that position changing.
Maybe there's a solution to avoid that ?
Maybe I started in a wrong way ?
(I don't put my title's text hardly because I want my application to be multilingual so instead of displaying the 'string' in parameter, I'll display the corresponding translation and it's why I can't set a fixed position because two translations are not the same length.)
Thanks in advance !
I am not entirely sure what your problem is exactly, but it does sound like you are trying to solve something from a slightly awkward angle. IS there a reason for not using Unity's layout (or even auto layout groups) to achieve your goal?
I just did a quick test: added 'Content Size Fitter component' to the text object with Horizontal set to Preferred, then placed an image as a child of that text object, when I set the anchor X to 0 and pivot X to 1, the image now follows the left hand side of the text, no matter what size.
This has the advantage of doing absolutely zero work unless text (or its position) changes, in which case everything is handled automagically by Unity's UI system
It sounds like the width of the titlePageText is not obtaining a value immediately in Start(), which is why it is initially zero. After a couple of frames when a non-zero value is found, it finally shifts your iconPage over but your object already has a texture at this point. You really shouldn't be doing this in Update() because it means it will constantly be setting the anchoredPosition and texture of your iconPage every frame.
One solution to avoid this would be to use a coroutine with yield to wait for your components to initialize and then set the position. Note that this is a bit of a hack since you are introducing some forced loading time.
public string title;
public Texture texture;
private Text titlePageText;
private RawImage iconPage;
void Awake()
{
titlePageText = gameObject.GetComponentsInChildren<Text>().First();
iconPage = gameObject.GetComponentsInChildren<RawImage>().First();
}
void Start()
{
titlePageText.text = title;
StartCoroutine(initializeComponenets());
}
IEnumerator initializeComponenets()
{
yield return new waitForSeconds(.05f); //might need longer wait here
iconPage.rectTransform.anchoredPosition = new Vector2(-titlePageText.rectTransform.rect.width / 2 - 40, iconPage.rectTransform.anchoredPosition.y);
iconPage.texture = texture;
}

Change color of multiple game objects in Unity 5

I would like to change the color of multiple gameobjects in Unity using a single script. I'm kinda lost in the way how to do it. I'm new to Unity and this is some sort of basic training for me.
Unity version: 5.3.4
Observed Behavior:
Added the same script to the other gameobjects and all change to the same color
Expected Behavior:
Change the color of the gameobjects individually
List of things tried:
Using the -FindGameObject-
Tried to acces the materials using the -GameObject-
Tried both at the same time
Thinking in multiple scripts to achieve the results I want
Here's the code
C#:
using UnityEngine;
using System.Collections;
public class ChangeColor : MonoBehaviour
{
//If I change these variables to -GameObject-
//It blocks me to access the renderer
//Making the variables public doesn't work either
private Renderer cube;
private Renderer sphere;
void Start ()
{
//Tried here the -FindGameObjectWithTag-
cube = GetComponent<Renderer>();
sphere = GetComponent<Renderer>();
}
void Update ()
{
if(Input.GetKeyDown(KeyCode.A))
{
//Tried here the -FindGameObjectWithTag-
cube.material.color = Color.red;
}
if(Input.GetKeyDown(KeyCode.S))
{
//Tried here the -FindGameObjectWithTag-
sphere.material.color = Color.green;
}
}
}
Maybe I'm doing something wrong, as I said I'm new to Unity, I kindly accept any help, if it is noobfriendly the better.
Thanks
There are a couple different ways you can go about doing this.
If they are all under the same parent without other objects there too you could loop through the children and change their colors
GameObject Parent = GameObject.Find("ParentObject");
for( int x = 0; x > Parent.transform.childCount; x++);
{
Parent.transform.GetChild(x).GetComponent<Renderer>().material.color = Color.red;
}
If there are less than say 20ish, you could create a list and simply drag and drop each transform into the inspector after changing the list's size to the number of objects you have.
/*Make sure you have Using "System.Collections.Generic" at the top */
//put this outside function so that the inspector can see it
public List<Transform> Objs;
// in your function put this (when changing the color)
foreach(Transform Tform in Objs){
Tform.GetComponent<Renderer>().material.color = Color.red;
}
Similarly to 2, if you want to do it all in code you can do
//Outside function
public List<Transform> Objs;
//inside function
Objs.Add(GameObject.Find("FirstObject").transform);
Objs.Add(GameObject.Find("SecondObject").transform);
//... keep doing this
//now do the same foreach loop as in 2
You could search by tag (if you have a lot of objects) (i would imagine that this would take a bit longer just because it is going through each component but I have no evidence to back that up)
//Outside function
public GameObject[] Objs;
//inside function
Objs = GameObject.FindGameObjectsWithTag("ATagToChangeColor");
foreach(Transform Tform in Objs){
Tform.GetComponent<Renderer>().material.color = Color.red();
}
And about this comment:
//If I change these variables to -GameObject-
//It blocks me to access the renderer
//Making the variables public doesn't work either
If you make them of type GameObject then you should easily be able to access the renderer simply by doing this:
public GameObject cube;
public void Start(){
cube.GetComponent<Renderer>().material.color = Color.red();
}
And making the variables public allows unity's inspector to see the variables so that you can change them in the unity editor without having to open the script.

Categories