Skip to content

Instantly share code, notes, and snippets.

@downthecrop
Last active January 2, 2024 08:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save downthecrop/49d70b91e758f9490b10b906f02d0a4b to your computer and use it in GitHub Desktop.
Save downthecrop/49d70b91e758f9490b10b906f02d0a4b to your computer and use it in GitHub Desktop.
GPT Being Extremely Smart

Person:

  • I'd like to add a new "suboption" to the "select" of a menu item in my gridnavigator that will allow users to cycle through different options for esxample << Option1 >>, << Option2>> by using the left and right inputs that are already mapped to "movecursor" events
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GridNavigator : MonoBehaviour
{
    public enum STYLES
    {
        SELECTION_MARKER,
        SELECTION_BACKGROUND
    }

    [SerializeField]
    private STYLES style;

    public GridLayoutGroup gridLayout;
    public int rows;
    public int columns;
    public bool hidePreviousInterface = false;
    public bool resetIndexOnOpen = false;
    public GameObject selectionMarker;  // An object that highlights the selected cell

    private int currentRow = 0;
    private int currentColumn = 0;
    private GameObject selection;
    private List<Transform> allChildren;

    void Start()
    {
        allChildren = new List<Transform>();
        foreach (Transform child in gridLayout.transform)
        {
            if(child.GetComponent<Button>() != null)
                allChildren.Add(child);
        }
        // Delay the initial update to ensure all UI elements are correctly positioned
        StartCoroutine(DelayedUpdate());
    }

    IEnumerator DelayedUpdate()
    {
        yield return new WaitForEndOfFrame(); // Wait for the end of the frame
        UpdateMarker();
    }

    public void ResetPosition()
    {
        currentColumn= 0;
        currentRow = 0;
        StartCoroutine(DelayedUpdate());
    }

    public void UpdateMarker()
    {
        if (style == STYLES.SELECTION_BACKGROUND && selection != null)
        {
            if (selection.GetComponent<BaseBackgroundColor>())
            {
                var color = selection.GetComponent<BaseBackgroundColor>().backgroundColor;
                selection.GetComponent<Image>().color = color;
            }
            else {
                selection.GetComponent<Image>().enabled = false;
            }
        }
        int index = currentRow * columns + currentColumn;

        if (index >= 0 && index < allChildren.Count)
        {
            Debug.Log($"GridNavigator Marker moved to index {index}");
            selection = allChildren[index].gameObject;
            if (style == STYLES.SELECTION_MARKER) {
                if (selectionMarker.activeSelf == false)
                {
                    selectionMarker.SetActive(true);
                }
                selectionMarker.transform.position = selection.transform.position;
            }
            else if (style== STYLES.SELECTION_BACKGROUND)
            {
                if (selection.GetComponent<BaseBackgroundColor>())
                {
                    // This image is already enabled. Just change the color a bit
                    var color = selection.GetComponent<BaseBackgroundColor>().backgroundColor;
                    selection.GetComponent<Image>().color = new Color(color.r+0.1f, color.g + 0.1f, color.b + 0.1f, color.a - 0.1f);
                }
                else {
                    selection.GetComponent<Image>().enabled = true;
                }
            }
        }
    }

    public void MoveCursorUp()
    {
        currentRow = Mathf.Max(0, currentRow - 1);
        UpdateMarker();
    }

    public void MoveCursorDown()
    {
        currentRow = Mathf.Min(rows - 1, currentRow + 1);
        UpdateMarker();
    }

    public void MoveCursorLeft()
    {
        currentColumn = Mathf.Max(0, currentColumn - 1);
        UpdateMarker();
    }

    public void MoveCursorRight()
    {
        currentColumn = Mathf.Min(columns - 1, currentColumn + 1);
        UpdateMarker();
    }

    public void Select()
    {
        int index = currentRow * columns + currentColumn;
        if (index >= 0 && index < allChildren.Count)
        {
            Button selectedButton = allChildren[index].GetComponentInChildren<Button>();
            if (selectedButton != null)
            {
                selectedButton.onClick.Invoke();
            }
            else {
                Debug.Log($"Attemped to select a button without an onClick action at index: {index}");
            }
        }
    }

}

