為了解決啟動成就、排名等UI時的卡頓問題,需要用wrapcontent腳本,在網上找了篇部落格,試了試,還行,支援多行單列,多行多列。(多行多列我的做法是用一個空gameobject作為多列子項的父物體)。
具體做法:
首先給UIGrid元件所在物體挂wrapcontent腳本,然後設定minIndex = 0, itemSize = grid.cellHeight, 最後設定重新整理函數onInitializeItem的回調。
這是從其它地方找的一個wrapcontent腳本:
//----------------------------------------------
// NGUI: Next-Gen UI kit
// Copyright © 2011-2016 Tasharen Entertainment
//----------------------------------------------
using UnityEngine;
using System.Collections.Generic;
/// <summary>
/// This script makes it possible for a scroll view to wrap its content, creating endless scroll views.
/// Usage: simply attach this script underneath your scroll view where you would normally place a UIGrid:
///
/// + Scroll View
/// |- UIWrappedContent
/// |-- Item 1
/// |-- Item 2
/// |-- Item 3
/// </summary>
[AddComponentMenu("NGUI/Interaction/My Wrap Content")]
[ExecuteInEditMode]
public class MyWrapContent : MonoBehaviour
{
public delegate void OnInitializeItem(GameObject go, int wrapIndex, int realIndex);
/// <summary>
/// Width or height of the child items for positioning purposes.
/// </summary>
public int itemSize = ;
/// <summary>
/// Whether the content will be automatically culled. Enabling this will improve performance in scroll views that contain a lot of items.
/// </summary>
public bool cullContent = true;
/// <summary>
/// Minimum allowed index for items. If "min" is equal to "max" then there is no limit.
/// For vertical scroll views indices increment with the Y position (towards top of the screen).
/// </summary>
public int minIndex = ;
/// <summary>
/// Maximum allowed index for items. If "min" is equal to "max" then there is no limit.
/// For vertical scroll views indices increment with the Y position (towards top of the screen).
/// </summary>
public int maxIndex = ;
/// <summary>
/// Whether hidden game objects will be ignored for the purpose of calculating bounds.
/// </summary>
public bool hideInactive = false;
public bool wrapInCircle = false;
/// <summary>
/// Callback that will be called every time an item needs to have its content updated.
/// The 'wrapIndex' is the index within the child list, and 'realIndex' is the index using position logic.
/// </summary>
public OnInitializeItem onInitializeItem;
protected Transform mTrans;
protected UIPanel mPanel;
protected UIScrollView mScroll;
protected bool mHorizontal = false;
protected bool mFirstTime = true;
protected List<Transform> mChildren = new List<Transform>();
protected List<Transform> MChildren
{
get
{
if (mChildren == null || mChildren.Count == )
CacheChild();
return mChildren;
}
}
/// <summary>
/// Initialize everything and register a callback with the UIPanel to be notified when the clipping region moves.
/// </summary>
bool isStart = false;
protected virtual void Start()
{
if (isStart) return;
isStart = true;
SortBasedOnScrollMovement();
WrapContent();
if (mScroll != null) mScroll.GetComponent<UIPanel>().onClipMove = OnMove;
mFirstTime = false;
adjustWrapPos();
}
/// <summary>
/// Callback triggered by the UIPanel when its clipping region moves (for example when it's being scrolled).
/// </summary>
protected virtual void OnMove(UIPanel panel) { WrapContent(); }
void CacheChild()
{
mChildren.Clear();
for (int i = ; i < transform.childCount; ++i)
{
Transform t = transform.GetChild(i);
if (hideInactive && !t.gameObject.active) continue;
mChildren.Add(t);
}
}
/// <summary>
/// Immediately reposition all children.
/// </summary>
[ContextMenu("Sort Based on Scroll Movement")]
public virtual void SortBasedOnScrollMovement()
{
if (!CacheScrollView()) return;
// Cache all children and place them in order
mChildren.Clear();
for (int i = ; i < mTrans.childCount; ++i)
{
Transform t = mTrans.GetChild(i);
if (hideInactive && !t.gameObject.active) continue;
mChildren.Add(t);
}
// Sort the list of children so that they are in order
/*if (mHorizontal) mChildren.Sort(UIGrid.SortHorizontal);
else mChildren.Sort(UIGrid.SortVertical);*/
ResetChildPositions();
}
/// <summary>
/// Immediately reposition all children, sorting them alphabetically.
/// </summary>
[ContextMenu("Sort Alphabetically")]
public virtual void SortAlphabetically()
{
if (!CacheScrollView()) return;
// Cache all children and place them in order
mChildren.Clear();
for (int i = ; i < mTrans.childCount; ++i)
{
Transform t = mTrans.GetChild(i);
if (hideInactive && !t.gameObject.active) continue;
mChildren.Add(t);
}
// Sort the list of children so that they are in order
mChildren.Sort(UIGrid.SortByName);
ResetChildPositions();
}
/// <summary>
/// Cache the scroll view and return 'false' if the scroll view is not found.
/// </summary>
protected bool CacheScrollView()
{
mTrans = transform;
mPanel = NGUITools.FindInParents<UIPanel>(gameObject);
mScroll = mPanel.GetComponent<UIScrollView>();
if (mScroll == null) return false;
if (mScroll.movement == UIScrollView.Movement.Horizontal) mHorizontal = true;
else if (mScroll.movement == UIScrollView.Movement.Vertical) mHorizontal = false;
else return false;
return true;
}
/// <summary>
/// Helper function that resets the position of all the children.
/// </summary>
[ContextMenu("ResetChildPosition")]
public virtual void ResetChildPositions()
{
if (!Application.isPlaying)
{
CacheChild();
adjustWrapPos();
}
for (int i = , imax = MChildren.Count; i < imax; ++i)
{
Transform t = MChildren[i];
Vector3 pos = t.localPosition;
t.localPosition = mHorizontal ? new Vector3(i * itemSize, pos.y, f) : new Vector3(pos.x, -i * itemSize, f);
UpdateItem(t, i);
}
}
/// <summary>
/// 重新整理Item資料
/// </summary>
public void RefreshChildData()
{
for (int i = , imax = MChildren.Count; i < imax; ++i)
{
Transform t = MChildren[i];
UpdateItem(t, i);
}
}
/// <summary>
/// Wrap all content, repositioning all children as needed.
/// </summary>
public virtual void WrapContent()
{
float extents = itemSize * MChildren.Count * f;
Vector3[] corners = mPanel.worldCorners;
for (int i = ; i < ; ++i)
{
Vector3 v = corners[i];
v = mTrans.InverseTransformPoint(v);
corners[i] = v;
}
Vector3 center = Vector3.Lerp(corners[], corners[], f);
bool allWithinRange = true;
float ext2 = extents * f;
if (mHorizontal)
{
float min = corners[].x - itemSize;
float max = corners[].x + itemSize;
for (int i = , imax = MChildren.Count; i < imax; ++i)
{
Transform t = MChildren[i];
float distance = t.localPosition.x - center.x;
int index = Mathf.RoundToInt(t.localPosition.x / itemSize);
if (wrapInCircle)
index = (index % (maxIndex - minIndex) + (maxIndex - minIndex)) % (maxIndex - minIndex) + minIndex;
if (distance < -extents)
{
Vector3 pos = t.localPosition;
pos.x += ext2;
distance = pos.x - center.x;
int realIndex = Mathf.RoundToInt(pos.x / itemSize);
if (wrapInCircle)
realIndex = (realIndex % (maxIndex - minIndex) + (maxIndex - minIndex)) % (maxIndex - minIndex) + minIndex;
if (minIndex == maxIndex || (minIndex <= realIndex && realIndex < maxIndex))
{
t.localPosition = pos;
UpdateItem(t, i);
}
else allWithinRange = false;
}
else if (distance > extents)
{
Vector3 pos = t.localPosition;
pos.x -= ext2;
distance = pos.x - center.x;
int realIndex = Mathf.RoundToInt(pos.x / itemSize);
if (wrapInCircle)
realIndex = (realIndex % (maxIndex - minIndex) + (maxIndex - minIndex)) % (maxIndex - minIndex) + minIndex;
if (minIndex == maxIndex || (minIndex <= realIndex && realIndex < maxIndex))
{
t.localPosition = pos;
UpdateItem(t, i);
}
else allWithinRange = false;
}
else if (mFirstTime) UpdateItem(t, i);
if (cullContent)
{
distance += mPanel.clipOffset.x - mTrans.localPosition.x;
if (!UICamera.IsPressed(t.gameObject))
NGUITools.SetActive(t.gameObject, (distance > min && distance < max), false);
}
else
{
NGUITools.SetActive(t.gameObject, true);
}
if (!(maxIndex < && maxIndex < ) && (index < minIndex || index >= maxIndex))
NGUITools.SetActive(t.gameObject, false);
}
}
else
{
float min = corners[].y - itemSize;
float max = corners[].y + itemSize;
for (int i = , imax = MChildren.Count; i < imax; ++i)
{
Transform t = MChildren[i];
float distance = t.localPosition.y - center.y;
int index = Mathf.RoundToInt(-t.localPosition.y / itemSize);//y軸向下為負軸,是以index要取反
if (wrapInCircle)
index = (index % (maxIndex - minIndex) + (maxIndex - minIndex)) % (maxIndex - minIndex) + minIndex;
if (distance < -extents)
{
Vector3 pos = t.localPosition;
pos.y += ext2;
distance = pos.y - center.y;
int realIndex = Mathf.RoundToInt(-pos.y / itemSize);//y軸向下為負軸,是以index要取反
if (wrapInCircle)
realIndex = (realIndex % (maxIndex - minIndex) + (maxIndex - minIndex)) % (maxIndex - minIndex) + minIndex;
if (minIndex == maxIndex || (minIndex <= realIndex && realIndex < maxIndex))
{
t.localPosition = pos;
UpdateItem(t, i);
}
else allWithinRange = false;
}
else if (distance > extents)
{
Vector3 pos = t.localPosition;
pos.y -= ext2;
distance = pos.y - center.y;
int realIndex = Mathf.RoundToInt(-pos.y / itemSize);//y軸向下為負軸,是以index要取反
if (wrapInCircle)
realIndex = (realIndex % (maxIndex - minIndex) + (maxIndex - minIndex)) % (maxIndex - minIndex) + minIndex;
if (minIndex == maxIndex || (minIndex <= realIndex && realIndex < maxIndex))
{
t.localPosition = pos;
UpdateItem(t, i);
}
else allWithinRange = false;
}
else if (mFirstTime) UpdateItem(t, i);
if (cullContent)
{
distance += mPanel.clipOffset.y - mTrans.localPosition.y;
if (!UICamera.IsPressed(t.gameObject))
{
NGUITools.SetActive(t.gameObject, (distance > min && distance < max), false);
}
}
else
{
NGUITools.SetActive(t.gameObject, true);
}
if (!(maxIndex < && maxIndex < ) && (index < minIndex || index >= maxIndex))
NGUITools.SetActive(t.gameObject, false);
}
}
//Start首次WrapContent的時候會影響ScrollView的restrictWithPanel是以注釋掉。
//隻要能保證WrapContent還沒有滑到min或maxIndex的時候,底部的下一個Item的active為true,
//注釋掉這句就不會有什麼影響
// mScroll.restrictWithinPanel = !allWithinRange;
//mScroll.InvalidateBounds();
}
/// <summary>
/// Sanity checks.
/// </summary>
void OnValidate()
{
if (maxIndex < minIndex)
maxIndex = minIndex;
if (minIndex > maxIndex)
maxIndex = minIndex;
}
/// <summary>
/// Want to update the content of items as they are scrolled? Override this function.
/// </summary>
protected virtual void UpdateItem(Transform item, int index)
{
int realIndex = getRealIndex(item.localPosition);
if (!(minIndex < && maxIndex < ) && (realIndex < minIndex || realIndex >= maxIndex))
item.gameObject.SetActiveRecursively(false);
else
{
item.gameObject.SetActiveRecursively(true);
if (onInitializeItem != null)
{
onInitializeItem(item.gameObject, index, realIndex);
}
}
}
/// <summary>
/// 調整Grid的初始坐标貼邊,為了自适應
/// </summary>
[ContextMenu("adjustWrapPos")]
public void adjustWrapPos()
{
if (mScroll == null)
CacheScrollView();
Bounds b = NGUIMath.CalculateRelativeWidgetBounds(mScroll.transform, MChildren[], true);
Vector2 pos = transform.localPosition;
if (mHorizontal)
{
pos.x = -(mPanel.GetViewSize().x / f - mPanel.baseClipRegion.x - b.extents.x - mPanel.clipSoftness.x);
}
else
{
pos.y = mPanel.GetViewSize().y / f + mPanel.baseClipRegion.y - b.extents.y - mPanel.clipSoftness.y;
}
transform.localPosition = pos;
}
/// <summary>
///根據使用者設定的maxIndex 擷取ScrollView能滑動到的最小坐标 滑倒maxIndex實際對應的是最小坐标
/// </summary>
/// <returns></returns>
public Vector2 getMinSpringPos()
{
if (mScroll == null) CacheScrollView();
Vector2 pos = mScroll.transform.localPosition;
if (mHorizontal)
{
if (minIndex == maxIndex)
pos.x = mScroll.transform.localPosition.x - mPanel.GetViewSize().x;
else
pos.x = -(itemSize * maxIndex - mPanel.GetViewSize().x);
}
else
{
if (minIndex == maxIndex)
pos.y = mScroll.transform.localPosition.y + mPanel.GetViewSize().y;
else
pos.y = (itemSize * minIndex);
}
return pos;
}
/// <summary>
/// 根據使用者設定的minIndex 擷取ScrollView能滑動到的最大坐标 滑倒minIndex實際對應的是最大坐标
/// </summary>
/// <returns></returns>
public Vector2 getMaxSpringPos()
{
if (mScroll == null) CacheScrollView();
Vector2 pos = mScroll.transform.localPosition;
if (mHorizontal)
{
if (minIndex == maxIndex)
pos.x = mScroll.transform.localPosition.x + mPanel.GetViewSize().x;
else
pos.x = -(itemSize * minIndex);
}
else
{
if (minIndex == maxIndex)
pos.y = mScroll.transform.localPosition.y - mPanel.GetViewSize().y;
else
{
pos.y = (itemSize * maxIndex - mPanel.GetViewSize().y);
pos.y = pos.y < ? : pos.y;
}
}
return pos;
}
public bool couldSpringBack()
{
//因為x軸向負軸為變大,y軸向負軸為變小
if (mHorizontal)
{
Vector2 pos = mScroll.transform.localPosition;
Vector2 minPos = getMinSpringPos();
return pos.x > minPos.x;
}
else
{
Vector2 pos = mScroll.transform.localPosition;
Vector2 maxPos = getMaxSpringPos();
return pos.y > maxPos.y;
}
}
public bool couldSpringForward()
{
if (mHorizontal)
{
Vector2 pos = mScroll.transform.localPosition;
Vector2 maxPos = getMaxSpringPos();
return pos.x < maxPos.x;
}
else
{
Vector2 pos = mScroll.transform.localPosition;
Vector2 minPos = getMinSpringPos();
return pos.y < minPos.y;
}
}
/// <summary>
/// 限制ScrollView的滑動邊界
/// </summary>
/// <param name="pos"></param>
public void RoundSpringLimit(ref Vector3 pos)
{
Vector2 minLimitPos = getMinSpringPos();
Vector2 maxLimitPos = getMaxSpringPos();
pos.x = pos.x < minLimitPos.x ? minLimitPos.x : pos.x;
pos.x = pos.x > maxLimitPos.x ? maxLimitPos.x : pos.x;
pos.y = pos.y < minLimitPos.y ? minLimitPos.y : pos.y;
pos.y = pos.y > maxLimitPos.y ? maxLimitPos.y : pos.y;
}
/// <summary>
/// 擷取一個locl坐标下對應的真實序号
/// </summary>
/// <param name="localPos"></param>
/// <returns></returns>
public int getRealIndex(Vector3 localPos)
{
int realIndex = (mHorizontal) ?
Mathf.RoundToInt(localPos.x / itemSize) :
Mathf.RoundToInt(-localPos.y / itemSize);//y軸向下為負軸,是以index要取反
if (wrapInCircle)
realIndex = (realIndex % (maxIndex - minIndex) + (maxIndex - minIndex)) % (maxIndex - minIndex) + minIndex;
return realIndex;
}
/// <summary>
/// 根據localPos擷取對應的序号
/// </summary>
/// <param name="localPos"></param>
/// <returns></returns>
public int getIndexWithLocalPos(Vector3 localPos)
{
return getIndexWithRealIndex(getRealIndex(localPos));
}
/// <summary>
/// 根據真實序号擷取對應的序号
/// </summary>
/// <param name="realIndex"></param>
/// <returns></returns>
public int getIndexWithRealIndex(int realIndex)
{
int index = realIndex % MChildren.Count;
return index;
}
/// <summary>
/// 擷取一個真實序号對應的locl坐标
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public Vector3 getRealShouldPos(int realIndex)
{
if (MChildren.Count == ) return Vector3.zero;
Vector3 pos = MChildren[].localPosition;
int realIndex0 = getRealIndex(pos);
if (mHorizontal)
{
pos.x = pos.x + (realIndex - realIndex0) * itemSize;
}
else
{
pos.y = pos.y - (realIndex - realIndex0) * itemSize;
}
return pos;
}
public Vector3 getRealIndexShouldSpringPOS(int realIndex)
{
if (!mScroll) CacheScrollView();
Vector3 scrollPos = mScroll.transform.localPosition;
if (mHorizontal)
{
scrollPos.x = -(realIndex * itemSize);
}
else
{
scrollPos.y = realIndex * itemSize;
}
RoundSpringLimit(ref scrollPos);
return scrollPos;
}
/// <summary>
/// 根據一個真實序号調整目前所有Item的坐标
/// </summary>
/// <param name="realIndex"></param>
public void WrapChildPosWithRealIndex(int realIndex)
{
if (!isStart)
Start();
int index = getIndexWithRealIndex(realIndex);
Vector3 targetPos = getRealShouldPos(realIndex);
Vector3 offset = targetPos - MChildren[index].localPosition;
for (int i = , Imax = MChildren.Count; i < Imax; i++)
{
Vector3 pos = MChildren[i].localPosition;
pos += offset;
MChildren[i].localPosition = pos;
}
WrapContent();
RefreshChildData();
}
public bool isInBottom()
{
float totalLength = itemSize * maxIndex;
if (mHorizontal)
{
return mPanel.clipOffset.x >= (totalLength - mPanel.GetViewSize().x);
}
else
{
return mPanel.clipOffset.y <= -(totalLength - mPanel.GetViewSize().y);
}
}
}