Unity3D - Sizing LayoutElements Within A Layout Group - unity3d

I've been trying to work on this concept for a chat system. I just wanted to create a chat message prefab with a horizontal group. And three objects inside of it. Avatar(Image), TimeStamp and User(Text), Message (Text).
The avatar needs to have the height and width locked.
The timestamp and username need to have the height locked, but stretch to the content.
The message needs to take up the rest of the width in the panel, and also stretch in height
The issue I have right now is as follows: If the message has too much text in it, squish the username and timestamp. Also, this issue also occurs when instantiating additional versions of the prefab.
Visual of what the chat looks like so far.
The avatar's layout element settings
The timestamp and username layout element settings
The message layout element settings
And then the code I use to instantiate the prefab.
void sendMessage(string m) //for testing only.
{
Message newMessage = new Message();
//GetTimeStamp
newMessage.stamp = DateTime.Now;
//AppendUserName
newMessage.stampname = newMessage.stamp.ToString("[hh:mm:ss] ") + localPlayer.name + ": "; //Takes the time stamp and the player name
//GetMessageFromParser
newMessage.text = m;
//AddToMessageList
allMessages.Add(newMessage);
parser.text = "";
GameObject nm = Instantiate(messageUI);
Vector2 font = nm.transform.GetChild(1).GetComponent<TMP_Text>().GetPreferredValues(newMessage.stampname);
Debug.Log("Pref Size" + font.x);
nm.transform.GetChild(1).GetComponent<TMP_Text>().text = newMessage.stampname;
nm.transform.GetChild(2).GetComponent<TMP_Text>().text = newMessage.text;
nm.transform.SetParent(messageList.transform, true);
}

Related

Unity how to Create Texts via script?

I'm trying to create Texts via script. I want to create texts with the name of objects of a certain tag.
"For example, if I have two objects named Cube and Sphere and both have a tag of "TargetObj" then their names should be displayed as texts on the screen(in the case Cube, Sphere)"
I want to achieve this regardless of the number of objects. so a loop is needed.
Here is what I've tried so far.
[SerializeField] GameObject LevelCanvas;
targetObjects = GameObject.FindGameObjectsWithTag("TargetObj");
foreach (var obj in targetObjects)
{
Text mytext = LevelCanvas.AddComponent<Text>();
mytext.text = "Find " + obj.name;
Font ArialFont = (Font)Resources.GetBuiltinResource(typeof(Font), "Arial.ttf");
mytext.font = ArialFont;
mytext.material = ArialFont.material;
}
it only shows one object name although I have 2 more objects that were supposed to be shown as well.
You need to create a text object on a new gameobject.
static Text CreateText(Transform parent)
{
var go = new GameObject();
go.transform.parent = parent;
var text = go.AddComponent<Text>();
return text;
}
var mytext = CreateText(LevelCanvas.transform);
myext.text = ...
Many components in unity can only be added once per game object. That means you need a separate game object for every text you want to show.
A typical way to approach this is to create a "prefab" of a game object which has a text element already. This way you can set a default font and other settings.
Then in code you use the Instantiate method on that prefab and via GetComponent() you can access the text component instance to set the desired string to display.

Setting parent of an Image while populating inventory from code

I am developing an inventory system for my game. I have the UI set up, I have this:
As you can see, I have a Bag Panel, inside it there are a number of slots, that are the slots you can see in the right part of the image.
I am populating it this way:
inventoryPanel.SetActive(true);
GameObject bagObject= GameObject.Find("Bag");
List<GameObject> childrens = new List<GameObject>();
Transform[] listSlots = bagObject.GetComponentsInChildren<Transform>();
foreach(Transform child in listSlots)
{
if(!child.gameObject.name.Equals(bagObject.name))
childrens.Add(child.gameObject);
}
for (int i = 0; i < character.ItemsInInventory.Count; i++)
{
if (character.ItemsInInventory[i] != -1)
{
GameObject slot = childrens[i];
print("Slot: " + slot.name);
Image imagen = childrens[i].GetComponent<Image>();
string ruta = GetRutaSprite(character.ItemsInInventory[i]);
Sprite icono = Resources.Load<Sprite>(ruta);
imagen.name = "Item";
imagen.sprite = icono;
print("parent is " + slot.name);
imagen.transform.SetParent(slot.transform);
}
}
When I run this, I got this:
As you can see, Slot0 has dissapeared, and it is being substituted by item. What I would like to achieve is Item be a son of Slot0. I dont know why it happens this way, since I am setting the image's parent to the slot. In the print("Slot: " + slot.name); line it prints "Slot0", but in print("parent is " + slot.name); line it prints Item. Why is that? Also, the sword looks like it is behind something...it is not displayed correctly. I would like to show the icon AND the slot border.
How can I populate my inventory properly?
In your code, you just found the slot, get an image (which is border), and replace sprite on it with your items sprite.
You need to instantiate new image instead. It can be a prefab, or you can use your border image to create a copy from it (do not forget to change color). Or, if you have a separate Image inside slot, and you want to set the sprite for it, you need to get reference on it, but not on border. For this, you need a script on your slot, holding that reference.
for make a minimal changes in your code, you can do like this:
Sprite icono = Resources.Load<Sprite>(ruta);
Image newImage = Instantiate(imagen, slot.transform);
newImage .name = "Item";
newImage .sprite = icono;
print("parent is " + slot.name);
newImage.transform.SetParent(slot.transform);
But I defenitely recommend to rework it: at least, add a script on your slot, holding a reference to your second Image (which is not border).