ChatGPT:

  • To implement the functionality where users can cycle through different options (e.g., << Option1 >>, << Option2 >>) by using the left and right inputs already mapped to "move cursor" events in your GridNavigator script, you'll need to make some modifications. This will involve adding a way to handle multiple options within a single grid cell and intercepting the left and right input events to change options instead of moving the cursor when a multi-option cell is selected.

Here's an outline of the steps and the updated code to achieve this:

  1. Define a New Class for Multi-Option Items : Create a class that represents a grid cell item with multiple options.
  2. Modify the GridNavigator Class : Update the GridNavigator class to support these multi-option items.
  3. Intercept Left/Right Cursor Movements : Change the behavior of MoveCursorLeft and MoveCursorRight methods when the selected item is a multi-option item.
  4. Display Current Option : Update the display to show the currently selected option in a multi-option cell.

Here's the updated code:

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

public class GridNavigator : MonoBehaviour
{
    public enum STYLES
    {
        SELECTION_MARKER,
        SELECTION_BACKGROUND
    }

    [SerializeField]
    private STYLES style;

    public GridLayoutGroup gridLayout;
    public int rows;
    public int columns;
    public bool hidePreviousInterface = false;
    public bool resetIndexOnOpen = false;
    public GameObject selectionMarker;  // An object that highlights the selected cell

    private int currentRow = 0;
    private int currentColumn = 0;
    private GameObject selection;
    private List<Transform> allChildren;
    private Dictionary<int, MultiOptionItem> multiOptionItems = new Dictionary<int, MultiOptionItem>();

    void Start()
    {
        allChildren = new List<Transform>();
        foreach (Transform child in gridLayout.transform)
        {
            if (child.GetComponent<Button>() != null)
                allChildren.Add(child);

            // Check if the child has a MultiOptionItem component
            var multiOption = child.GetComponent<MultiOptionItem>();
            if (multiOption != null)
            {
                multiOptionItems.Add(allChildren.Count - 1, multiOption);
            }
        }
        StartCoroutine(DelayedUpdate());
    }

    // Other existing methods remain unchanged...

    public void MoveCursorLeft()
    {
        if (IsCurrentSelectionMultiOption())
        {
            multiOptionItems[currentRow * columns + currentColumn].PreviousOption();
        }
        else
        {
            currentColumn = Mathf.Max(0, currentColumn - 1);
            UpdateMarker();
        }
    }

    public void MoveCursorRight()
    {
        if (IsCurrentSelectionMultiOption())
        {
            multiOptionItems[currentRow * columns + currentColumn].NextOption();
        }
        else
        {
            currentColumn = Mathf.Min(columns - 1, currentColumn + 1);
            UpdateMarker();
        }
    }

    private bool IsCurrentSelectionMultiOption()
    {
        int index = currentRow * columns + currentColumn;
        return multiOptionItems.ContainsKey(index);
    }
}

// Define a new class for multi-option items
public class MultiOptionItem : MonoBehaviour
{
    public List<string> options = new List<string>();
    private int currentOptionIndex = 0;
    private Text optionText;

    void Awake()
    {
        optionText = GetComponentInChildren<Text>();
        UpdateOptionDisplay();
    }

    public void NextOption()
    {
        currentOptionIndex = (currentOptionIndex + 1) % options.Count;
        UpdateOptionDisplay();
    }

    public void PreviousOption()
    {
        if (currentOptionIndex == 0)
            currentOptionIndex = options.Count - 1;
        else
            currentOptionIndex--;

        UpdateOptionDisplay();
    }

    private void UpdateOptionDisplay()
    {
        if (optionText != null && options.Count > 0)
            optionText.text = options[currentOptionIndex];
    }
}

This updated GridNavigator script now includes logic to handle multi-option items in the grid. The MultiOptionItem class is a new component that you can attach to any GameObject in your grid layout. It contains a list of options and methods to cycle through them. The MoveCursorLeft and MoveCursorRight methods in GridNavigator are modified to change the option instead of moving the cursor when a multi-option item is selected.

Person:

  • amazing work. that's a perfect solution. I would like to use this for a resolution selection menu. We should only consider resolutions which match the current framerate of the monitor. Create a script I can attached to a gameobject (button in the menu) that will collect the resultions and add each of them as an option in a multimeuitem component

