当前位置: 首页 > news >正文

dw做的网站如何上传云服务器网络推广公司简介模板

dw做的网站如何上传云服务器,网络推广公司简介模板,企业网络服务,个人博客网页发现问题 在游戏开发中有一个常见的需求#xff0c;就是需要在屏幕显示多个#xff08;多达上百#xff09;显示item#xff0c;然后用户用手指滚动视图可以选择需要查看的item。 现在的情况是在100个data的时候#xff0c;Unity引擎是直接创建出对应的100个显示item。 …发现问题 在游戏开发中有一个常见的需求就是需要在屏幕显示多个多达上百显示item然后用户用手指滚动视图可以选择需要查看的item。 现在的情况是在100个data的时候Unity引擎是直接创建出对应的100个显示item。 这样的问题是显示屏只有6~7个是当前用户看得到的其余的90多个一直放在内存中这样的处理是一个比较浪费内存空间的处理方法。 所以我们现在需要一种优化就是在data有100个的时候我们只创建显示区域的几个显示item就好了然后这几个显示item我们会复用起来不断的更新data到这几个显示item上。 要完成以上逻辑需要处理的地方有一下几个 1.item的更新data回调 2.item的数量回调 3.计算item的index、尺寸及对应的位置 模仿FairyGUI的处理 在FairyGUI对于前两个问题FairyGUI中有“列表”组件来完成对于第三个问题就使用了虚拟列表来完成这种优化现在我们来模仿FiryGUI的逻辑在Unity的组件中完成这个功能。 解决前两个问题 框架代码 首先对于前两个问题我们来做一个简单的自定义滚动视图先不处理复用的逻辑。 using System; using UnityEngine; using UnityEngine.UI;[RequireComponent(typeof(RectTransform))] [DisallowMultipleComponent] public class ScrollView : ScrollRect {[Tooltip(item的模板)]public RectTransform itemTemplate;//更新数据回调public Actionint, RectTransform updateFunc;//设置数量回调更新数据public Funcint itemCountFunc;public virtual void SetUpdateFunc(Actionint,RectTransform func){updateFunc func;}public virtual void SetItemCountFunc(Funcint func){itemCountFunc func;InternalUpdateData();}protected virtual void InternalUpdateData(){if (updateFunc null){return;}RemoveAllChildren();for (int i 0; i itemCountFunc(); i){GameObject itemObj Instantiate(itemTemplate.gameObject, content, true);itemObj.transform.localPosition itemTemplate.localPosition;itemObj.SetActive(true);updateFunc(i, itemObj.GetComponentRectTransform());}}public void RemoveAllChildren(){for(int i 0;i content.childCount; i){Transform child content.GetChild(i);if (itemTemplate ! child){Destroy(child.gameObject);}}} } 在这个脚本中我们继承了ScrollRect组件添加了item的更新数据回调以及item的数据设置回调。 这两个问题的处理相对还算比较简单。 主要是通过回调来自定义data在对应显示item的创建。 脚本的在编辑器上显示为 由于我们没有在ScrollView脚本中处理复用的逻辑所以需要在显示对象Content上添加Layout组件。 至此我们解决前两个问题的框架的逻辑就处理好了。 示例 现在我们贴出如何使用ScrollView的示例代码。 UIBoxRoguelike.cs using UnityEngine.UI;/// summary /// 宝箱翻牌UI /// /summary public class UIBoxRoguelike : BasePanel {public const string ItemsList ItemsList;// 奖励列表public const string ClaimMagicBox ClaimMagicBox;// 领取神秘宝箱/// summary/// 随机类型 0正常 1随机 2神秘/// /summarypublic enum RoguelikeType{Normal 0,Random,Secret,}public Image imgBoxIcon;public Button btnMask;public ScrollView roguelikeSr;private bool _isSecret;// 是否神秘奖励private void Start(){var type RoguelikeType.Normal;var boxCfg ConfigManager._BoxCfgMgr.GetDataByID((int)BoxModel.Box.BoxID);imgBoxIcon.sprite AssetBundleMgr.GetInstance().LoadUISprite(boxCfg.Icon);// 根据当前宝箱btnMask.onClick.AddListener(() {for (int i 0; i BoxModel.ItemsList.Count; i){// 存在神秘奖励 且 未领取if (BoxModel.ItemsList[i].Type ! RoguelikeType.Secret !BoxModel.HasSecretGet) continue;type RoguelikeType.Secret;break;}// 存在神秘奖励 且 未领取if (type RoguelikeType.Secret !BoxModel.HasSecretGet){UIMgr.GetInstance().ShowPanelUIBoxPop(UIDef.UI_BOXPOP, BoxModel.Box);}else{UIMgr.GetInstance().ShowPanelUIRewardPanel(UIDef.UI_REWARDPANEL, BoxModel.RewardList.ToArray());TimerHelper.SetTimeOut(0.3f, () {UIMgr.GetInstance().ShowPanelUIBoxDetail(UIDef.UI_BOXDETAIL);});HideMe();}UIMgr.GetInstance().HidePanel(UIDef.UI_BOXDETAIL);});}public override void Notify(string msgType, object msgData){base.Notify(msgType, msgData);switch (msgType){case ItemsList:RefreshContent(msgData as RoguelikeItemData[]);break;case ClaimMagicBox:RefreshContent(msgData as RoguelikeItemData[]);break;}}private void RefreshContent(RoguelikeItemData[] data){roguelikeSr.SetUpdateFunc((index, rectTransform) {UIBoxRoguelikeItem item rectTransform.GetComponentUIBoxRoguelikeItem();item.OnRefresh(data[index]);});roguelikeSr.SetItemCountFunc(() data.Length);} }这个示例代码我们主要看RefreshConent方法就好了。 另一个脚本UIBoxRoguelikeItem.cs。 using System.Text; using UnityEngine; using UnityEngine.UI;public class UIBoxRoguelikeItem : MonoBehaviour {public Image imgBg;public Image imgIcon;public Text txtTitle;public Text txtCount;public Button btnSecret;private RoguelikeItemData _data;private void Start(){btnSecret.onClick.AddListener(() {// 切换宝箱随机类型_data.Type UIBoxRoguelike.RoguelikeType.Normal;// 刷新当前奖励信息OnRefresh(_data);// 禁用按钮btnSecret.gameObject.SetActive(false);});}public void OnRefresh(RoguelikeItemData data){_data data;imgIcon.sprite AssetBundleMgr.GetInstance().LoadUISprite(data.Icon);imgBg.sprite AssetBundleMgr.GetInstance().LoadUISprite(GetIconBgPathByType(data.Type));txtTitle.text data.Name;txtCount.text data.Count.ToString();txtTitle.gameObject.SetActive(data.Type ! UIBoxRoguelike.RoguelikeType.Secret);txtCount.gameObject.SetActive(data.Type ! UIBoxRoguelike.RoguelikeType.Secret);imgIcon.gameObject.SetActive(data.Type ! UIBoxRoguelike.RoguelikeType.Secret);btnSecret.gameObject.SetActive(data.Type UIBoxRoguelike.RoguelikeType.Secret);}private string GetIconBgPathByType(UIBoxRoguelike.RoguelikeType type){StringBuilder iconBuilder new StringBuilder();switch (type){case UIBoxRoguelike.RoguelikeType.Normal:iconBuilder.Append(UIAtlas/Box/card02_icon);break;case UIBoxRoguelike.RoguelikeType.Random:iconBuilder.Append(UIAtlas/Box/card01_icon);break;case UIBoxRoguelike.RoguelikeType.Secret:iconBuilder.Append(UIAtlas/Box/card03_icon);break;}return iconBuilder.ToString();} }public class RoguelikeItemData {public int ItemId;// 道具idpublic string Icon;// 图标public string Name;// 名字public int Count;// 数量public UIBoxRoguelike.RoguelikeType Type;// 随机类型public RoguelikeItemData(int itemId, string icon, string name, int count,UIBoxRoguelike.RoguelikeType type UIBoxRoguelike.RoguelikeType.Normal){ItemId itemId;Icon icon;Name name;Count count;Type type; // 是否神秘宝箱} }复用的逻辑处理 框架代码 好了现在我们来处理第三个问题第三个问题比前两个问题要复杂得多。 处理的主要两个脚本文件是ScrollViewEx.cs和ScollViewExItem.cs ScrollViewEx.cs代码 using System.Collections; using System.Collections.Generic; using System; using UnityEngine; using UnityEngine.Events;[RequireComponent(typeof(RectTransform))] [DisallowMultipleComponent] public class ScrollViewEx : ScrollView {[SerializeField]private int m_pageSize 50;public int pageSize m_pageSize;private int startOffset 0;private Funcint realItemCountFunc;private bool canNextPage false;public class ScrollItemWithRect{// scroll item 身上的 RectTransform组件public RectTransform item;// scroll item 在scrollview中的位置public Rect rect;// rect 是否需要更新public bool rectDirty true;}int m_dataCount 0;ListScrollItemWithRect managedItems new ListScrollItemWithRect();// for hide and showpublic enum ItemLayoutType{// 最后一位表示滚动方向Vertical 1, // 0001Horizontal 2, // 0010VerticalThenHorizontal 4, // 0100HorizontalThenVertical 5, // 0101}public const int flagScrollDirection 1; // 0001[SerializeField]ItemLayoutType m_layoutType ItemLayoutType.Vertical;protected ItemLayoutType layoutType { get { return m_layoutType; } }// const int 代替 enum 减少 (int)和(CriticalItemType)转换protected static class CriticalItemType{public const int UpToHide 0;public const int DownToHide 1;public const int UpToShow 2;public const int DownToShow 3;}// 只保存4个临界indexprotected int[] criticalItemIndex new int[4];Rect refRect;// resource managementSimpleObjPoolRectTransform itemPool null;[Tooltip(初始化时池内item数量)]public int poolSize;[Tooltip(默认item尺寸)]public Vector2 defaultItemSize;[Tooltip(默认item间隔)]public Vector2 defaultItemSpace;//设置尺寸回调public Funcint, Vector2 itemSizeFunc;public Funcint, RectTransform itemGetFunc;public ActionRectTransform itemRecycleFunc;public ActionRectTransform RecycleFunc;private Action UpdateCriticalItemsPreprocess null;//选择元素回调private Actionint, RectTransform selectIndexFunc;private UnityEventint, ScrollViewExItem _onClickItem;// statusprivate bool initialized false;private int willUpdateData 0;public override void SetUpdateFunc(Actionint,RectTransform func){if (func ! null){var f func;func (index, rect) {f(index startOffset, rect);};}base.SetUpdateFunc(func);}public void SetItemSizeFunc(Funcint, Vector2 func){if (func ! null){var f func;func (index) {return f(index startOffset);};}itemSizeFunc func;}public override void SetItemCountFunc(Funcint func){realItemCountFunc func;if (func ! null){var f func;func () Mathf.Min(f(), pageSize);}base.SetItemCountFunc(func);}public void SetItemRecycleFunc(ActionRectTransform func){RecycleFunc func;}public void SetSelectIndexFunc(Actionint,RectTransform func){selectIndexFunc func;}public void SetUpdateCriticalItemsPreprocess(Action func){UpdateCriticalItemsPreprocess func;}public void SetItemGetAndRecycleFunc(Funcint, RectTransform getFunc, ActionRectTransform recycleFunc){if(getFunc ! null recycleFunc ! null){itemGetFunc getFunc;itemRecycleFunc recycleFunc;}}public void UpdateData(bool immediately true){if (!initialized){InitScrollView();}if(immediately){willUpdateData | 3; // 0011InternalUpdateData();}else{if(willUpdateData 0 gameObject.active){StartCoroutine(DelayUpdateData());}willUpdateData | 3;}}public void UpdateDataIncrementally(bool immediately true){if (!initialized){InitScrollView();}if (immediately){willUpdateData | 1; // 0001InternalUpdateData();}else{if (willUpdateData 0){StartCoroutine(DelayUpdateData());}willUpdateData | 1;}}public void ScrollTo(int index){InternalScrollTo(index);}protected void InternalScrollTo(int index){int count 0;if (realItemCountFunc ! null){count realItemCountFunc();}index Mathf.Clamp(index, 0, count - 1);startOffset Mathf.Clamp(index - pageSize / 2, 0, count - itemCountFunc());UpdateData(true);index Mathf.Clamp(index, 0, m_dataCount - 1);EnsureItemRect(index);Rect r managedItems[index].rect;int dir (int)layoutType flagScrollDirection;if (dir 1){// verticalfloat value 1 - (-r.yMax / (content.sizeDelta.y - refRect.height));//value Mathf.Clamp01(value);SetNormalizedPosition(value, 1);}else{// horizontalfloat value r.xMin / (content.sizeDelta.x - refRect.width);//value Mathf.Clamp01(value);SetNormalizedPosition(value, 0);}}private IEnumerator DelayUpdateData(){yield return null;InternalUpdateData();}protected override void InternalUpdateData(){int newDataCount 0;bool keepOldItems ((willUpdateData 2) 0);if (itemCountFunc ! null){newDataCount itemCountFunc();}// if (newDataCount ! managedItems.Count)if (true){if (managedItems.Count newDataCount) //增加{if(!keepOldItems){foreach (var itemWithRect in managedItems){// 重置所有rectitemWithRect.rectDirty true;}}while (managedItems.Count newDataCount){managedItems.Add(new ScrollItemWithRect());}}else //减少 保留空位 避免GC{for (int i 0, count managedItems.Count; i count; i){if(i newDataCount){// 重置所有rectif(!keepOldItems){managedItems[i].rectDirty true;}if(i newDataCount - 1){managedItems[i].rectDirty true;}}// 超出部分 清理回收itemif (i newDataCount){managedItems[i].rectDirty true;if (managedItems[i].item ! null){RecycleOldItem(managedItems[i].item);managedItems[i].item null;}}}}}else{if(!keepOldItems){for (int i 0, count managedItems.Count; i count; i){// 重置所有rectmanagedItems[i].rectDirty true;}}}m_dataCount newDataCount;ResetCriticalItems();willUpdateData 0;}void ResetCriticalItems(){bool hasItem, shouldShow;int firstIndex -1, lastIndex -1;for (int i 0; i m_dataCount; i){hasItem managedItems[i].item ! null;shouldShow ShouldItemSeenAtIndex(i);if (shouldShow){if (firstIndex -1){firstIndex i;}lastIndex i;}if (hasItem shouldShow){// 应显示且已显示SetDataForItemAtIndex(managedItems[i].item, i);continue;}if (hasItem shouldShow){// 不应显示且未显示//if (firstIndex ! -1)//{// // 已经遍历完所有要显示的了 后边的先跳过// break;//}continue;}if (hasItem !shouldShow){// 不该显示 但是有RecycleOldItem(managedItems[i].item);managedItems[i].item null;continue;}if (shouldShow !hasItem){// 需要显示 但是没有RectTransform item GetNewItem(i);managedItems[i].item item;OnGetItemForDataIndex(item, i);continue;}}// content.localPosition Vector2.zero;criticalItemIndex[CriticalItemType.UpToHide] firstIndex;criticalItemIndex[CriticalItemType.DownToHide] lastIndex;criticalItemIndex[CriticalItemType.UpToShow] Mathf.Max(firstIndex - 1, 0);criticalItemIndex[CriticalItemType.DownToShow] Mathf.Min(lastIndex 1, m_dataCount - 1);}protected override void SetContentAnchoredPosition(Vector2 position){base.SetContentAnchoredPosition(position);UpdateCriticalItemsPreprocess?.Invoke();UpdateCriticalItems();}protected override void SetNormalizedPosition(float value, int axis){base.SetNormalizedPosition(value, axis);ResetCriticalItems();}RectTransform GetCriticalItem(int type){int index criticalItemIndex[type];if(index 0 index m_dataCount){return managedItems[index].item;}return null;}void UpdateCriticalItems(){//if (itemSizeFunc ! null)//{// managedItems.ForEach(item // {// item.rectDirty true;// });//}bool dirty true;while (dirty){dirty false;for (int i CriticalItemType.UpToHide; i CriticalItemType.DownToShow; i ){if(i CriticalItemType.DownToHide) //隐藏离开可见区域的item{dirty dirty || CheckAndHideItem(i);}else //显示进入可见区域的item{dirty dirty || CheckAndShowItem(i);}}}}public void ForceUpdateCriticalItems(){// Debug.Log(count : managedItems.Count);//// managedItems.ForEach(item // {// item.rectDirty true;// });//UpdateCriticalItems();}private bool CheckAndHideItem(int criticalItemType){RectTransform item GetCriticalItem(criticalItemType);int criticalIndex criticalItemIndex[criticalItemType];if (item ! null !ShouldItemSeenAtIndex(criticalIndex)){RecycleOldItem(item);managedItems[criticalIndex].item null;//Debug.Log(回收了 criticalIndex);if (criticalItemType CriticalItemType.UpToHide){// 最上隐藏了一个criticalItemIndex[criticalItemType 2] Mathf.Max(criticalIndex, criticalItemIndex[criticalItemType 2]);criticalItemIndex[criticalItemType];}else{// 最下隐藏了一个criticalItemIndex[criticalItemType 2] Mathf.Min(criticalIndex, criticalItemIndex[criticalItemType 2]);criticalItemIndex[criticalItemType]--;}criticalItemIndex[criticalItemType] Mathf.Clamp(criticalItemIndex[criticalItemType], 0, m_dataCount - 1);return true;}return false;}private bool CheckAndShowItem(int criticalItemType){RectTransform item GetCriticalItem(criticalItemType);int criticalIndex criticalItemIndex[criticalItemType];//if (item null ShouldItemFullySeenAtIndex(criticalItemIndex[criticalItemType - 2]))if (item null ShouldItemSeenAtIndex(criticalIndex)){RectTransform newItem GetNewItem(criticalIndex);OnGetItemForDataIndex(newItem, criticalIndex);//Debug.Log(创建了 criticalIndex);managedItems[criticalIndex].item newItem;if (criticalItemType CriticalItemType.UpToShow){// 最上显示了一个criticalItemIndex[criticalItemType - 2] Mathf.Min(criticalIndex, criticalItemIndex[criticalItemType - 2]);criticalItemIndex[criticalItemType]--;}else{// 最下显示了一个criticalItemIndex[criticalItemType - 2] Mathf.Max(criticalIndex, criticalItemIndex[criticalItemType - 2]);criticalItemIndex[criticalItemType];}criticalItemIndex[criticalItemType] Mathf.Clamp(criticalItemIndex[criticalItemType], 0, m_dataCount - 1);return true;}return false;}bool ShouldItemSeenAtIndex(int index){if(index 0 || index m_dataCount){return false;}EnsureItemRect(index);return new Rect(refRect.position - content.anchoredPosition, refRect.size).Overlaps(managedItems[index].rect);}bool ShouldItemFullySeenAtIndex(int index){if (index 0 || index m_dataCount){return false;}EnsureItemRect(index);return IsRectContains(new Rect(refRect.position - content.anchoredPosition, refRect.size),(managedItems[index].rect));}bool IsRectContains(Rect outRect, Rect inRect, bool bothDimensions false){if (bothDimensions){bool xContains (outRect.xMax inRect.xMax) (outRect.xMin inRect.xMin);bool yContains (outRect.yMax inRect.yMax) (outRect.yMin inRect.yMin);return xContains yContains;}else{int dir (int)layoutType flagScrollDirection;if(dir 1){// 垂直滚动 只计算y向return (outRect.yMax inRect.yMax) (outRect.yMin inRect.yMin);}else // 0{// 水平滚动 只计算x向return (outRect.xMax inRect.xMax) (outRect.xMin inRect.xMin);}}}void InitPool(){GameObject poolNode new GameObject(POOL);poolNode.SetActive(false);poolNode.transform.SetParent(transform,false);itemPool new SimpleObjPoolRectTransform(poolSize,(RectTransform item) {// 回收item.transform.SetParent(poolNode.transform,false);},() {// 构造GameObject itemObj Instantiate(itemTemplate.gameObject);//设置元素的滚动视图组件(即this)if (itemObj.GetComponentScrollViewExItem()){itemObj.GetComponentScrollViewExItem().scrollView this;}RectTransform item itemObj.GetComponentRectTransform();itemObj.transform.SetParent(poolNode.transform,false);item.anchorMin Vector2.up;item.anchorMax Vector2.up;item.pivot Vector2.zero;//rectTrans.pivot Vector2.up;itemObj.SetActive(true);return item;});}void OnGetItemForDataIndex(RectTransform item, int index){SetDataForItemAtIndex(item, index);item.transform.SetParent(content, false);}void SetDataForItemAtIndex(RectTransform item, int index){if (updateFunc ! null)updateFunc(index,item);SetPosForItemAtIndex(item,index);}void SetPosForItemAtIndex(RectTransform item, int index){EnsureItemRect(index);var managedItem managedItems[index];if (managedItem.item ! null managedItem.item.GetComponentScrollViewExItem()){item.GetComponentScrollViewExItem().itemIndex index;}Rect r managedItem.rect;item.localPosition r.position;item.sizeDelta r.size;}Vector2 GetItemSize(int index,ScrollItemWithRect item){if(index 0 index m_dataCount){if (itemSizeFunc ! null){return itemSizeFunc(index);}}return defaultItemSize;}private RectTransform GetNewItem(int index){RectTransform item;if(itemGetFunc ! null){item itemGetFunc(index);}else{item itemPool.Get();}return item;}private void RecycleOldItem(RectTransform item){if (itemRecycleFunc ! null){itemRecycleFunc(item);}else{itemPool.Recycle(item);}if (RecycleFunc ! null){RecycleFunc(item);}}void InitScrollView(){initialized true;// 根据设置来控制原ScrollRect的滚动方向int dir (int)layoutType flagScrollDirection;content.pivot Vector2.up;InitPool();UpdateRefRect();}Vector3[] viewWorldConers new Vector3[4];Vector3[] rectCorners new Vector3[2];void UpdateRefRect(){/** WorldCorners* * 1 ------- 2 * | |* | |* 0 ------- 3* */// refRect是在Content节点下的 viewport的 rectviewRect.GetWorldCorners(viewWorldConers);rectCorners[0] content.transform.InverseTransformPoint(viewWorldConers[0]);rectCorners[1] content.transform.InverseTransformPoint(viewWorldConers[2]);refRect new Rect((Vector2)rectCorners[0] - content.anchoredPosition, rectCorners[1] - rectCorners[0]);}void MovePos(ref Vector2 pos, Vector2 size){// 注意 所有的rect都是左下角为基准switch (layoutType){case ItemLayoutType.Vertical:// 垂直方向 向下移动pos.y - size.y;break;case ItemLayoutType.Horizontal:// 水平方向 向右移动pos.x size.x;break;case ItemLayoutType.VerticalThenHorizontal:pos.y - size.y;if (pos.y -(refRect.height - size.y / 2)){pos.y 0;pos.x size.x;}break;case ItemLayoutType.HorizontalThenVertical:pos.x size.x;if(pos.x refRect.width - size.x / 2){pos.x 0;pos.y - size.y;}break;default:break;}}protected void EnsureItemRect(int index){if (!managedItems[index].rectDirty){// 已经是干净的了return;}ScrollItemWithRect firstItem managedItems[0];if (firstItem.rectDirty){Vector2 firstSize GetItemSize(0, firstItem);firstItem.rect CreateWithLeftTopAndSize(Vector2.zero, firstSize);firstItem.rect.position defaultItemSpace;firstItem.rectDirty false;if (firstItem.item){firstItem.item.localPosition firstItem.rect.position;}}// 当前item之前的最近的已更新的rectint nearestClean 0;for (int i index; i 0; --i){if (!managedItems[i].rectDirty){nearestClean i;break;}}// 需要更新 从 nearestClean 到 index 的尺寸Rect nearestCleanRect managedItems[nearestClean].rect;Vector2 curPos GetLeftTop(nearestCleanRect);Vector2 size nearestCleanRect.size;MovePos(ref curPos, size);for (int i nearestClean 1; i index; i){size GetItemSize(i, managedItems[i]);managedItems[i].rect CreateWithLeftTopAndSize(curPos, size);managedItems[i].rect.position defaultItemSpace;managedItems[i].rectDirty false;MovePos(ref curPos, size);if (managedItems[i].item){managedItems[i].item.localPosition managedItems[i].rect.position;}}Vector2 range new Vector2(Mathf.Abs(curPos.x), Mathf.Abs(curPos.y));switch (layoutType){case ItemLayoutType.VerticalThenHorizontal:range.x size.x;range.y refRect.height;break;case ItemLayoutType.HorizontalThenVertical:range.x refRect.width;if (curPos.x ! 0){range.y size.y;}break;default:break;}content.sizeDelta range;}//选择Itempublic void SelectItem(int index){for (int i 0; i managedItems.Count; i){var managedItem managedItems[i];if (managedItem ! null managedItem.item ! null managedItem.item.GetComponentScrollViewExItem()){ScrollViewExItem item managedItem.item.GetComponentScrollViewExItem();item.SetSelected(item.itemIndex index);if (item.itemIndex index selectIndexFunc ! null){selectIndexFunc(index, managedItem.item);}}}}public UnityEventint, ScrollViewExItem onClickItem _onClickItem ?? (_onClickItem new UnityEventint, ScrollViewExItem());private static Vector2 GetLeftTop(Rect rect){Vector2 ret rect.position;ret.y rect.size.y;return ret;}private static Rect CreateWithLeftTopAndSize(Vector2 leftTop, Vector2 size){Vector2 leftBottom leftTop - new Vector2(0,size.y);//Debug.Log( leftBottom : leftBottom size : size );return new Rect(leftBottom,size);}protected override void OnDestroy(){if (itemPool ! null){itemPool.Purge();}}protected Rect GetItemLocalRect(int index){if(index 0 index m_dataCount){EnsureItemRect(index);return managedItems[index].rect;}return new Rect();}protected override void Awake(){base.Awake();onValueChanged.AddListener(OnValueChanged);}private void Update(){if (Input.GetMouseButtonUp(0) || Input.GetMouseButtonDown(0))canNextPage true;}bool reloadFlag false;private void OnValueChanged(Vector2 position){if (reloadFlag){UpdateData(true);reloadFlag false;}if (Input.GetMouseButton(0) !canNextPage) return;int toShow;int critical;bool downward;int pin;if (((int)layoutType flagScrollDirection) 1){// 垂直滚动 只计算y向if (velocity.y 0){// 向上toShow criticalItemIndex[CriticalItemType.DownToShow];critical pageSize - 1;if (toShow critical){return;}pin critical - 1;downward false;}else{// 向下toShow criticalItemIndex[CriticalItemType.UpToShow];critical 0;if (toShow critical){return;}pin critical 1;downward true;}}else // 0{// 水平滚动 只计算x向if (velocity.x 0){// 向右toShow criticalItemIndex[CriticalItemType.UpToShow];critical 0;if (toShow critical){return;}pin critical 1;downward true;}else{// 向左toShow criticalItemIndex[CriticalItemType.DownToShow];critical pageSize - 1;if (toShow critical){return;}pin critical - 1;downward false;}}// 翻页int old startOffset;if (downward){startOffset - pageSize / 2;}else{startOffset pageSize / 2;}canNextPage false;int realDataCount 0;if (realItemCountFunc ! null){realDataCount realItemCountFunc();}startOffset Mathf.Clamp(startOffset, 0, Mathf.Max(realDataCount - pageSize, 0));if (old ! startOffset){reloadFlag true;// 计算 pin元素的世界坐标Rect rect GetItemLocalRect(pin);Vector2 oldWorld content.TransformPoint(rect.position);UpdateData(true);int dataCount 0;if (itemCountFunc ! null){dataCount itemCountFunc();}if (dataCount 0){EnsureItemRect(0);if (dataCount 1){EnsureItemRect(dataCount - 1);}}// 根据 pin元素的世界坐标 计算出content的positionint pin2 pin old - startOffset;Rect rect2 GetItemLocalRect(pin2);Vector2 newWorld content.TransformPoint(rect2.position);Vector2 deltaWorld newWorld - oldWorld;Vector2 deltaLocal content.InverseTransformVector(deltaWorld);SetContentAnchoredPosition(content.anchoredPosition - deltaLocal);UpdateData(true);// 减速velocity / 50f;}} }ScrollViewExItem.cs using UnityEngine;public class ScrollViewExItem : MonoBehaviour {public ScrollViewEx scrollView;public int itemIndex;public bool isSelected;public void SetSelected(bool value){isSelected value;OnSelected();}//选择监听方法public virtual void OnSelected(){}//点击监听方法public virtual void OnClick(){scrollView.onClickItem.Invoke(itemIndex, this);} }还有一个工具类脚本SimpleObjPool.cs。 using System; using System.Collections.Generic;public class SimpleObjPoolT {private readonly StackT m_Stack;private readonly FuncT m_ctor;private readonly ActionT m_OnRecycle;private int m_Size;private int m_UsedCount;public SimpleObjPool(int max 5, ActionT actionOnReset null, Func T ctor null){m_Stack new StackT(max);m_Size max;m_OnRecycle actionOnReset;m_ctor ctor;}public T Get(){T item;if (m_Stack.Count 0){if(null ! m_ctor){item m_ctor();}else{item Activator.CreateInstanceT();}}else{item m_Stack.Pop();}m_UsedCount;return item;}public void Recycle(T item){if(m_OnRecycle! null){m_OnRecycle.Invoke(item);}if(m_Stack.Count m_Size){m_Stack.Push(item);}m_UsedCount -- ;}/*public T GetAndAutoRecycle(){T obj Get();Utils.OnNextFrameCall(() { Recycle(obj); });return obj;}*/public void Purge(){// TODO}public override string ToString(){return string.Format(SimpleObjPool: item[{0}], inUse[{1}], restInPool[{2}/{3}] , typeof(T), m_UsedCount, m_Stack.Count, m_Size);}} 以上三个脚本的代码就不一一细说了大家可以参考。 至此我们的滚动视图复用框架就完成了。 示例 示例代码 接下来贴出使用的组件截图和使用脚本示例代码。 使用的实力代码脚本为UIBoxDetail.cs和UIBoxDetailItem.cs。 using System.Collections.Generic; using Msg; using UnityEngine; using UnityEngine.UI;/// summary /// 宝箱详情UI /// /summary public class UIBoxDetail : BasePanel {public const string BoxList UI_Event_BoxList;// 宝箱列表public const string UnlockBox UI_Event_UnlockBox;// 解锁宝箱public const string ReduceTime UI_Event_ReduceTime;// 扣减广告加速时间public RectTransform coinDiamondRoot;public Button btnBack;/// summary/// 宝箱背景类型/// /summarypublic enum BgType{None,// 无宝箱Lock,// 未解锁SpeedUp,// 加速Get,// 领取Overflow// 已满}public ScrollViewEx detailSrEx;private void OnEnable(){EventMgr.GetInstance().AddEventListenerBoxOpenResponse(BoxEvent.BoxOpenResponse, OnBoxOpenResponse);}private void OnDisable(){EventMgr.GetInstance().RemoveEventListenerBoxOpenResponse(BoxEvent.BoxOpenResponse, OnBoxOpenResponse);}protected override void Awake(){detailSrEx.UpdateData(false);detailSrEx.SetUpdateFunc((index, rectTransform) {UIBoxDetailItem item rectTransform.GetComponentUIBoxDetailItem();item.OnRefresh(BoxModel.BoxList[index]);});detailSrEx.SetItemCountFunc(() BoxModel.BoxList.Count);}private void Start(){BoxMgr.GetInstance().BoxListReq();UIMgr.GetInstance().ShowInnerRes(coinDiamondRoot, new ListTopInnerResDataVo{new TopInnerResDataVo(E_TopInnerRes.Coin, PersonalInfoModel.Player.NumGold),new TopInnerResDataVo(E_TopInnerRes.Diamond, PersonalInfoModel.Player.NumStone)});txtClose.text MultilingualUtil.MultilingualText(29);btnBack.onClick.AddListener(HideMe);}public override void Notify(string msgType, object msgData){base.Notify(msgType, msgData);switch (msgType){case BoxList:case UnlockBox:case ReduceTime:RefreshBoxList(msgData as Box[]);break;}}private void RefreshBoxList(Box[] boxes){detailSrEx.UpdateData(false);detailSrEx.SetUpdateFunc((index, rectTransform) {UIBoxDetailItem item rectTransform.GetComponentUIBoxDetailItem();item.OnRefresh(boxes[index]);});detailSrEx.SetItemCountFunc(() boxes.Length);}#region responseprivate void OnBoxOpenResponse(BoxOpenResponse response){detailSrEx.SetUpdateFunc((index, rectTransform) {rectTransform.name index.ToString();});detailSrEx.SetItemCountFunc(() BoxModel.BoxList.Count);}#endregion[Header(---- 多语言控件 ----)]public Text txtClose; }using System.Text; using Msg; using UnityEngine; using UnityEngine.UI;public class UIBoxDetailItem : ScrollViewExItem {public RectTransform timeGroup;public Image imgBg;public Image imgIcon;public Image imgMask;public Text txtTime;public Text txtTips;public Text txtEmpty;public Text txtTitle;public Button btnTitle;private StringBuilder _iconPath new StringBuilder();// icon路径private StringBuilder _titleBuilder new StringBuilder();private UIBoxDetail.BgType _selectedType;// 当前选中宝箱private Timer _timer;private Timer _timerUpdate;private long _countdownStamp;// 倒计时时间private bool _isTimeGroup;// 是否启用时间组件private bool _isTime;// 是否启用时间文本UIprivate bool _isIcon;// 是否启用Iconprivate void Start(){_isTimeGroup false;_isTime false;_isIcon false;_selectedType UIBoxDetail.BgType.None;// 默认无btnTitle.onClick.AddListener(() {BoxModel.SetBox(BoxModel.BoxList[itemIndex]);switch (_selectedType){case UIBoxDetail.BgType.Lock:case UIBoxDetail.BgType.SpeedUp:UIMgr.GetInstance().ShowPanelUIBoxOpen(UIDef.UI_BOXOPEN, BoxModel.BoxList[itemIndex]);break;case UIBoxDetail.BgType.Get:// 直接领取奖励BoxMgr.GetInstance().BoxClaimRewardReq(BoxModel.BoxList[itemIndex].BoxID, BoxModel.BoxList[itemIndex].ID);break;}});}private void OnDestroy(){_timer?.Stop();_timerUpdate?.Stop();}public void OnRefresh(Box data){_timer?.Stop();_timerUpdate?.Stop();// 创建新角色没匹配时宝箱列表没有长度if (data null){RefreshContent(UIBoxDetail.BgType.None, null);return;}// 新角色匹配后宝箱列表有长度if (data.ID ! string.Empty data.BoxID 0){RefreshContent(UIBoxDetail.BgType.None, data);return;}if (data.ID string.Empty data.BoxID 0){RefreshContent(UIBoxDetail.BgType.None, data);return;}var boxCfg ConfigManager._BoxCfgMgr.GetDataByID((int)data.BoxID);var second BoxMgr.GetInstance().CalculateSecond(boxCfg.LifeTime);if (data.UnlockTimeStamp 0)// 未解锁{RefreshContent(UIBoxDetail.BgType.Lock, data);txtTime.text second 10? second MultilingualUtil.MultilingualText(426): second MultilingualUtil.MultilingualText(280);}else if (data.UnlockTimeStamp 0 TimeUtil.GetUnixTimeStamp() data.UnlockTimeStamp)// 加速{RefreshContent(UIBoxDetail.BgType.SpeedUp, data);_countdownStamp data.UnlockTimeStamp - TimeUtil.GetUnixTimeStamp() - data.ReduceTime;// 当前宝箱时间戳小于_timer new Timer(1f, true, () {_countdownStamp--;if (txtTime ! null)txtTime.text TimeUtil.FormatTime(_countdownStamp);});_timer.Start();_timerUpdate new Timer(Time.deltaTime, true, () {if (_countdownStamp 0){BoxMgr.GetInstance().BoxListReq();// 重新请求宝箱列表BoxModel.SetHasSpeedUp(false);_timer?.Stop();_timerUpdate?.Stop();}});_timerUpdate.Start();}else if (data.UnlockTimeStamp 0 TimeUtil.GetUnixTimeStamp() data.UnlockTimeStamp)// 可领取{RefreshContent(UIBoxDetail.BgType.Get, data);}if (data.UnlockTimeStamp 0)txtTime.text second 10? second MultilingualUtil.MultilingualText(426): second MultilingualUtil.MultilingualText(280);elsetxtTime.text TimeUtil.FormatTime(_countdownStamp);imgIcon.sprite AssetBundleMgr.GetInstance().LoadUISprite(data.BoxID ! 0 ? boxCfg.Icon : );}/// summary/// 刷新内容/// /summary/// param nametype类型/param/// param namedata宝箱数据/paramprivate void RefreshContent(UIBoxDetail.BgType type, Box data){_iconPath.Clear();_titleBuilder.Clear();switch (type){case UIBoxDetail.BgType.None: // 无宝箱_isTime false;_isTimeGroup false;_isIcon false;_selectedType UIBoxDetail.BgType.None;_iconPath.Append(UIAtlas/Box/empty_btn);break;case UIBoxDetail.BgType.Lock: // 未解锁_isTime true;_isTimeGroup false;_isIcon true;_selectedType UIBoxDetail.BgType.Lock;_titleBuilder.Append(MultilingualUtil.MultilingualText(85));_iconPath.Append(UIAtlas/Box/treasure02_btn);break;case UIBoxDetail.BgType.SpeedUp: // 加速_isTime true;_isTimeGroup true;_isIcon true;_selectedType UIBoxDetail.BgType.SpeedUp;_titleBuilder.Append(MultilingualUtil.MultilingualText(86));_iconPath.Append(UIAtlas/Box/treasure01_btn);break;case UIBoxDetail.BgType.Get: // 领取奖励_isTime false;_isTimeGroup false;_isIcon true;_selectedType UIBoxDetail.BgType.Get;_titleBuilder.Append(MultilingualUtil.MultilingualText(87));_iconPath.Append(UIAtlas/Box/open_btn);break;}var boxCfg ConfigManager._BoxCfgMgr.GetDataByID((int)data.BoxID);if (boxCfg ! null)txtTips.text BoxCfgMgr.Instance.GetMultiLangName(boxCfg);txtTitle.text _titleBuilder.ToString();txtEmpty.text MultilingualUtil.MultilingualText(84);imgBg.sprite AssetBundleMgr.GetInstance().LoadUISprite(_iconPath.ToString());imgIcon.gameObject.SetActive(_isIcon);txtTime.gameObject.SetActive(data.BoxID ! 0 _isTime);timeGroup.gameObject.SetActive(_isTimeGroup);imgMask.gameObject.SetActive(data.BoxID ! 0 data.ReduceTime ! 0);txtTips.gameObject.SetActive(data.BoxID ! 0);txtEmpty.gameObject.SetActive(data.BoxID 0);btnTitle.gameObject.SetActive(data.BoxID ! 0);} }示例组件截图 itemTemplate需要指定一个有UIBoxDetailItem脚本的显示对象如下图所示。 最后 其中还有更多的细节就未能一一提及。 当然还有更多有待优化的逻辑需要大家来指出。
http://www.hkea.cn/news/14334460/

相关文章:

  • hyip网站开发怎么做淘宝客网站赚钱
  • 网站管理助手未找到iis广告推广公司
  • 网站如何做站内站跑腿个人网站怎么做
  • django企业网站源码网站规划与开发技术
  • 素材网站的图可以做海报吗免费签名设计
  • 做网站报价手机网站的制作
  • 网站推广的常用方法有哪些?西安seo包年服务
  • 模板网站怎么优化建网站多少钱可以卖货的
  • 响应式网站的特点如何进行网站建设的销售
  • 有做lol直播网站在线做GO分析的网站
  • 有了域名就可以做网站了吗互助网站建设
  • 内江市住房和城乡建设局网站东北网站建设公司
  • 网站内容收费码支付wordpress用不
  • 广州微网站建设案例网站怎样做公众号
  • 黄石网站建设广东佛山网络有限公司
  • 宁波网站建设ysdshwordpress首页弹窗
  • 网站开发专业术语大全网站建设建构
  • 网站建设 律师wordpress会员可见插件
  • 阿里云虚拟主机做2个网站吗做外贸的网站有何用处
  • 如何建立一个网站链接把文件信息存里厨师培训机构 厨师短期培训班
  • 在山东省建设监理协会网站工信部 网站备案材料 复印件 电子版
  • 特产网站怎么做投资公司怎么赚钱
  • 石家庄哪里有做外贸网站的公司建一个类似亨物说网站建设费用
  • 好的ppt模板免费下载网站哪个网站可以做加工
  • 小型企业网站如何建设网页页面设计尺寸
  • 网站设置二级域名网站建设一般报价
  • 百度网站数据统计怎么做wordpress忘记密码了
  • 最牛网站建设是谁东莞关键词搜索排名
  • 2018年做淘宝客网站需要备案嘛平面设计素材网站推荐
  • 黑河网站制作织梦做的网站首页被篡改