Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Collection as PersistentVariables

Discussion in 'Localization Tools' started by shelim, Mar 18, 2022.

  1. shelim

    shelim

    Joined:
    Aug 26, 2017
    Posts:
    28
    Is there a pre-defined
    PersistentVariables
    variable for collection (list, array, anything serializable) of objects?

    Specifically I need an array of
    LocalizedString
    passed to other
    LocalizedString
    with refresh event calling when any children
    LocalizedString
    triggers change for any reason.
     
  2. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    No but you can create one. You need to implement IVariable and IVariableGroup.

    It took a little playing around but I managed to get something that works:
    You can add this to a group or as a local variable.
    Code (csharp):
    1.  
    2. [DisplayName("LocalizedString Array")]
    3. public class LocalizedStringArray : IVariableGroup, IVariable
    4. {
    5.     public List<LocalizedString> localizedStrings = new List<LocalizedString>();
    6.  
    7.     public object GetSourceValue(ISelectorInfo selector) => this;
    8.  
    9.     public bool TryGetValue(string key, out IVariable value)
    10.     {
    11.         value = localizedStrings[int.Parse(key)];
    12.         return true;
    13.     }
    14. }
    Usage is like this
    upload_2022-3-18_22-51-29.png

    upload_2022-3-18_22-51-49.png

    upload_2022-3-18_22-52-2.png
     
    shelim likes this.
  3. shelim

    shelim

    Joined:
    Aug 26, 2017
    Posts:
    28
    This is nice! I will try it later on, but I may have a problem here - will the smart formatter recognize such structure as array (ie, can I use
    list
    formatter on such data?)
     
  4. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    Ah no you won't be able to use the list formatter.
    You could return a list of strings in GetSourceValue. You would only need the IVariable Part then, no need for group. I dont think it would trigger the update if the child was updated though.
    I'll.try and take a closer look Monday to see how we could make it work with a list.
     
    shelim likes this.
  5. shelim

    shelim

    Joined:
    Aug 26, 2017
    Posts:
    28
    Thank you, let us check this at Monday then :)
     
  6. shelim

    shelim

    Joined:
    Aug 26, 2017
    Posts:
    28
    Okay, I managed to workarounded this by facading list:

    Here is entire component, to be put into ListVariable.cs . It is not meant as roboust solution, more like temporary workaround :)

    (License is either PublicDomain or DoWTFyou want if your country does not support PublicDomain)

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using UnityEngine;
    6. using UnityEngine.Localization;
    7. using UnityEngine.Localization.SmartFormat.Core.Extensions;
    8. using UnityEngine.Localization.SmartFormat.PersistentVariables;
    9.  
    10. [Serializable]
    11. public abstract class ListVariable<T> : IList<T>, IReadOnlyList<T>, IVariable, IVariableValueChanged where T : IVariableValueChanged, IVariable
    12. {
    13.     public event Action<IVariable> ValueChanged;
    14.  
    15.     [SerializeField]
    16.     private List<T> _listField;
    17.  
    18.     private List<T> _list => _listField ?? (_listField = new List<T>());
    19.  
    20.     public object GetSourceValue(ISelectorInfo selector)
    21.     {
    22.         return _list.Select(x => x.GetSourceValue(selector));
    23.     }
    24.  
    25.     public int Count => _list.Count;
    26.  
    27.     public bool IsReadOnly => false;
    28.  
    29.     public bool IsFixedSize => false;
    30.  
    31.     public bool IsSynchronized => false;
    32.  
    33.     public object SyncRoot => _list;
    34.  
    35.     public T this[int index]
    36.     {
    37.         get => _list[index];
    38.         set
    39.         {
    40.             _list[index] = value;
    41.             ValueChanged?.Invoke(this);
    42.         }
    43.     }
    44.     private void Item_ValueChanged(IVariable obj)
    45.     {
    46.         ValueChanged?.Invoke(this);
    47.     }
    48.  
    49.  
    50.     public int IndexOf(T item)
    51.     {
    52.         return _list.IndexOf(item);
    53.     }
    54.  
    55.     public void Insert(int index, T item)
    56.     {
    57.         item.ValueChanged += Item_ValueChanged;
    58.         _list.Insert(index, item);
    59.         ValueChanged?.Invoke(this);
    60.     }
    61.  
    62.     public void RemoveAt(int index)
    63.     {
    64.         _list[index].ValueChanged -= Item_ValueChanged;
    65.         _list.RemoveAt(index);
    66.         ValueChanged?.Invoke(this);
    67.     }
    68.  
    69.     public void Add(T item)
    70.     {
    71.         item.ValueChanged += Item_ValueChanged;
    72.         _list.Add(item);
    73.         ValueChanged?.Invoke(this);
    74.     }
    75.  
    76.     public void Clear()
    77.     {
    78.         foreach (var item in _list)
    79.         {
    80.             item.ValueChanged -= Item_ValueChanged;
    81.         }
    82.         _list.Clear();
    83.         ValueChanged?.Invoke(this);
    84.     }
    85.  
    86.     public bool Contains(T item)
    87.     {
    88.         return _list.Contains(item);
    89.     }
    90.  
    91.     public void CopyTo(T[] array, int arrayIndex)
    92.     {
    93.         _list.CopyTo(array, arrayIndex);
    94.     }
    95.  
    96.     public bool Remove(T item)
    97.     {
    98.         item.ValueChanged -= Item_ValueChanged;
    99.         var ret = _list.Remove(item);
    100.         if (ret)
    101.             ValueChanged?.Invoke(this);
    102.         return ret;
    103.     }
    104.  
    105.     public IEnumerator<T> GetEnumerator()
    106.     {
    107.         return _list.GetEnumerator();
    108.     }
    109.  
    110.     IEnumerator IEnumerable.GetEnumerator()
    111.     {
    112.         return _list.GetEnumerator();
    113.     }
    114. }
    115.  
    116. [DisplayName("Localized List")]
    117. [Serializable]
    118. public sealed class LocalizedListString : ListVariable<LocalizedString>
    119. {
    120.  
    121. }
    And here is a dirty test:

    Code (CSharp):
    1.  
    2. public class Example : MonoBehaviour
    3. {
    4.     public LocalizedString _entry;
    5.  
    6.     private LocalizedListString _list;
    7.     public LocalizedString _item;
    8.  
    9.     private IntVariable[] _variable;
    10.  
    11.     public TMPro.TextMeshProUGUI _target;
    12.  
    13.     // Start is called before the first frame update
    14.     void Start()
    15.     {
    16.         _variable = new IntVariable[3];
    17.         _list = new LocalizedListString();
    18.  
    19.         for (int i = 0; i < 3; i++)
    20.         {
    21.             var item = new LocalizedString(_item.TableReference, _item.TableEntryReference);
    22.             item.Add("value", _variable[i] = new IntVariable { Value = 0 });
    23.  
    24.             _list.Add(item);
    25.         }
    26.         _entry.Add("list", _list);
    27.         _entry.StringChanged += _entry_StringChanged;
    28.     }
    29.  
    30.     private void _entry_StringChanged(string value)
    31.     {
    32.         _target.text = value;
    33.     }
    34.  
    35.     // Update is called once per frame
    36.     void Update()
    37.     {
    38.         _variable[1].Value = Mathf.RoundToInt(Time.time); // Notice that I change only variable inside collection entry
    39.     }
    40. }
    41.  
    Unity_HQ5IxCBjSO.gif
     
    karl_jones likes this.
  7. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    Ah very nice