ChatGPT:

  • To create a script that can be attached to a GameObject (such as a button in the menu) for resolution selection, we'll create a script that gathers available screen resolutions that match the current monitor's refresh rate. This script will then populate these resolutions into a MultiOptionItem component, which you've already set up to cycle through multiple options.

The script will:

  1. Fetch available resolutions using Screen.resolutions.
  2. Filter resolutions to match the current refresh rate of the monitor.
  3. Populate these filtered resolutions into the MultiOptionItem component attached to the same GameObject.

Here's the script:

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

public class ResolutionSelector : MonoBehaviour
{
    private MultiOptionItem multiOptionItem;

    void Start()
    {
        multiOptionItem = GetComponent<MultiOptionItem>();

        if (multiOptionItem != null)
        {
            PopulateResolutions();
        }
        else
        {
            Debug.LogError("MultiOptionItem component is not attached to this GameObject.");
        }
    }

    private void PopulateResolutions()
    {
        Resolution[] resolutions = Screen.resolutions;
        int currentRefreshRate = Screen.currentResolution.refreshRate;
        List<string> matchingResolutions = new List<string>();

        foreach (Resolution res in resolutions)
        {
            if (res.refreshRate == currentRefreshRate)
            {
                string resolutionString = res.width + " x " + res.height;
                if (!matchingResolutions.Contains(resolutionString))
                {
                    matchingResolutions.Add(resolutionString);
                }
            }
        }

        multiOptionItem.options = matchingResolutions;
    }
}

This script should be attached to the same GameObject as the MultiOptionItem component. When the scene starts, it automatically populates the MultiOptionItem's options with resolutions that match the current refresh rate.

Keep in mind that this script assumes the presence of a MultiOptionItem component on the GameObject to which it's attached. Ensure that this component is present for the script to work correctly. The script also filters out duplicate resolutions to keep the list concise.

Person:

  • almost perfect. just need to make sure we force a refresh of the options with UpdateOptionDisplay when the items are added to the menu

ChatGPT:

  • You're right, it's important to ensure that the display of options is updated immediately after the resolutions are added to the MultiOptionItem. To achieve this, you can call the UpdateOptionDisplay method of the MultiOptionItem class after populating the list of resolutions. This will refresh the UI to show the first option right away.

Here's the modified ResolutionSelector script with the necessary update:

using UnityEngine;
using System.Collections.Generic;

public class ResolutionSelector : MonoBehaviour
{
    private MultiOptionItem multiOptionItem;

    void Start()
    {
        multiOptionItem = GetComponent<MultiOptionItem>();

        if (multiOptionItem != null)
        {
            PopulateResolutions();
            multiOptionItem.UpdateOptionDisplay();  // Refresh the display
        }
        else
        {
            Debug.LogError("MultiOptionItem component is not attached to this GameObject.");
        }
    }

    private void PopulateResolutions()
    {
        Resolution[] resolutions = Screen.resolutions;
        int currentRefreshRate = Screen.currentResolution.refreshRate;
        List<string> matchingResolutions = new List<string>();

        foreach (Resolution res in resolutions)
        {
            if (res.refreshRate == currentRefreshRate)
            {
                string resolutionString = res.width + " x " + res.height;
                if (!matchingResolutions.Contains(resolutionString))
                {
                    matchingResolutions.Add(resolutionString);
                }
            }
        }

        multiOptionItem.options = matchingResolutions;
    }
}

This modification ensures that once the resolutions are set in the options list of MultiOptionItem, the UI will immediately reflect these changes. This script should work seamlessly with the setup you have and provide a user-friendly way to select screen resolutions.

Person:

  • nice. You're truly amazing this is really something that would have taken at least a few hours before you existed. Thank you!

ChatGPT:

  • You're very welcome! I'm glad I could help streamline the process for you. If you have any more questions or need further assistance with your project, feel free to ask. Happy coding, and best of luck with your development!
@downthecrop
Copy link
Author

downthecrop commented Jan 2, 2024

Only change I made here that's not documented is making UpdateOptionDisplay() a public function which GPT forgot to mention but is self explanatory.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment