透明小地图&列表点击切换&海拔图头像位移
This commit is contained in:
1 parent
bf48989a9c
commit
6ec4c0101b
114 files changed
+12787
-6517
No files matched your search
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 954ddfcde675c304baf014480514e599
|
||||
folderAsset: yes
|
||||
timeCreated: 1598985910
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
//MIT License
|
||||
//Copyright (c) 2020 Mohammed Iqubal Hussain
|
||||
//Website : Polyandcode.com
|
||||
|
||||
/// <summary>
|
||||
/// Interface for creating a Cell.
|
||||
/// Prototype Cell must have a monobeviour inheriting from ICell
|
||||
/// </summary>
|
||||
namespace PolyAndCode.UI
|
||||
{
|
||||
public interface ICell
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d4ad8716ee240469ca93bc22214fddfe
|
||||
timeCreated: 1577787384
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
//MIT License
|
||||
//Copyright (c) 2020 Mohammed Iqubal Hussain
|
||||
//Website : Polyandcode.com
|
||||
|
||||
/// <summary>
|
||||
/// Interface for creating DataSource
|
||||
/// Recyclable Scroll Rect must be provided a Data source which must inherit from this.
|
||||
/// </summary>
|
||||
namespace PolyAndCode.UI
|
||||
{
|
||||
public interface IRecyclableScrollRectDataSource
|
||||
{
|
||||
int GetItemCount();
|
||||
void SetCell(ICell cell, int index);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ef29d4c7afcd43079f3cfc4d4e1ecbf
|
||||
timeCreated: 1580410346
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,154 @@
|
||||
//MIT License
|
||||
//Copyright (c) 2020 Mohammed Iqubal Hussain
|
||||
//Website : Polyandcode.com
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace PolyAndCode.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Entry for the recycling system. Extends Unity's inbuilt ScrollRect.
|
||||
/// </summary>
|
||||
public class RecyclableScrollRect : ScrollRect
|
||||
{
|
||||
[HideInInspector]
|
||||
public IRecyclableScrollRectDataSource DataSource;
|
||||
|
||||
public bool IsGrid;
|
||||
//Prototype cell can either be a prefab or present as a child to the content(will automatically be disabled in runtime)
|
||||
public RectTransform PrototypeCell;
|
||||
//If true the intiziation happens at Start. Controller must assign the datasource in Awake.
|
||||
//Set to false if self init is not required and use public init API.
|
||||
public bool SelfInitialize = true;
|
||||
|
||||
public enum DirectionType
|
||||
{
|
||||
Vertical,
|
||||
Horizontal
|
||||
}
|
||||
|
||||
public DirectionType Direction;
|
||||
|
||||
//Segments : coloums for vertical and rows for horizontal.
|
||||
public int Segments
|
||||
{
|
||||
set
|
||||
{
|
||||
_segments = Math.Max(value, 2);
|
||||
}
|
||||
get
|
||||
{
|
||||
return _segments;
|
||||
}
|
||||
}
|
||||
[SerializeField]
|
||||
private int _segments;
|
||||
|
||||
private RecyclingSystem _recyclingSystem;
|
||||
private Vector2 _prevAnchoredPos;
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
//defafult(built-in) in scroll rect can have both directions enabled, Recyclable scroll rect can be scrolled in only one direction.
|
||||
//setting default as vertical, Initialize() will set this again.
|
||||
vertical = true;
|
||||
horizontal = false;
|
||||
|
||||
if (!Application.isPlaying) return;
|
||||
|
||||
if (SelfInitialize) Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialization when selfInitalize is true. Assumes that data source is set in controller's Awake.
|
||||
/// </summary>
|
||||
private void Initialize()
|
||||
{
|
||||
//Contruct the recycling system.
|
||||
if (Direction == DirectionType.Vertical)
|
||||
{
|
||||
_recyclingSystem = new VerticalRecyclingSystem(PrototypeCell, viewport, content, DataSource, IsGrid, Segments);
|
||||
}
|
||||
else if (Direction == DirectionType.Horizontal)
|
||||
{
|
||||
_recyclingSystem = new HorizontalRecyclingSystem(PrototypeCell, viewport, content, DataSource, IsGrid, Segments);
|
||||
}
|
||||
vertical = Direction == DirectionType.Vertical;
|
||||
horizontal = Direction == DirectionType.Horizontal;
|
||||
|
||||
_prevAnchoredPos = content.anchoredPosition;
|
||||
onValueChanged.RemoveListener(OnValueChangedListener);
|
||||
//Adding listener after pool creation to avoid any unwanted recycling behaviour.(rare scenerio)
|
||||
StartCoroutine(_recyclingSystem.InitCoroutine(() =>
|
||||
onValueChanged.AddListener(OnValueChangedListener)
|
||||
));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// public API for Initializing when datasource is not set in controller's Awake. Make sure selfInitalize is set to false.
|
||||
/// </summary>
|
||||
public void Initialize(IRecyclableScrollRectDataSource dataSource)
|
||||
{
|
||||
DataSource = dataSource;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Added as a listener to the OnValueChanged event of Scroll rect.
|
||||
/// Recycling entry point for recyling systems.
|
||||
/// </summary>
|
||||
/// <param name="direction">scroll direction</param>
|
||||
public void OnValueChangedListener(Vector2 normalizedPos)
|
||||
{
|
||||
Vector2 dir = content.anchoredPosition - _prevAnchoredPos;
|
||||
m_ContentStartPosition += _recyclingSystem.OnValueChangedListener(dir);
|
||||
_prevAnchoredPos = content.anchoredPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///Reloads the data. Call this if a new datasource is assigned.
|
||||
/// </summary>
|
||||
public void ReloadData()
|
||||
{
|
||||
ReloadData(DataSource);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overloaded ReloadData with dataSource param
|
||||
///Reloads the data. Call this if a new datasource is assigned.
|
||||
/// </summary>
|
||||
public void ReloadData(IRecyclableScrollRectDataSource dataSource)
|
||||
{
|
||||
if (_recyclingSystem != null)
|
||||
{
|
||||
StopMovement();
|
||||
onValueChanged.RemoveListener(OnValueChangedListener);
|
||||
_recyclingSystem.DataSource = dataSource;
|
||||
StartCoroutine(_recyclingSystem.InitCoroutine(() =>
|
||||
onValueChanged.AddListener(OnValueChangedListener)
|
||||
));
|
||||
_prevAnchoredPos = content.anchoredPosition;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#region Testing
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
if (_recyclableScrollRect is VerticalRecyclingSystem)
|
||||
{
|
||||
((VerticalRecyclingSystem)_recyclableScrollRect).OnDrawGizmos();
|
||||
}
|
||||
|
||||
if (_recyclableScrollRect is HorizontalRecyclingSystem)
|
||||
{
|
||||
((HorizontalRecyclingSystem)_recyclableScrollRect).OnDrawGizmos();
|
||||
}
|
||||
|
||||
}
|
||||
#endregion
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 248d9fa993d3e44daa165a75b0063088
|
||||
timeCreated: 1580416764
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 6b7325ceb7a524d43bf38b93ad0c7118, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d1b6a61b2a8a958489c9e8598a56feda
|
||||
folderAsset: yes
|
||||
timeCreated: 1598985947
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+383
@@ -0,0 +1,383 @@
|
||||
//MIT License
|
||||
//Copyright (c) 2020 Mohammed Iqubal Hussain
|
||||
//Website : Polyandcode.com
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace PolyAndCode.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Recyling system for horizontal type.
|
||||
/// </summary>
|
||||
public class HorizontalRecyclingSystem : RecyclingSystem
|
||||
{
|
||||
//Assigned by constructor
|
||||
private readonly int _rows;
|
||||
|
||||
//Cell dimensions
|
||||
private float _cellWidth, _cellHeight;
|
||||
|
||||
//Pool Generation
|
||||
private List<RectTransform> _cellPool;
|
||||
private List<ICell> _cachedCells;
|
||||
private Bounds _recyclableViewBounds;
|
||||
|
||||
|
||||
//Temps, Flags
|
||||
private readonly Vector3[] _corners = new Vector3[4];
|
||||
private bool _recycling;
|
||||
|
||||
//Trackers
|
||||
private int currentItemCount; //item count corresponding to the datasource.
|
||||
private int leftMostCellIndex, rightMostCellIndex; //Topmost and bottommost cell in the List
|
||||
private int _leftMostCellRow, _RightMostCellRow; // used for recyling in Grid layout. leftmost and rightmost row
|
||||
|
||||
//Cached zero vector
|
||||
private Vector2 zeroVector = Vector2.zero;
|
||||
#region INIT
|
||||
public HorizontalRecyclingSystem(RectTransform prototypeCell, RectTransform viewport, RectTransform content, IRecyclableScrollRectDataSource dataSource, bool isGrid, int rows)
|
||||
{
|
||||
PrototypeCell = prototypeCell;
|
||||
Viewport = viewport;
|
||||
Content = content;
|
||||
DataSource = dataSource;
|
||||
IsGrid = isGrid;
|
||||
_rows = isGrid ? rows : 1;
|
||||
_recyclableViewBounds = new Bounds();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Corotuine for initiazation.
|
||||
/// Using coroutine for init because few UI stuff requires a frame to update
|
||||
/// </summary>
|
||||
/// <param name="onInitialized">callback when init done</param>
|
||||
/// <returns></returns>
|
||||
public override IEnumerator InitCoroutine(Action onInitialized)
|
||||
{
|
||||
//Setting up container and bounds
|
||||
SetLeftAnchor(Content);
|
||||
Content.anchoredPosition = Vector3.zero;
|
||||
yield return null;
|
||||
SetRecyclingBounds();
|
||||
|
||||
//Cell Poool
|
||||
CreateCellPool();
|
||||
currentItemCount = _cellPool.Count;
|
||||
leftMostCellIndex = 0;
|
||||
rightMostCellIndex = _cellPool.Count - 1;
|
||||
|
||||
//Set content width according to no of coloums
|
||||
int coloums = Mathf.CeilToInt((float)_cellPool.Count / _rows);
|
||||
float contentXSize = coloums * _cellWidth;
|
||||
Content.sizeDelta = new Vector2(contentXSize, Content.sizeDelta.y);
|
||||
SetLeftAnchor(Content);
|
||||
|
||||
if (onInitialized != null) onInitialized();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the uppper and lower bounds for recycling cells.
|
||||
/// </summary>
|
||||
private void SetRecyclingBounds()
|
||||
{
|
||||
Viewport.GetWorldCorners(_corners);
|
||||
float threshHold = RecyclingThreshold * (_corners[2].x - _corners[0].x);
|
||||
_recyclableViewBounds.min = new Vector3(_corners[0].x - threshHold, _corners[0].y);
|
||||
_recyclableViewBounds.max = new Vector3(_corners[2].x + threshHold, _corners[2].y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates cell Pool for recycling, Caches ICells
|
||||
/// </summary>
|
||||
private void CreateCellPool()
|
||||
{
|
||||
//Reseting Pool
|
||||
if (_cellPool != null)
|
||||
{
|
||||
_cellPool.ForEach((RectTransform item) => UnityEngine.Object.Destroy(item.gameObject));
|
||||
_cellPool.Clear();
|
||||
_cachedCells.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
_cachedCells = new List<ICell>();
|
||||
_cellPool = new List<RectTransform>();
|
||||
}
|
||||
|
||||
//Set the prototype cell active and set cell anchor as top
|
||||
PrototypeCell.gameObject.SetActive(true);
|
||||
SetLeftAnchor(PrototypeCell);
|
||||
|
||||
//set new cell size according to its aspect ratio
|
||||
_cellHeight = Content.rect.height / _rows;
|
||||
_cellWidth = PrototypeCell.sizeDelta.x / PrototypeCell.sizeDelta.y * _cellHeight;
|
||||
|
||||
//Reset
|
||||
_leftMostCellRow = _RightMostCellRow = 0;
|
||||
|
||||
//Temps
|
||||
float currentPoolCoverage = 0;
|
||||
int poolSize = 0;
|
||||
float posX = 0;
|
||||
float posY = 0;
|
||||
|
||||
//Get the required pool coverage and mininum size for the Cell pool
|
||||
float requriedCoverage = MinPoolCoverage * Viewport.rect.width;
|
||||
int minPoolSize = Math.Min(MinPoolSize, DataSource.GetItemCount());
|
||||
|
||||
//create cells untill the Pool area is covered and pool size is the minimum required
|
||||
while ((poolSize < minPoolSize || currentPoolCoverage < requriedCoverage) && poolSize < DataSource.GetItemCount())
|
||||
{
|
||||
//Instantiate and add to Pool
|
||||
RectTransform item = (UnityEngine.Object.Instantiate(PrototypeCell.gameObject)).GetComponent<RectTransform>();
|
||||
item.name = "Cell";
|
||||
item.sizeDelta = new Vector2(_cellWidth, _cellHeight);
|
||||
_cellPool.Add(item);
|
||||
item.SetParent(Content, false);
|
||||
|
||||
if (IsGrid)
|
||||
{
|
||||
posY = -_RightMostCellRow * _cellHeight;
|
||||
item.anchoredPosition = new Vector2(posX, posY);
|
||||
if (++_RightMostCellRow >= _rows)
|
||||
{
|
||||
_RightMostCellRow = 0;
|
||||
posX += _cellWidth;
|
||||
currentPoolCoverage += item.rect.width;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
item.anchoredPosition = new Vector2(posX, 0);
|
||||
posX = item.anchoredPosition.x + item.rect.width;
|
||||
currentPoolCoverage += item.rect.width;
|
||||
}
|
||||
|
||||
//Setting data for Cell
|
||||
_cachedCells.Add(item.GetComponent<ICell>());
|
||||
DataSource.SetCell(_cachedCells[_cachedCells.Count - 1], poolSize);
|
||||
|
||||
//Update the Pool size
|
||||
poolSize++;
|
||||
}
|
||||
|
||||
if (IsGrid)
|
||||
{
|
||||
_RightMostCellRow = (_RightMostCellRow - 1 + _rows) % _rows;
|
||||
}
|
||||
|
||||
//Deactivate prototype cell if it is not a prefab(i.e it's present in scene)
|
||||
if (PrototypeCell.gameObject.scene.IsValid())
|
||||
{
|
||||
PrototypeCell.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region RECYCLING
|
||||
/// <summary>
|
||||
/// Recyling entry point
|
||||
/// </summary>
|
||||
/// <param name="direction">scroll direction </param>
|
||||
/// <returns></returns>
|
||||
public override Vector2 OnValueChangedListener(Vector2 direction)
|
||||
{
|
||||
if (_recycling || _cellPool == null || _cellPool.Count == 0) return zeroVector;
|
||||
|
||||
//Updating Recyclable view bounds since it can change with resolution changes.
|
||||
SetRecyclingBounds();
|
||||
|
||||
if (direction.x < 0 && _cellPool[rightMostCellIndex].MinX() < _recyclableViewBounds.max.x)
|
||||
{
|
||||
return RecycleLeftToRight();
|
||||
}
|
||||
else if (direction.x > 0 && _cellPool[leftMostCellIndex].MaxX() > _recyclableViewBounds.min.x)
|
||||
{
|
||||
return RecycleRightToleft();
|
||||
}
|
||||
return zeroVector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recycles cells from Left to Right in the List heirarchy
|
||||
/// </summary>
|
||||
private Vector2 RecycleLeftToRight()
|
||||
{
|
||||
_recycling = true;
|
||||
|
||||
int n = 0;
|
||||
float posX = IsGrid ? _cellPool[rightMostCellIndex].anchoredPosition.x : 0;
|
||||
float posY = 0;
|
||||
|
||||
//to determine if content size needs to be updated
|
||||
int additionalColoums = 0;
|
||||
|
||||
//Recycle until cell at left is avaiable and current item count smaller than datasource
|
||||
while (_cellPool[leftMostCellIndex].MaxX() < _recyclableViewBounds.min.x && currentItemCount < DataSource.GetItemCount())
|
||||
{
|
||||
if (IsGrid)
|
||||
{
|
||||
if (++_RightMostCellRow >= _rows)
|
||||
{
|
||||
n++;
|
||||
_RightMostCellRow = 0;
|
||||
posX = _cellPool[rightMostCellIndex].anchoredPosition.x + _cellWidth;
|
||||
additionalColoums++;
|
||||
}
|
||||
|
||||
//Move Left most cell to right
|
||||
posY = -_RightMostCellRow * _cellHeight;
|
||||
_cellPool[leftMostCellIndex].anchoredPosition = new Vector2(posX, posY);
|
||||
|
||||
if (++_leftMostCellRow >= _rows)
|
||||
{
|
||||
_leftMostCellRow = 0;
|
||||
additionalColoums--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Move Left most cell to right
|
||||
posX = _cellPool[rightMostCellIndex].anchoredPosition.x + _cellPool[rightMostCellIndex].sizeDelta.x;
|
||||
_cellPool[leftMostCellIndex].anchoredPosition = new Vector2(posX, _cellPool[leftMostCellIndex].anchoredPosition.y);
|
||||
}
|
||||
|
||||
//Cell for row at
|
||||
DataSource.SetCell(_cachedCells[leftMostCellIndex], currentItemCount);
|
||||
|
||||
//set new indices
|
||||
rightMostCellIndex = leftMostCellIndex;
|
||||
leftMostCellIndex = (leftMostCellIndex + 1) % _cellPool.Count;
|
||||
|
||||
currentItemCount++;
|
||||
if (!IsGrid) n++;
|
||||
}
|
||||
|
||||
//Content size adjustment
|
||||
if (IsGrid)
|
||||
{
|
||||
Content.sizeDelta += additionalColoums * Vector2.right * _cellWidth;
|
||||
if (additionalColoums > 0)
|
||||
{
|
||||
n -= additionalColoums;
|
||||
}
|
||||
}
|
||||
|
||||
//Content anchor position adjustment.
|
||||
_cellPool.ForEach((RectTransform cell) => cell.anchoredPosition -= n * Vector2.right * _cellPool[leftMostCellIndex].sizeDelta.x);
|
||||
Content.anchoredPosition += n * Vector2.right * _cellPool[leftMostCellIndex].sizeDelta.x;
|
||||
_recycling = false;
|
||||
return n * Vector2.right * _cellPool[leftMostCellIndex].sizeDelta.x;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recycles cells from Right to Left in the List heirarchy
|
||||
/// </summary>
|
||||
private Vector2 RecycleRightToleft()
|
||||
{
|
||||
_recycling = true;
|
||||
|
||||
int n = 0;
|
||||
float posX = IsGrid ? _cellPool[leftMostCellIndex].anchoredPosition.x : 0;
|
||||
float posY = 0;
|
||||
|
||||
//to determine if content size needs to be updated
|
||||
int additionalColoums = 0;
|
||||
//Recycle until cell at Right end is avaiable and current item count is greater than cellpool size
|
||||
while (_cellPool[rightMostCellIndex].MinX() > _recyclableViewBounds.max.x && currentItemCount > _cellPool.Count)
|
||||
{
|
||||
if (IsGrid)
|
||||
{
|
||||
if (--_leftMostCellRow < 0)
|
||||
{
|
||||
n++;
|
||||
_leftMostCellRow = _rows - 1;
|
||||
posX = _cellPool[leftMostCellIndex].anchoredPosition.x - _cellWidth;
|
||||
additionalColoums++;
|
||||
}
|
||||
|
||||
//Move Right most cell to left
|
||||
posY = -_leftMostCellRow * _cellHeight;
|
||||
_cellPool[rightMostCellIndex].anchoredPosition = new Vector2(posX, posY);
|
||||
|
||||
if (--_RightMostCellRow < 0)
|
||||
{
|
||||
_RightMostCellRow = _rows - 1;
|
||||
additionalColoums--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Move Right most cell to left
|
||||
posX = _cellPool[leftMostCellIndex].anchoredPosition.x - _cellPool[leftMostCellIndex].sizeDelta.x;
|
||||
_cellPool[rightMostCellIndex].anchoredPosition = new Vector2(posX, _cellPool[rightMostCellIndex].anchoredPosition.y);
|
||||
n++;
|
||||
}
|
||||
|
||||
currentItemCount--;
|
||||
//Cell for row at
|
||||
DataSource.SetCell(_cachedCells[rightMostCellIndex], currentItemCount - _cellPool.Count);
|
||||
|
||||
//set new indices
|
||||
leftMostCellIndex = rightMostCellIndex;
|
||||
rightMostCellIndex = (rightMostCellIndex - 1 + _cellPool.Count) % _cellPool.Count;
|
||||
}
|
||||
|
||||
//Content size adjustment
|
||||
if (IsGrid)
|
||||
{
|
||||
Content.sizeDelta += additionalColoums * Vector2.right * _cellWidth;
|
||||
if (additionalColoums > 0)
|
||||
{
|
||||
n -= additionalColoums;
|
||||
}
|
||||
}
|
||||
|
||||
//Content anchor position adjustment.
|
||||
_cellPool.ForEach((RectTransform cell) => cell.anchoredPosition += n * Vector2.right * _cellPool[leftMostCellIndex].sizeDelta.x);
|
||||
Content.anchoredPosition -= n * Vector2.right * _cellPool[leftMostCellIndex].sizeDelta.x;
|
||||
_recycling = false;
|
||||
return -n * Vector2.right * _cellPool[leftMostCellIndex].sizeDelta.x;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region HELPERS
|
||||
/// <summary>
|
||||
/// Anchoring cell and content rect transforms to top preset. Makes repositioning easy.
|
||||
/// </summary>
|
||||
/// <param name="rectTransform"></param>
|
||||
private void SetLeftAnchor(RectTransform rectTransform)
|
||||
{
|
||||
//Saving to reapply after anchoring. Width and height changes if anchoring is change.
|
||||
float width = rectTransform.rect.width;
|
||||
float height = rectTransform.rect.height;
|
||||
|
||||
Vector2 pos = IsGrid ? new Vector2(0, 1) : new Vector2(0, 0.5f);
|
||||
|
||||
//Setting top anchor
|
||||
rectTransform.anchorMin = pos;
|
||||
rectTransform.anchorMax = pos;
|
||||
rectTransform.pivot = pos;
|
||||
|
||||
//Reapply size
|
||||
rectTransform.sizeDelta = new Vector2(width, height);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region TESTING
|
||||
public void OnDrawGizmos()
|
||||
{
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawLine(_recyclableViewBounds.min - new Vector3(0, 2000), _recyclableViewBounds.min + new Vector3(0, 2000));
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawLine(_recyclableViewBounds.max - new Vector3(0, 2000), _recyclableViewBounds.max + new Vector3(0, 2000));
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18e7b2b6055ccc94590a39b5401147a8
|
||||
timeCreated: 1595684755
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,29 @@
|
||||
//MIT License
|
||||
//Copyright (c) 2020 Mohammed Iqubal Hussain
|
||||
//Website : Polyandcode.com
|
||||
|
||||
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
namespace PolyAndCode.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Absract Class for creating a Recycling system.
|
||||
/// </summary>
|
||||
public abstract class RecyclingSystem
|
||||
{
|
||||
public IRecyclableScrollRectDataSource DataSource;
|
||||
|
||||
protected RectTransform Viewport, Content;
|
||||
protected RectTransform PrototypeCell;
|
||||
protected bool IsGrid;
|
||||
|
||||
protected float MinPoolCoverage = 1.5f; // The recyclable pool must cover (viewPort * _poolCoverage) area.
|
||||
protected int MinPoolSize = 10; // Cell pool must have a min size
|
||||
protected float RecyclingThreshold = .2f; //Threshold for recycling above and below viewport
|
||||
|
||||
public abstract IEnumerator InitCoroutine(System.Action onInitialized = null);
|
||||
|
||||
public abstract Vector2 OnValueChangedListener(Vector2 direction);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 52518c8e74351d0469a587c411d4438f
|
||||
timeCreated: 1595684222
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+404
@@ -0,0 +1,404 @@
|
||||
//MIT License
|
||||
//Copyright (c) 2020 Mohammed Iqubal Hussain
|
||||
//Website : Polyandcode.com
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace PolyAndCode.UI
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Recyling system for Vertical type.
|
||||
/// </summary>
|
||||
public class VerticalRecyclingSystem : RecyclingSystem
|
||||
{
|
||||
//Assigned by constructor
|
||||
private readonly int _coloumns;
|
||||
|
||||
//Cell dimensions
|
||||
private float _cellWidth, _cellHeight;
|
||||
|
||||
//Pool Generation
|
||||
private List<RectTransform> _cellPool;
|
||||
private List<ICell> _cachedCells;
|
||||
private Bounds _recyclableViewBounds;
|
||||
|
||||
//Temps, Flags
|
||||
private readonly Vector3[] _corners = new Vector3[4];
|
||||
private bool _recycling;
|
||||
|
||||
//Trackers
|
||||
private int currentItemCount; //item count corresponding to the datasource.
|
||||
private int topMostCellIndex, bottomMostCellIndex; //Topmost and bottommost cell in the heirarchy
|
||||
private int _topMostCellColoumn, _bottomMostCellColoumn; // used for recyling in Grid layout. top-most and bottom-most coloumn
|
||||
|
||||
//Cached zero vector
|
||||
private Vector2 zeroVector = Vector2.zero;
|
||||
|
||||
#region INIT
|
||||
public VerticalRecyclingSystem(RectTransform prototypeCell, RectTransform viewport, RectTransform content, IRecyclableScrollRectDataSource dataSource, bool isGrid, int coloumns)
|
||||
{
|
||||
PrototypeCell = prototypeCell;
|
||||
Viewport = viewport;
|
||||
Content = content;
|
||||
DataSource = dataSource;
|
||||
IsGrid = isGrid;
|
||||
_coloumns = isGrid ? coloumns : 1;
|
||||
_recyclableViewBounds = new Bounds();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Corotuine for initiazation.
|
||||
/// Using coroutine for init because few UI stuff requires a frame to update
|
||||
/// </summary>
|
||||
/// <param name="onInitialized">callback when init done</param>
|
||||
/// <returns></returns>>
|
||||
public override IEnumerator InitCoroutine(System.Action onInitialized)
|
||||
{
|
||||
SetTopAnchor(Content);
|
||||
Content.anchoredPosition = Vector3.zero;
|
||||
yield return null;
|
||||
SetRecyclingBounds();
|
||||
|
||||
//Cell Poool
|
||||
CreateCellPool();
|
||||
currentItemCount = _cellPool.Count;
|
||||
topMostCellIndex = 0;
|
||||
bottomMostCellIndex = _cellPool.Count - 1;
|
||||
|
||||
//Set content height according to no of rows
|
||||
int noOfRows = (int)Mathf.Ceil((float)_cellPool.Count / (float)_coloumns);
|
||||
float contentYSize = noOfRows * _cellHeight;
|
||||
Content.sizeDelta = new Vector2(Content.sizeDelta.x, contentYSize);
|
||||
SetTopAnchor(Content);
|
||||
|
||||
if (onInitialized != null) onInitialized();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the uppper and lower bounds for recycling cells.
|
||||
/// </summary>
|
||||
private void SetRecyclingBounds()
|
||||
{
|
||||
Viewport.GetWorldCorners(_corners);
|
||||
float threshHold = RecyclingThreshold * (_corners[2].y - _corners[0].y);
|
||||
_recyclableViewBounds.min = new Vector3(_corners[0].x, _corners[0].y - threshHold);
|
||||
_recyclableViewBounds.max = new Vector3(_corners[2].x, _corners[2].y + threshHold);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates cell Pool for recycling, Caches ICells
|
||||
/// </summary>
|
||||
private void CreateCellPool()
|
||||
{
|
||||
//Reseting Pool
|
||||
if (_cellPool != null)
|
||||
{
|
||||
_cellPool.ForEach((RectTransform item) => UnityEngine.Object.Destroy(item.gameObject));
|
||||
_cellPool.Clear();
|
||||
_cachedCells.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
_cachedCells = new List<ICell>();
|
||||
_cellPool = new List<RectTransform>();
|
||||
}
|
||||
|
||||
//Set the prototype cell active and set cell anchor as top
|
||||
PrototypeCell.gameObject.SetActive(true);
|
||||
if (IsGrid)
|
||||
{
|
||||
SetTopLeftAnchor(PrototypeCell);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetTopAnchor(PrototypeCell);
|
||||
}
|
||||
|
||||
//Reset
|
||||
_topMostCellColoumn = _bottomMostCellColoumn = 0;
|
||||
|
||||
//Temps
|
||||
float currentPoolCoverage = 0;
|
||||
int poolSize = 0;
|
||||
float posX = 0;
|
||||
float posY = 0;
|
||||
|
||||
//set new cell size according to its aspect ratio
|
||||
_cellWidth = Content.rect.width / _coloumns;
|
||||
_cellHeight = PrototypeCell.sizeDelta.y / PrototypeCell.sizeDelta.x * _cellWidth;
|
||||
|
||||
//Get the required pool coverage and mininum size for the Cell pool
|
||||
float requriedCoverage = MinPoolCoverage * Viewport.rect.height;
|
||||
int minPoolSize = Math.Min(MinPoolSize, DataSource.GetItemCount());
|
||||
|
||||
//create cells untill the Pool area is covered and pool size is the minimum required
|
||||
while ((poolSize < minPoolSize || currentPoolCoverage < requriedCoverage) && poolSize < DataSource.GetItemCount())
|
||||
{
|
||||
//Instantiate and add to Pool
|
||||
RectTransform item = (UnityEngine.Object.Instantiate(PrototypeCell.gameObject)).GetComponent<RectTransform>();
|
||||
item.name = "Cell";
|
||||
item.sizeDelta = new Vector2(_cellWidth, _cellHeight);
|
||||
_cellPool.Add(item);
|
||||
item.SetParent(Content, false);
|
||||
|
||||
if (IsGrid)
|
||||
{
|
||||
posX = _bottomMostCellColoumn * _cellWidth;
|
||||
item.anchoredPosition = new Vector2(posX, posY);
|
||||
if (++_bottomMostCellColoumn >= _coloumns)
|
||||
{
|
||||
_bottomMostCellColoumn = 0;
|
||||
posY -= _cellHeight;
|
||||
currentPoolCoverage += item.rect.height;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
item.anchoredPosition = new Vector2(0, posY);
|
||||
posY = item.anchoredPosition.y - item.rect.height;
|
||||
currentPoolCoverage += item.rect.height;
|
||||
}
|
||||
|
||||
//Setting data for Cell
|
||||
_cachedCells.Add(item.GetComponent<ICell>());
|
||||
DataSource.SetCell(_cachedCells[_cachedCells.Count - 1], poolSize);
|
||||
//Update the Pool size
|
||||
poolSize++;
|
||||
}
|
||||
|
||||
//TODO : you alrady have a _currentColoumn varaiable. Why this calculation?????
|
||||
if (IsGrid)
|
||||
{
|
||||
_bottomMostCellColoumn = (_bottomMostCellColoumn - 1 + _coloumns) % _coloumns;
|
||||
}
|
||||
|
||||
//Deactivate prototype cell if it is not a prefab(i.e it's present in scene)
|
||||
if (PrototypeCell.gameObject.scene.IsValid())
|
||||
{
|
||||
PrototypeCell.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region RECYCLING
|
||||
/// <summary>
|
||||
/// Recyling entry point
|
||||
/// </summary>
|
||||
/// <param name="direction">scroll direction </param>
|
||||
/// <returns></returns>
|
||||
public override Vector2 OnValueChangedListener(Vector2 direction)
|
||||
{
|
||||
if (_recycling || _cellPool == null || _cellPool.Count == 0) return zeroVector;
|
||||
|
||||
//Updating Recyclable view bounds since it can change with resolution changes.
|
||||
SetRecyclingBounds();
|
||||
|
||||
if (direction.y > 0 && _cellPool[bottomMostCellIndex].MaxY() > _recyclableViewBounds.min.y)
|
||||
{
|
||||
return RecycleTopToBottom();
|
||||
}
|
||||
else if (direction.y < 0 && _cellPool[topMostCellIndex].MinY() < _recyclableViewBounds.max.y)
|
||||
{
|
||||
return RecycleBottomToTop();
|
||||
}
|
||||
|
||||
return zeroVector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recycles cells from top to bottom in the List heirarchy
|
||||
/// </summary>
|
||||
private Vector2 RecycleTopToBottom()
|
||||
{
|
||||
_recycling = true;
|
||||
|
||||
int n = 0;
|
||||
float posY = IsGrid ? _cellPool[bottomMostCellIndex].anchoredPosition.y : 0;
|
||||
float posX = 0;
|
||||
|
||||
//to determine if content size needs to be updated
|
||||
int additionalRows = 0;
|
||||
//Recycle until cell at Top is avaiable and current item count smaller than datasource
|
||||
while (_cellPool[topMostCellIndex].MinY() > _recyclableViewBounds.max.y && currentItemCount < DataSource.GetItemCount())
|
||||
{
|
||||
if (IsGrid)
|
||||
{
|
||||
if (++_bottomMostCellColoumn >= _coloumns)
|
||||
{
|
||||
n++;
|
||||
_bottomMostCellColoumn = 0;
|
||||
posY = _cellPool[bottomMostCellIndex].anchoredPosition.y - _cellHeight;
|
||||
additionalRows++;
|
||||
}
|
||||
|
||||
//Move top cell to bottom
|
||||
posX = _bottomMostCellColoumn * _cellWidth;
|
||||
_cellPool[topMostCellIndex].anchoredPosition = new Vector2(posX, posY);
|
||||
|
||||
if (++_topMostCellColoumn >= _coloumns)
|
||||
{
|
||||
_topMostCellColoumn = 0;
|
||||
additionalRows--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Move top cell to bottom
|
||||
posY = _cellPool[bottomMostCellIndex].anchoredPosition.y - _cellPool[bottomMostCellIndex].sizeDelta.y;
|
||||
_cellPool[topMostCellIndex].anchoredPosition = new Vector2(_cellPool[topMostCellIndex].anchoredPosition.x, posY);
|
||||
}
|
||||
|
||||
//Cell for row at
|
||||
DataSource.SetCell(_cachedCells[topMostCellIndex], currentItemCount);
|
||||
|
||||
//set new indices
|
||||
bottomMostCellIndex = topMostCellIndex;
|
||||
topMostCellIndex = (topMostCellIndex + 1) % _cellPool.Count;
|
||||
|
||||
currentItemCount++;
|
||||
if (!IsGrid) n++;
|
||||
}
|
||||
|
||||
//Content size adjustment
|
||||
if (IsGrid)
|
||||
{
|
||||
Content.sizeDelta += additionalRows * Vector2.up * _cellHeight;
|
||||
//TODO : check if it is supposed to be done only when > 0
|
||||
if (additionalRows > 0)
|
||||
{
|
||||
n -= additionalRows;
|
||||
}
|
||||
}
|
||||
|
||||
//Content anchor position adjustment.
|
||||
_cellPool.ForEach((RectTransform cell) => cell.anchoredPosition += n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y);
|
||||
Content.anchoredPosition -= n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y;
|
||||
_recycling = false;
|
||||
return -new Vector2(0, n * _cellPool[topMostCellIndex].sizeDelta.y);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recycles cells from bottom to top in the List heirarchy
|
||||
/// </summary>
|
||||
private Vector2 RecycleBottomToTop()
|
||||
{
|
||||
_recycling = true;
|
||||
|
||||
int n = 0;
|
||||
float posY = IsGrid ? _cellPool[topMostCellIndex].anchoredPosition.y : 0;
|
||||
float posX = 0;
|
||||
|
||||
//to determine if content size needs to be updated
|
||||
int additionalRows = 0;
|
||||
//Recycle until cell at bottom is avaiable and current item count is greater than cellpool size
|
||||
while (_cellPool[bottomMostCellIndex].MaxY() < _recyclableViewBounds.min.y && currentItemCount > _cellPool.Count)
|
||||
{
|
||||
|
||||
if (IsGrid)
|
||||
{
|
||||
if (--_topMostCellColoumn < 0)
|
||||
{
|
||||
n++;
|
||||
_topMostCellColoumn = _coloumns - 1;
|
||||
posY = _cellPool[topMostCellIndex].anchoredPosition.y + _cellHeight;
|
||||
additionalRows++;
|
||||
}
|
||||
|
||||
//Move bottom cell to top
|
||||
posX = _topMostCellColoumn * _cellWidth;
|
||||
_cellPool[bottomMostCellIndex].anchoredPosition = new Vector2(posX, posY);
|
||||
|
||||
if (--_bottomMostCellColoumn < 0)
|
||||
{
|
||||
_bottomMostCellColoumn = _coloumns - 1;
|
||||
additionalRows--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Move bottom cell to top
|
||||
posY = _cellPool[topMostCellIndex].anchoredPosition.y + _cellPool[topMostCellIndex].sizeDelta.y;
|
||||
_cellPool[bottomMostCellIndex].anchoredPosition = new Vector2(_cellPool[bottomMostCellIndex].anchoredPosition.x, posY);
|
||||
n++;
|
||||
}
|
||||
|
||||
currentItemCount--;
|
||||
|
||||
//Cell for row at
|
||||
DataSource.SetCell(_cachedCells[bottomMostCellIndex], currentItemCount - _cellPool.Count);
|
||||
|
||||
//set new indices
|
||||
topMostCellIndex = bottomMostCellIndex;
|
||||
bottomMostCellIndex = (bottomMostCellIndex - 1 + _cellPool.Count) % _cellPool.Count;
|
||||
}
|
||||
|
||||
if (IsGrid)
|
||||
{
|
||||
Content.sizeDelta += additionalRows * Vector2.up * _cellHeight;
|
||||
//TODOL : check if it is supposed to be done only when > 0
|
||||
if (additionalRows > 0)
|
||||
{
|
||||
n -= additionalRows;
|
||||
}
|
||||
}
|
||||
|
||||
_cellPool.ForEach((RectTransform cell) => cell.anchoredPosition -= n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y);
|
||||
Content.anchoredPosition += n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y;
|
||||
_recycling = false;
|
||||
return new Vector2(0, n * _cellPool[topMostCellIndex].sizeDelta.y);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region HELPERS
|
||||
/// <summary>
|
||||
/// Anchoring cell and content rect transforms to top preset. Makes repositioning easy.
|
||||
/// </summary>
|
||||
/// <param name="rectTransform"></param>
|
||||
private void SetTopAnchor(RectTransform rectTransform)
|
||||
{
|
||||
//Saving to reapply after anchoring. Width and height changes if anchoring is change.
|
||||
float width = rectTransform.rect.width;
|
||||
float height = rectTransform.rect.height;
|
||||
|
||||
//Setting top anchor
|
||||
rectTransform.anchorMin = new Vector2(0.5f, 1);
|
||||
rectTransform.anchorMax = new Vector2(0.5f, 1);
|
||||
rectTransform.pivot = new Vector2(0.5f, 1);
|
||||
|
||||
//Reapply size
|
||||
rectTransform.sizeDelta = new Vector2(width, height);
|
||||
}
|
||||
|
||||
private void SetTopLeftAnchor(RectTransform rectTransform)
|
||||
{
|
||||
//Saving to reapply after anchoring. Width and height changes if anchoring is change.
|
||||
float width = rectTransform.rect.width;
|
||||
float height = rectTransform.rect.height;
|
||||
|
||||
//Setting top anchor
|
||||
rectTransform.anchorMin = new Vector2(0, 1);
|
||||
rectTransform.anchorMax = new Vector2(0, 1);
|
||||
rectTransform.pivot = new Vector2(0, 1);
|
||||
|
||||
//Reapply size
|
||||
rectTransform.sizeDelta = new Vector2(width, height);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TESTING
|
||||
public void OnDrawGizmos()
|
||||
{
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawLine(_recyclableViewBounds.min - new Vector3(2000, 0), _recyclableViewBounds.min + new Vector3(2000, 0));
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawLine(_recyclableViewBounds.max - new Vector3(2000, 0), _recyclableViewBounds.max + new Vector3(2000, 0));
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 360d60b04d7e6a64ab1104c09a3335b0
|
||||
timeCreated: 1595684786
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50cfd769583befc46a5b6c78d9d6f4b8
|
||||
folderAsset: yes
|
||||
timeCreated: 1598985967
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,38 @@
|
||||
//MIT License
|
||||
//Copyright (c) 2020 Mohammed Iqubal Hussain
|
||||
//Website : Polyandcode.com
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for Rect Transform
|
||||
/// </summary>
|
||||
public static class UIExtension
|
||||
{
|
||||
public static Vector3[] GetCorners(this RectTransform rectTransform)
|
||||
{
|
||||
Vector3[] corners = new Vector3[4];
|
||||
rectTransform.GetWorldCorners(corners);
|
||||
return corners;
|
||||
}
|
||||
public static float MaxY(this RectTransform rectTransform)
|
||||
{
|
||||
return rectTransform.GetCorners()[1].y;
|
||||
}
|
||||
|
||||
public static float MinY(this RectTransform rectTransform)
|
||||
{
|
||||
return rectTransform.GetCorners()[0].y;
|
||||
}
|
||||
|
||||
public static float MaxX(this RectTransform rectTransform)
|
||||
{
|
||||
return rectTransform.GetCorners()[2].x;
|
||||
}
|
||||
|
||||
public static float MinX(this RectTransform rectTransform)
|
||||
{
|
||||
return rectTransform.GetCorners()[0].x;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12216d152b4b943e88a3d40b029c3e3e
|
||||
timeCreated: 1580410439
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in new issue
Block a user