RectTransform coordinates according to TextMesh Pro text size

I'm trying to create an InputField (TextMesh Pro) with a dynamic (its text content may vary) prefix.
This spectacular image should explain the goal.
https://imgur.com/a/qx1eXOa
So I set a TextMeshPro text to use as Prefix, and by script I was trying to "move" the TextArea accordingly.
The fact is, TextArea is a RectTransform, and I'm operating in a ScreenSpace render mode.
I was trying like this:
private TextMeshProGUI prefix;
private RecTransform textArea;
public void ChangePrefixTo(string newPrefix)
{
float oldWidth = prefix.preferredWidth;
prefix.text = newPrefix;
float newWidth = prefix.preferredWidth;
Vector2 newPos = new Vector2();
newPos.x = textArea.position.x + (newWidth - oldWidth);
newPos.y = textArea.position.y;
textArea.position = newPos;
}
, but the textArea gets shot into the stars.
How can I map the RectTransform position according to the size of a TextMeshPro text?
Thanks for the help and long live the whales
Instead of using a script to resize your prefix, you can group both of your elements in a Horizontal Layout Group, check only Width for Child Controls Size.
Add a Layout Element and set your Preferred Width to define the size of your TextArea.
The Prefix will scale according to his content, and it will push your text area as it grows.

Display loaded UI Image in Container with native size

I want to display UI logo images in their aspect ratio but in proper size that fit within its container. Already all logo images as per aspect ratio but few are too big or small compare to requirements.
At present this kind of thing happening:
Here is the code that I am using:
private void ShowGamePlayLogoViewed ()
{
for (int i = 0; i < DataCollection.companyDetailsList.Count; i++) {
Company company = DataCollection.companyDetailsList [i];
if (company.ViewedCounter > 0) {
GameObject companyItemObj = Instantiate (companyItemPref, gridViewContainer) as GameObject;
CompanyItem companyItem = companyItemObj.GetComponent<CompanyItem> ();
companyItem.companyId = company.CompanyId;
companyItem.UpdateCompanyLogo (company.CompanyLogo);
companyItem.UpdateCompanyName (company.CompanyName);
}
}
}
public void UpdateCompanyLogo (Sprite logoSprite)
{
logoImage.sprite = logoSprite;
logoImage.SetNativeSize ();
}
As you are seeing, logos overlapping the container. I want to display properly them in their respective containers also in aspect ratio too.
Let me clarify one more thing: all logos loaded from web server and they all are dynamic at a time, based on server data it will appear in mobile screen.
I have found solution through Unity Forum and I want to say thanks to #Hosnkobf for reply.
Here is the exact reply that worked for me properly:
adjust the image game object inside unity so that it has the height you are looking for. also adjust the anchors so that it scales with different resolutions properly.
Add an "AspectRatioFitter" component to the object. Make it "Height controls width".
Instead of logoImage.SetNativeSize (); do the following:
float aspectRatio = logoSprite.rect.width / logoSprite.rect.height;
var fitter = logoImage.GetComponent<UnityEngine.UI.AspectRatioFitter>();
fitter.aspectRatio = aspectRatio;
I hope this become useful to other members :)

Unity3D ScrollView/ScrollBar from bottom to top

I'm trying to create a terminal emulator in Unity. I'm able to enter text and have it displayed as output above, and the output will generate a scrollbar when enough text is entered via a ScrollView.
However I can't figure out how to make the scrollbar jump to the bottom when a user enters text.
This is the method used to draw the GUI Window.
// relevant instance variables
private Rect _windowPosition = new Rect();
List<string> output = new List<string>();
string user_input = "";
Vector2 scroll_pos = new Vector2(1,1);
...
...
...
private void onWindow(int windowID){
GUILayout.BeginVertical();
// Output is placed here. Is Scrollable
scroll_pos = GUILayout.BeginScrollView( scroll_pos );
GUILayout.Label( String.Join("\n", output.ToArray()) );
GUILayout.EndScrollView();
// Output ends here. Next piece is user input
user_input = GUILayout.TextField(user_input);
if (Event.current.Equals(Event.KeyboardEvent("return"))) {
output.Add(user_input);
user_input = ""; //clears the TextField
scroll_pos.x = 1;
scroll_pos.y = 1;
}
GUILayout.EndVertical();
GUI.DragWindow();
}
From my searching, I'd seen it said that I should just change the scroll_pos variable, since that's used to control/read the scrollbar's position. The scrollbar's value is normalized between 0 - 1.
I've tried forcing the scroll_pos values to be both, 0 and 1, but it has no impact. Beyond this I'm not sure how to approach the problem.
Try to change
scroll_pos.x = 1;
scroll_pos.y = 1;
to
scroll_pos.y += 9999;
if you want to force scroll on horizontal do it with X too but usually consoles doesn't generates horizontal scroll, they force line breaks based on how many columns you configure.