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 件のコメント:
コメントを投稿