DynamicScrollRectから見れば, GameObjectがキャッシュされているかは知らなくてよいこと.
/* This is free and unencumbered software released into the public domain. For more information, please refer to <http://unlicense.org/> */ public class ObjectPool<T> where T : UnityEngine.Component { private int size_; private T[] objects_; private Transform parent_; private GameObject prefab_; public ObjectPool(int capacity, Transform parent, GameObject prefab) { size_ = 0; objects_ = new T[capacity]; parent_ = parent; prefab_ = prefab; } public T popOrInstantiate() { if(size_<=0){ GameObject obj = GameObject.Instantiate<GameObject>(prefab_); return obj.GetComponent<T>(); } --size_; return objects_[size_]; } public void push(T obj) { if(objects_.Length<=size_){ System.Array.Resize<T>(ref objects_, size_+8); } obj.transform.SetParent(parent_); objects_[size_] = obj; ++size_; } } public class DynamicScrollRect : UnityEngine.UI.ScrollRect { public interface IListAdaptor { int getOverCount(); int size(); float getTotalHeight(); float getHeight(int index); float getTop(int index); RectTransform create(int index); void destroy(RectTransform rect); }; public class TestAdaptor : IListAdaptor { public const float Height = 32.0f; private ObjectPool<RectTransform> pool_; private string[] contents_; public TestAdaptor(Transform parent, GameObject prefab) { pool_ = new ObjectPool<RectTransform>(16, parent, prefab); contents_ = new string[128]; for(int i=0; i<contents_.Length; ++i){ contents_[i] = string.Format("{0}", i); } } public int getOverCount() { return 2; } public int size() { return contents_.Length; } public float getTotalHeight() { return contents_.Length * Height; } public float getHeight(int index) { return Height; } public float getTop(int index) { return index * Height; } public RectTransform create(int index) { RectTransform rect = pool_.popOrInstantiate(); rect.gameObject.SetActive(true); UnityEngine.UI.Text text = rect.GetComponent<UnityEngine.UI.Text>(); text.text = contents_[index]; return rect; } public void destroy(RectTransform rect) { rect.gameObject.SetActive(false); pool_.push(rect); } } public const float Epsilon= 1.0e-5f; public GameObject prefab_; private IListAdaptor adaptor_; private float prevPosition_; private struct Item { public int index_; public RectTransform rect_; public Item(int index, RectTransform rect) { index_ = index; rect_ = rect; } }; private System.Collections.Generic.List<Item> items_ = new System.Collections.Generic.List<Item>(8); public void initialize(IListAdaptor adaptor) { if(null != adaptor_){ clear(); this_.onValueChanged.RemoveAllListeners(); } adaptor_ = adaptor; if(null != adaptor_){ RectTransform thisRect = GetComponent(); this.content.sizeDelta = new Vector2(thisRect.rect.width, adaptor_.getTotalHeight()); relayout(thisRect.rect.height); this.onValueChanged.AddListener(onScroll); } prevPosition_ = this.normalizedPosition.y; } public void relayout() { relayout(this.viewport.rect.height); } public void relayout(float height) { float minY = this.content.anchoredPosition.y - Epsilon; float maxY = minY + height + Epsilon; int start = int.MaxValue; int end = int.MinValue; float y = 0.0f; for(int i=0; i<adaptor_.size(); ++i){ float ny = y + adaptor_.getHeight(i); if(minY<=ny && y<=maxY){ start = Mathf.Min(start, i); end = Mathf.Max(end, i); } y = ny; } if(end<start){ clear(); return; } create(start, end); } public void updateLayout() { if(items_.Count<=0){ relayout(); return; } float minY = this.content.anchoredPosition.y - Epsilon; float maxY = minY + this.viewport.rect.height + Epsilon; int start = int.MaxValue; int end = int.MinValue; int begin = Mathf.Max(0, items_[0].index_-adaptor_.getOverCount()); int term = Mathf.Min(adaptor_.size(), items_[items_.Count-1].index_+adaptor_.getOverCount()); float y = adaptor_.getTop(begin); for(int i=begin; i<term; ++i){ float ny = y + adaptor_.getHeight(i); if(minY<=ny && y<=maxY){ start = Mathf.Min(start, i); end = Mathf.Max(end, i); } y = ny; } if(end<start){ clear(); return; } create(start, end); } public void clear() { for(int i=0; i<items_.Count; ++i) { adaptor_.destroy(items_[i].rect_); } items_.Clear(); } private void pushBack(int index) { RectTransform rect = adaptor_.create(index); rect.SetParent(this.content, false); rect.transform.localPosition = Vector3.zero; rect.localScale = Vector3.one; rect.SetAsLastSibling(); items_.Add(new Item(index, rect)); } private void pushFront(int index) { RectTransform rect = adaptor_.create(index); rect.SetParent(this.content, false); rect.transform.localPosition = Vector3.zero; rect.localScale = Vector3.one; rect.SetAsFirstSibling(); items_.Insert(0, new Item(index, rect)); } private void create(int start, int end) { for(int i=0; i<items_.Count;){ if(items_[i].index_<start || end<items_[i].index_){ adaptor_.destroy(items_[i].rect_); items_.RemoveAt(i); }else{ ++i; } } for(int i=start; i<=end; ++i){ if(items_.Count<=0 || items_[items_.Count-1].index_<i){ pushBack(i); } } for(int i=end; start<=i; --i){ if(items_.Count<=0 || i<items_[0].index_){ pushFront(i); } } if(items_.Count<=0){ return; } float contentHeight = this.content.rect.height; float y = contentHeight*0.5f - adaptor_.getTop(items_[0].index_); for(int i=0; i<items_.Count; ++i){ float height = adaptor_.getHeight(items_[i].index_); //items_[i].rect_.anchoredPosition = new Vector2(0.0f, y-0.5f*height); items_[i].rect_.anchoredPosition = new Vector2(0.0f, y); y -= height; } } new System.Collections.IEnumerator Start() { base.Start(); yield return null; initialize(new TestAdaptor(transform, prefab_)); } void onScroll(Vector2 position) { float pos = this.normalizedPosition.y; float diff = Mathf.Abs(pos-prevPosition_) + Epsilon; prevPosition_ = pos; float height; float contentHeight = this.content.rect.height; if(Epsilon<contentHeight){ height = this.viewport.rect.height/this.content.rect.height; }else{ height = 0.0f; } if(height<=diff){ relayout(); }else{ updateLayout(); } } }
0 件のコメント:
コメントを投稿