Skip to content

Foldable options sections #176

@iwanPlays

Description

@iwanPlays

The options are quire long by now. Modern game have sections. Instead of section tabs/buttons, how about making the sections foldable and remembering the state?

To get this:

Image
Image

Drag the following onto the Canvas Pause Menu/Pause Menu/Options object in the Objects/Canvas Pause Menu.prefab

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class AutoFoldout : MonoBehaviour
{
    void Start()
    {
        Transform content = transform.Find("Viewport/Content");

        List<Transform> labels = new();

        foreach (Transform child in content)
        {
            if (child.name.StartsWith("Label "))
                labels.Add(child);
        }

        for (int i = 0; i < labels.Count; i++)
        {
            Transform label = labels[i];

            // WRAPPER OBJECT
            GameObject header = new GameObject(label.name + "_Header");

            header.transform.SetParent(content, false);

            int originalIndex = label.GetSiblingIndex();
            header.transform.SetSiblingIndex(originalIndex);

            RectTransform headerRect = header.AddComponent<RectTransform>();
            headerRect.anchorMin = new Vector2(0, 1);
            headerRect.anchorMax = new Vector2(1, 1);
            headerRect.pivot = new Vector2(0.5f, 0.5f);
            headerRect.sizeDelta = new Vector2(0, 30);

            Image image = header.AddComponent<Image>();
            image.color = new Color(0, 0, 0, 0.001f);

            Button button = header.AddComponent<Button>();

            // MOVE LABEL INTO HEADER
            label.SetParent(header.transform, false);

            Text text = label.GetComponent<Text>();
            if (text)
                text.raycastTarget = false;

            RectTransform labelRect = label.GetComponent<RectTransform>();
            labelRect.anchorMin = new Vector2(0, 0);
            labelRect.anchorMax = new Vector2(1, 1);
            labelRect.offsetMin = new Vector2(10, 0);
            labelRect.offsetMax = new Vector2(-40, 0);

            // ARROW
            GameObject arrowObj = new GameObject("Arrow");

            arrowObj.transform.SetParent(header.transform, false);

            RectTransform arrowRect = arrowObj.AddComponent<RectTransform>();
            arrowRect.anchorMin = new Vector2(1, 0);
            arrowRect.anchorMax = new Vector2(1, 1);
            arrowRect.pivot = new Vector2(1, 0.5f);
            arrowRect.sizeDelta = new Vector2(30, 0);
            arrowRect.anchoredPosition = new Vector2(-10, 0);

            Text arrow = arrowObj.AddComponent<Text>();
            arrow.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
            arrow.alignment = TextAnchor.MiddleCenter;
            arrow.color = Color.white;
            arrow.raycastTarget = false;

            // CONTENT
            List<GameObject> controlled = new();

            int start = originalIndex + 1;

            int end = i + 1 < labels.Count
                ? labels[i + 1].GetSiblingIndex()
                : content.childCount;

            for (int j = start; j < end; j++)
            {
                Transform child = content.GetChild(j);

                if (child != header.transform)
                    controlled.Add(child.gameObject);
            }

            string key = "foldout_" + label.name;

            bool open = PlayerPrefs.GetInt(key, 1) == 1;

            void Apply()
            {
                arrow.text = open ? "▼" : "◄";

                foreach (var obj in controlled)
                    obj.SetActive(open);

                LayoutRebuilder.ForceRebuildLayoutImmediate(
                    content.GetComponent<RectTransform>()
                );
            }

            button.onClick.AddListener(() =>
            {
                open = !open;

                PlayerPrefs.SetInt(key, open ? 1 : 0);
                PlayerPrefs.Save();

                Apply();
            });

            Apply();
        }
    }
}

License: CC0 and especially Wolfire can do whatever they want with this.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions