1
\$\begingroup\$

I'm new to Unity and C#, so I'll try to be as thorough as possible. I'm working on an objectives menu for my game (in Unity) and I'm not sure how to implement a behavior I'd like it to have (the sorting part).

Desired behavior:

  • List all the current objectives
  • When an objective is completed, it disappears from the list
  • Sort objectives such that they're all in a column with no extra rows between them (this is the part I can't figure out)

Current setup and behavior:

Objective information (name, description, visibility) is stored as public strings in a script attached to empty gameobjects.

Empty gameObjects with Objective script attached The attached objective script and its parameters

I set up 10 TextMeshPro text boxes I'm calling "slots". 5 for objective names, 5 for objective descriptions.

TextMeshPro slots

I have a script called ObjectiveHandler that takes information from the Objectives scripts attached to it and "writes" it to the slots. Here's what that looks like in the inspector.

ObjectiveHandler script with objective gameobjects and TMP UGUI plugged in

And the script itself.

using UnityEngine;
using TMPro;

public class ObjectiveHandler : MonoBehaviour
{

private TextMeshProUGUI[,] slots = new TextMeshProUGUI[2, 5]; //Column 0 is for names, column 1 is for descriptions

[Header("Name")]
public TextMeshProUGUI name1;
public TextMeshProUGUI name2;
public TextMeshProUGUI name3;
public TextMeshProUGUI name4;
public TextMeshProUGUI name5;
[Header("Description")]
public TextMeshProUGUI desc1;
public TextMeshProUGUI desc2;
public TextMeshProUGUI desc3;
public TextMeshProUGUI desc4;
public TextMeshProUGUI desc5;

private bool[] slotInUse = new bool[5];
//Indicates if the slot is in use.

private Objective objectiveInfo; //a sort of "go between" between the Objective script and this one.

public GameObject [] objList = new GameObject[5]; //array of objective gameobjects
private bool[,] objStatus = new bool[2, 5]; //Column 0 is objective visibility, column 1 is if the objective is in use
private int index = 0;
private string objectiveName; // Holds the current indexed objective's name
private string objectiveDesc; // Holds the current indexed objective's description

private void Start()
{

//Load names UGUI into slots array
    slots[0, 0] = name1;
    slots[0, 1] = name2;
    slots[0, 2] = name3;
    slots[0, 3] = name4;
    slots[0, 4] = name5;
    //Load desc UGUI into slots array
    slots[1, 0] = desc1;
    slots[1, 1] = desc2;
    slots[1, 2] = desc3;
    slots[1, 3] = desc4;
    slots[1, 4] = desc5;
}
private void Update()
{
    Sort();
}
void Sort()
{
    for (index = 0; index < 5; index++)
    {
        objectiveInfo = objList[index].GetComponent<Objective>(); //variables in Objective
        objectiveName = objectiveInfo.objectiveName;
        objectiveDesc = objectiveInfo.objectiveDescription;
        objStatus[0, index] = objectiveInfo.visibility;
        if ((objStatus[0,index] == true && objStatus[1,index] == false) && slotInUse[index] == false) //if the obj is visible and not in use
        {
             WriteUI(objectiveName, objectiveDesc, slots[0, index], slots[1, index]);
             objStatus[1, index] = true; //this obj is now in use
             slotInUse[index] = true;
        }
        else if ((objStatus[0, index] == false && objStatus[1, index] == false)) //if the obj is not visible and not in use
        {
            //Do nothing
        }
        else if ((objStatus[0, index] == false && objStatus[1, index] == true) && slotInUse[index] == true)
        {
            //ERASE
            EraseUI(slots[0, index], slots[1, index]);
            objStatus[1, index] = false; //this obj is now in use
            slotInUse[index] = false;
        }
    }
}
void WriteUI(string Name, string Desc, TextMeshProUGUI uiName, TextMeshProUGUI uiDesc) //writes ui text to TMP UGUI and indicates that the slot is in use
{
    Name = objectiveName;
    Desc = objectiveDesc;
    uiName.SetText(Name);
    uiDesc.SetText(Desc);
}

void EraseUI(TextMeshProUGUI uiName, TextMeshProUGUI uiDesc)
{
    uiName.SetText("");
    uiDesc.SetText("");
}
}

So far the objective names, descriptions and visibility are being read by the script, they're erasing when the objective is set to !visible and writing when it's set to visible.

What I can't figure out

I'd like to make it so that when an objective is set to !visible and the info in its slot is erased (and therefore, the slot is set to !inUse) the information in the boxes below it are shifted up.

Example scenario

There are currently 5 objectives. The objective in slot 3 is completed, so its visibility parameter is set to false (!visible, or "invisible"). Slot 3 is now empty, so the information in slots 4 and 5 shift into slots 3 and 4, respectively.

I concede that there may be better ways of doing this and I'm open to suggestions for improvement. If you can think of a way for me to do this with the setup I already have I'd be elated.

Many thanks in advance!

\$\endgroup\$
4
  • \$\begingroup\$ If you want the transition to happen instantly, a VerticalLayoutGroup can accomplish this out of the box. If you want to animate the shift, you need a little extra scripting to choreograph the transition. \$\endgroup\$
    – DMGregory
    Commented Nov 24, 2020 at 19:06
  • \$\begingroup\$ @DMGregory Thanks! I'll look that up! No plans for animations yet. I'm keeping the project scope small as I prototype. \$\endgroup\$
    – Hyperific
    Commented Nov 24, 2020 at 19:14
  • \$\begingroup\$ @DMGregory Wow, that's elegantly simple! Vertical Layout Group does exactly what I need it to. \$\endgroup\$
    – Hyperific
    Commented Nov 24, 2020 at 19:39
  • \$\begingroup\$ Want to write up your solution as an Answer? \$\endgroup\$
    – DMGregory
    Commented Nov 24, 2020 at 19:52

1 Answer 1

2
\$\begingroup\$

Per @DMGregory Unity's "Vertical Layout Group" component handles this natively.

I created an empty gameObject and made all of the "slots" for objective names/descriptions children of it. I added the Vertical Layout Group component to the new parent gameObject.

Now, when my objectives are set to !visible, I've scripted it such that they'll disable the gameobject for that slot (the empty gameobject whose children are the name and description TMP text fields), and Vertical Layout Group will automatically rearrange the remaining objectives. I've removed the ObjectiveHandler script and now the Objective script attached to each objective gameobject handles toggling visibility and writing to UI.

using TMPro;
using UnityEngine;

public class Objective : MonoBehaviour
{
public string objectiveName;
public string objectiveDescription;
public bool visibility;
public GameObject parent;
public TextMeshProUGUI objectiveNameUI;
public TextMeshProUGUI objectiveDescriptionUI;

void Start()
{
    objectiveNameUI.SetText(objectiveName);
    objectiveDescriptionUI.SetText(objectiveDescription);
}
void Update()
{
    if (visibility == true)
    {
        parent.SetActive(true);
    }
    else
    {
        parent.SetActive(false);
    }
}

}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .