From d2ab30e7a69bfe7efda63ae75812207377917bd3 Mon Sep 17 00:00:00 2001 From: miepzerino <o.skotnik@gmail.com> Date: Sun, 30 Mar 2025 18:50:27 +0000 Subject: [PATCH] Merge branch 'Flexalon-UI-Layouts' into develop --- Assets/Flexalon/Runtime/Cloner/FlexalonCloner.cs | 188 +++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 188 insertions(+), 0 deletions(-) diff --git a/Assets/Flexalon/Runtime/Cloner/FlexalonCloner.cs b/Assets/Flexalon/Runtime/Cloner/FlexalonCloner.cs new file mode 100644 index 0000000..a4cceb5 --- /dev/null +++ b/Assets/Flexalon/Runtime/Cloner/FlexalonCloner.cs @@ -0,0 +1,188 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace Flexalon +{ + /// <summary> + /// Sometimes, it's useful to generate child objects instead of defining them statically. + /// The Flexalon Cloner can generate objects from a set of prefabs iteratively or randomly, + /// and can optionally bind to a data source. + /// </summary> + [AddComponentMenu("Flexalon/Flexalon Cloner"), HelpURL("https://www.flexalon.com/docs/cloner")] + public class FlexalonCloner : MonoBehaviour + { + [SerializeField] + private List<GameObject> _objects; + /// <summary> Prefabs which should be cloned as children. </summary> + public List<GameObject> Objects + { + get => _objects; + set { _objects = value; MarkDirty(); } + } + + /// <summary> In which order should prefabs be cloned. </summary> + public enum CloneTypes + { + /// <summary> Clone prefabs in the order they are assigned. </summary> + Iterative, + + /// <summary> Clone prefabs in a random order. </summary> + Random + } + + [SerializeField] + private CloneTypes _cloneType = CloneTypes.Iterative; + /// <summary> In which order should prefabs be cloned. </summary> + public CloneTypes CloneType + { + get => _cloneType; + set { _cloneType = value; MarkDirty(); } + } + + [SerializeField] + private uint _count; + /// <summary> How many clones should be generated. </summary> + public uint Count + { + get => _count; + set { _count = value; MarkDirty(); } + } + + [SerializeField] + private int _randomSeed; + /// <summary> Seed used for the Random clone type, to ensure results remain consistent. </summary> + public int RandomSeed + { + get => _randomSeed; + set { _randomSeed = value; MarkDirty(); } + } + + [SerializeField] + private GameObject _dataSource = null; + /// <summary> Can be an gameObject with a component that implements FlexalonDataSource. + /// The number of objects cloned is set to the number of items in the Data property. </summary> + public GameObject DataSource + { + get => _dataSource; + set + { + UnhookDataSource(); + _dataSource = value; + HookDataSource(); + MarkDirty(); + } + } + + [SerializeField, HideInInspector] + private List<GameObject> _clones = new List<GameObject>(); + + void OnEnable() + { + HookDataSource(); + MarkDirty(); + } + + private void HookDataSource() + { + if (isActiveAndEnabled && _dataSource != null && _dataSource) + { + if (_dataSource.TryGetComponent<DataSource>(out var component)) + { + component.DataChanged += MarkDirty; + } + } + } + + private void UnhookDataSource() + { + if (_dataSource != null && _dataSource) + { + if (_dataSource.TryGetComponent<DataSource>(out var component)) + { + component.DataChanged -= MarkDirty; + } + } + } + + void OnDisable() + { + UnhookDataSource(); + MarkDirty(); + } + + /// <summary> Forces the cloner to regenerate its clones. </summary> + public void MarkDirty() + { + foreach(var clone in _clones) + { + if (Application.isPlaying) + { + Destroy(clone); + } + else + { + DestroyImmediate(clone); + } + } + + _clones.Clear(); + + if (isActiveAndEnabled && _objects != null && _objects.Count > 0) + { + switch (_cloneType) + { + case CloneTypes.Iterative: + GenerateIterativeClones(); + break; + case CloneTypes.Random: + GenerateRandomClones(); + break; + } + } + } + + private IReadOnlyList<object> GetData() + { + if (_dataSource != null && _dataSource) + { + return _dataSource.GetComponent<DataSource>()?.Data; + } + + return null; + } + + private void GenerateIterativeClones() + { + int i = 0; + var data = GetData(); + var count = data?.Count ?? (int)_count; + while (_clones.Count < count) + { + GenerateClone(i, data); + i = (i + 1) % _objects.Count; + } + } + + private void GenerateRandomClones() + { + var random = new System.Random(_randomSeed); + var data = GetData(); + var count = data?.Count ?? (int)_count; + while (_clones.Count < count) + { + GenerateClone(random.Next(_objects.Count), data); + } + } + + private void GenerateClone(int index, IReadOnlyList<object> data) + { + var clone = Instantiate(_objects[index], Vector3.zero, Quaternion.identity, transform); + _clones.Add(clone); + + if (data != null && clone.TryGetComponent<DataBinding>(out var dataBinding)) + { + dataBinding.SetData(data[_clones.Count - 1]); + } + } + } +} \ No newline at end of file -- Gitblit v1.9.3