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.
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.
[DisplayName("LocalizedString Array")]
public class LocalizedStringArray : IVariableGroup, IVariable
{
public List<LocalizedString> localizedStrings = new List<LocalizedString>();
public object GetSourceValue(ISelectorInfo selector) => this;
public bool TryGetValue(string key, out IVariable value)
{
value = localizedStrings[int.Parse(key)];
return true;
}
}
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?)
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.
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)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.SmartFormat.Core.Extensions;
using UnityEngine.Localization.SmartFormat.PersistentVariables;
[Serializable]
public abstract class ListVariable<T> : IList<T>, IReadOnlyList<T>, IVariable, IVariableValueChanged where T : IVariableValueChanged, IVariable
{
public event Action<IVariable> ValueChanged;
[SerializeField]
private List<T> _listField;
private List<T> _list => _listField ?? (_listField = new List<T>());
public object GetSourceValue(ISelectorInfo selector)
{
return _list.Select(x => x.GetSourceValue(selector));
}
public int Count => _list.Count;
public bool IsReadOnly => false;
public bool IsFixedSize => false;
public bool IsSynchronized => false;
public object SyncRoot => _list;
public T this[int index]
{
get => _list[index];
set
{
_list[index] = value;
ValueChanged?.Invoke(this);
}
}
private void Item_ValueChanged(IVariable obj)
{
ValueChanged?.Invoke(this);
}
public int IndexOf(T item)
{
return _list.IndexOf(item);
}
public void Insert(int index, T item)
{
item.ValueChanged += Item_ValueChanged;
_list.Insert(index, item);
ValueChanged?.Invoke(this);
}
public void RemoveAt(int index)
{
_list[index].ValueChanged -= Item_ValueChanged;
_list.RemoveAt(index);
ValueChanged?.Invoke(this);
}
public void Add(T item)
{
item.ValueChanged += Item_ValueChanged;
_list.Add(item);
ValueChanged?.Invoke(this);
}
public void Clear()
{
foreach (var item in _list)
{
item.ValueChanged -= Item_ValueChanged;
}
_list.Clear();
ValueChanged?.Invoke(this);
}
public bool Contains(T item)
{
return _list.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
_list.CopyTo(array, arrayIndex);
}
public bool Remove(T item)
{
item.ValueChanged -= Item_ValueChanged;
var ret = _list.Remove(item);
if (ret)
ValueChanged?.Invoke(this);
return ret;
}
public IEnumerator<T> GetEnumerator()
{
return _list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _list.GetEnumerator();
}
}
[DisplayName("Localized List")]
[Serializable]
public sealed class LocalizedListString : ListVariable<LocalizedString>
{
}
And here is a dirty test:
public class Example : MonoBehaviour
{
public LocalizedString _entry;
private LocalizedListString _list;
public LocalizedString _item;
private IntVariable[] _variable;
public TMPro.TextMeshProUGUI _target;
// Start is called before the first frame update
void Start()
{
_variable = new IntVariable[3];
_list = new LocalizedListString();
for (int i = 0; i < 3; i++)
{
var item = new LocalizedString(_item.TableReference, _item.TableEntryReference);
item.Add("value", _variable[i] = new IntVariable { Value = 0 });
_list.Add(item);
}
_entry.Add("list", _list);
_entry.StringChanged += _entry_StringChanged;
}
private void _entry_StringChanged(string value)
{
_target.text = value;
}
// Update is called once per frame
void Update()
{
_variable[1].Value = Mathf.RoundToInt(Time.time); // Notice that I change only variable inside collection entry
}
}