Editor stuff

This commit is contained in:
Starpelly
2022-01-05 19:11:33 -05:00
parent 775fd7e580
commit 576b0d8482
458 changed files with 37611 additions and 15 deletions

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1f5595dc255ff30488626bfc60c045a6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ff6158f1872894fd18d8b73aa8b3727e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,125 @@
/// <summary>
/// Credit - ryanslikesocool
/// Sourced from - https://github.com/ryanslikesocool/Unity-Card-UI
/// </summary>
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace UnityEngine.UI.Extensions
{
public class CardExpanding2D : MonoBehaviour
{
[SerializeField]
private float lerpSpeed = 8f;
[SerializeField]
private RectTransform buttonRect = null;
private Vector2 closeButtonMin = Vector2.zero;
private Vector2 closeButtonMax = Vector2.zero;
[SerializeField]
private Vector2 cardSize = Vector2.zero;
[SerializeField]
private Vector2 pageSize = Vector2.zero;
private Vector2 cardCenter = Vector2.zero;
private Vector2 pageCenter = Vector2.zero;
private Vector2 cardMin = Vector2.zero;
private Vector2 cardMax = Vector2.zero;
private Vector2 pageMin = Vector2.zero;
private Vector2 pageMax = Vector2.zero;
private RectTransform rectTrans;
///I wouldn't recommend changing animationActive's value here unless you want the card to start as a page.
private int animationActive = -1;
void Start()
{
rectTrans = GetComponent<RectTransform>();
///Setting up the button's starting color and page position.
buttonRect.GetComponent<Image>().color = new Color32(228, 0, 0, 0);
closeButtonMin = new Vector2(pageMin.x + pageSize.x - 64, pageMin.y + pageSize.y - 64);
closeButtonMax = new Vector2(pageMax.x - 16, pageMax.y - 16);
///Setting up the card and page offsets.
cardMin = new Vector2(cardCenter.x - cardSize.x * 0.5f, cardCenter.y - cardSize.y * 0.5f);
cardMax = new Vector2(cardCenter.x + cardSize.x * 0.5f, cardCenter.y + cardSize.y * 0.5f);
pageMin = new Vector2(pageCenter.x - pageSize.x * 0.5f, pageCenter.y - pageSize.y * 0.5f);
pageMax = new Vector2(pageCenter.x + pageSize.x * 0.5f, pageCenter.y + pageSize.y * 0.5f);
}
void Update()
{
///When animationActive == 1, the card is expanding into a page.
if (animationActive == 1)
{
rectTrans.offsetMin = Vector2.Lerp(rectTrans.offsetMin, pageMin, Time.deltaTime * lerpSpeed);
rectTrans.offsetMax = Vector2.Lerp(rectTrans.offsetMax, pageMax, Time.deltaTime * lerpSpeed);
if (rectTrans.offsetMin.x < pageMin.x * 0.995f && rectTrans.offsetMin.y < pageMin.y * 0.995f && rectTrans.offsetMax.x > pageMax.x * 0.995f && rectTrans.offsetMax.y > pageMax.y * 0.995f)
{
rectTrans.offsetMin = pageMin;
rectTrans.offsetMax = pageMax;
///Changes the button color so it's visible in the page view.
buttonRect.GetComponent<Image>().color = Color32.Lerp(buttonRect.GetComponent<Image>().color, new Color32(228, 0, 0, 191), Time.deltaTime * lerpSpeed);
if (Mathf.Abs(buttonRect.GetComponent<Image>().color.a - 191) < 2)
{
buttonRect.GetComponent<Image>().color = new Color32(228, 0, 0, 191);
animationActive = 0;
CardStack2D.canUseHorizontalAxis = true;
}
}
///When animationActive == -1, the page is shrinking into a card.
}
else if (animationActive == -1)
{
buttonRect.GetComponent<Image>().color = Color32.Lerp(buttonRect.GetComponent<Image>().color, new Color32(228, 0, 0, 0), Time.deltaTime * lerpSpeed * 1.25f);
rectTrans.offsetMin = Vector2.Lerp(rectTrans.offsetMin, cardMin, Time.deltaTime * lerpSpeed);
rectTrans.offsetMax = Vector2.Lerp(rectTrans.offsetMax, cardMax, Time.deltaTime * lerpSpeed);
if (rectTrans.offsetMin.x > cardMin.x * 1.005f && rectTrans.offsetMin.y > cardMin.y * 1.005f && rectTrans.offsetMax.x < cardMax.x * 1.005f && rectTrans.offsetMax.y < cardMax.y * 1.005f)
{
rectTrans.offsetMin = cardMin;
rectTrans.offsetMax = cardMax;
///Makes the button take up the whole card.
buttonRect.offsetMin = Vector2.zero;
buttonRect.offsetMax = Vector2.zero;
animationActive = 0;
CardStack2D.canUseHorizontalAxis = true;
}
}
}
public void ToggleCard()
{
CardStack2D.canUseHorizontalAxis = false;
if (animationActive != 1)
{
animationActive = 1;
cardCenter = transform.localPosition;
///Makes the button the right size in page view.
buttonRect.offsetMin = closeButtonMin;
buttonRect.offsetMax = closeButtonMax;
}
else if (animationActive != -1)
{
animationActive = -1;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9ab9da748840643a3bd4794f6f138979
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,82 @@
/// <summary>
/// Credit - ryanslikesocool
/// Sourced from - https://github.com/ryanslikesocool/Unity-Card-UI
/// </summary>
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEngine.UI.Extensions
{
[RequireComponent(typeof(Rigidbody))]
public class CardPopup2D : MonoBehaviour
{
[SerializeField]
private float rotationSpeed = 1f;
[SerializeField]
private float centeringSpeed = 4f;
[SerializeField]
private bool singleScene = false;
private Rigidbody rbody;
private bool isFalling;
private Vector3 cardFallRotation;
private bool fallToZero;
private float startZPos;
void Start()
{
rbody = GetComponent<Rigidbody>();
rbody.useGravity = false;
startZPos = transform.position.z;
}
void Update()
{
if (isFalling)
{
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(cardFallRotation), Time.deltaTime * rotationSpeed);
}
///This conditional makes the popup fall nicely into place.
if (fallToZero)
{
transform.position = Vector3.Lerp(transform.position, new Vector3(0, 0, startZPos), Time.deltaTime * centeringSpeed);
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(Vector3.zero), Time.deltaTime * centeringSpeed);
if (Vector3.Distance(transform.position, new Vector3(0, 0, startZPos)) < 0.0025f)
{
transform.position = new Vector3(0, 0, startZPos);
fallToZero = false;
}
}
///This is totally unnecessary.
if (transform.position.y < -4)
{
isFalling = false;
rbody.useGravity = false;
rbody.velocity = Vector3.zero;
transform.position = new Vector3(0, 8, startZPos);
if (singleScene)
{
CardEnter();
}
}
}
public void CardEnter()
{
fallToZero = true;
}
///A negative fallRotation will result in the card turning clockwise, while a positive fallRotation makes the card turn counterclockwise.
public void CardFallAway(float fallRotation)
{
rbody.useGravity = true;
isFalling = true;
cardFallRotation = new Vector3(0, 0, fallRotation);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a41308ab5f4aa489c9cf797b9152351a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,123 @@
/// <summary>
/// Credit - ryanslikesocool
/// Sourced from - https://github.com/ryanslikesocool/Unity-Card-UI
/// </summary>
using System.Collections;
namespace UnityEngine.UI.Extensions
{
public class CardStack2D : MonoBehaviour
{
[SerializeField]
private float cardMoveSpeed = 8f;
[SerializeField]
private float buttonCooldownTime = 0.125f;
[SerializeField]
private int cardZMultiplier = 32;
[SerializeField]
private bool useDefaultUsedXPos = true;
[SerializeField]
private int usedCardXPos = 1280;
[SerializeField]
private KeyCode leftButton = KeyCode.LeftArrow;
[SerializeField]
private KeyCode rightButton = KeyCode.RightArrow;
[SerializeField]
private Transform[] cards = null;
private int cardArrayOffset;
private Vector3[] cardPositions;
private int xPowerDifference;
///Static variables can be used across the scene if this script is in it.
///Thankfully it doesn't matter if another script attempts to use the variable and this script isn't in the scene.
public static bool canUseHorizontalAxis = true;
void Start()
{
///I've found that 9 is a good number for this.
///I wouldn't really recommend changing it, but go ahead if you want to.
xPowerDifference = 9 - cards.Length;
///This is optional, but makes it super easy to figure out the off screen position for cards.
///Unfortunately, it's only really useful if the cards are the same width.
if (useDefaultUsedXPos)
{
int cardWidth = (int)(cards[0].GetComponent<RectTransform>().rect.width);
usedCardXPos = (int)(Screen.width * 0.5f + cardWidth);
}
cardPositions = new Vector3[cards.Length * 2 - 1];
///This loop is for cards still in the stack.
for (int i = cards.Length; i > -1; i--)
{
if (i < cards.Length - 1)
{
cardPositions[i] = new Vector3(-Mathf.Pow(2, i + xPowerDifference) + cardPositions[i + 1].x, 0, cardZMultiplier * Mathf.Abs(i + 1 - cards.Length));
}
else
{
cardPositions[i] = Vector3.zero;
}
}
///This loop is for cards outside of the stack.
for (int i = cards.Length; i < cardPositions.Length; i++)
{
cardPositions[i] = new Vector3(usedCardXPos + 4 * (i - cards.Length), 0, -2 + -2 * (i - cards.Length));
}
}
void Update()
{
if (canUseHorizontalAxis)
{
///Controls for the cards.
if ((UIExtensionsInputManager.GetAxisRaw("Horizontal") < 0 || UIExtensionsInputManager.GetKey(leftButton)) && cardArrayOffset > 0)
{
cardArrayOffset--;
StartCoroutine(ButtonCooldown());
}
else if ((UIExtensionsInputManager.GetAxisRaw("Horizontal") > 0 || UIExtensionsInputManager.GetKey(rightButton)) && cardArrayOffset < cards.Length - 1)
{
cardArrayOffset++;
StartCoroutine(ButtonCooldown());
}
}
///This loop moves the cards. I know that none of my lerps are the "right way," but it looks much nicer.
for (int i = 0; i < cards.Length; i++)
{
cards[i].localPosition = Vector3.Lerp(cards[i].localPosition, cardPositions[i + cardArrayOffset], Time.deltaTime * cardMoveSpeed);
if (Mathf.Abs(cards[i].localPosition.x - cardPositions[i + cardArrayOffset].x) < 0.01f)
{
cards[i].localPosition = cardPositions[i + cardArrayOffset];
///This disables interaction with cards that are not on top of the stack.
if (cards[i].localPosition.x == 0)
{
cards[i].gameObject.GetComponent<CanvasGroup>().interactable = true;
}
else
{
cards[i].gameObject.GetComponent<CanvasGroup>().interactable = false;
}
}
}
}
///Stops the cards from scrolling super quickly if a button on the horizontal axis is held down.
IEnumerator ButtonCooldown()
{
canUseHorizontalAxis = false;
yield return new WaitForSeconds(buttonCooldownTime);
canUseHorizontalAxis = true;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3c0edb9a3f5da4e129739a8f92a115af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2e00d759ad79242bc80e41df440cc3df
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,222 @@
/// <summary>
/// Credit - ryanslikesocool
/// Sourced from - https://github.com/ryanslikesocool/Unity-Card-UI
/// </summary>
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace UnityEngine.UI.Extensions
{
[ExecuteInEditMode]
public class CardExpanding3D : MonoBehaviour
{
[SerializeField]
private float lerpSpeed = 12;
[SerializeField]
private float cornerSize = 64;
[Header("Parts")]
public RectTransform[] cardCorners;
public RectTransform[] cardEdges;
public RectTransform cardCenter;
[Header("Card Info")]
[Tooltip("Positions and sizes card to its current transform.")]
public bool cardAutoSize = true;
public Vector2 cardSize;
public Vector2 cardPosition;
[Range(1, 96)]
public int cardSuperness = 4;
[Header("Page Info")]
[Tooltip("Positions and sizes the page to the top third of the screen.")]
public bool pageAutoSize = true;
public Vector2 pageSize;
public Vector2 pagePosition;
[Range(1, 96)]
public int pageSuperness = 96;
///Just like with the 2D version of this script, I don't recommend touching this.
private int animationActive = 0;
private Vector2[] nextCornerPos = new Vector2[4];
private Vector2[] nextEdgePos = new Vector2[4];
private Vector2[] nextEdgeScale = new Vector2[4];
private Vector2 nextCenterScale;
private Vector2 nextPos;
private int nextSuperness;
private RectTransform rect;
private Vector2 nextMin;
private Vector2 nextMax;
void Start()
{
if (cardAutoSize)
{
cardSize = new Vector2(cardCorners[0].localScale.x * 2 + cardEdges[0].localScale.x, cardCorners[0].localScale.y * 2 + cardEdges[0].localScale.y);
cardPosition = cardCenter.localPosition;
}
if (pageAutoSize)
{
pageSize = new Vector2(Screen.width, Screen.height / 3);
pagePosition = new Vector2(0, Screen.height / 2 - pageSize.y / 2);
}
rect = GetComponent<RectTransform>();
}
void Update()
{
if (animationActive == 1 || animationActive == -1)
{
///Lerps the corners to new positions and supernesses.
for (int i = 0; i < cardCorners.Length; i++)
{
cardCorners[i].localPosition = Vector3.Lerp(cardCorners[i].localPosition, nextCornerPos[i], Time.deltaTime * lerpSpeed);
cardCorners[i].GetComponent<SuperellipsePoints>().superness = Mathf.Lerp(cardCorners[i].GetComponent<SuperellipsePoints>().superness, nextSuperness, Time.deltaTime * lerpSpeed);
///Forces everything to either the card layout or the page layout once the superness is similar enough.
if (Mathf.Abs(cardCorners[i].GetComponent<SuperellipsePoints>().superness - nextSuperness) <= 1)
{
cardCorners[i].localPosition = nextCornerPos[i];
cardEdges[i].localPosition = nextEdgePos[i];
cardEdges[i].localScale = new Vector3(nextEdgeScale[i].x, nextEdgeScale[i].y, 1);
transform.localPosition = nextPos;
cardCenter.localScale = new Vector3(nextCenterScale.x, nextCenterScale.y, 1);
cardCorners[i].GetComponent<SuperellipsePoints>().superness = nextSuperness;
rect.offsetMin = nextMin;
rect.offsetMax = nextMax;
}
}
///Lerps the edges to new positions and sizes.
for (int i = 0; i < cardEdges.Length; i++)
{
cardEdges[i].localPosition = Vector3.Lerp(cardEdges[i].localPosition, nextEdgePos[i], Time.deltaTime * lerpSpeed);
cardEdges[i].localScale = Vector3.Lerp(cardEdges[i].localScale, new Vector3(nextEdgeScale[i].x, nextEdgeScale[i].y, 1), Time.deltaTime * lerpSpeed);
}
///Lerps the center to new position and size.
transform.localPosition = Vector3.Lerp(transform.localPosition, nextPos, Time.deltaTime * lerpSpeed);
cardCenter.localScale = Vector3.Lerp(cardCenter.localScale, new Vector3(nextCenterScale.x, nextCenterScale.y, 1), Time.deltaTime * lerpSpeed);
///Lerps the RectTransform.
rect.offsetMin = Vector3.Lerp(rect.offsetMin, nextMin, Time.deltaTime * lerpSpeed);
rect.offsetMax = Vector3.Lerp(rect.offsetMax, nextMax, Time.deltaTime * lerpSpeed);
}
}
public void ToggleCard()
{
if (animationActive != 1 || animationActive == 0)
{
animationActive = 1;
///Gets new corner positions.
for (int i = 0; i < cardCorners.Length; i++)
{
float posX = pageSize.x / 2 * Mathf.Sign(cardCorners[i].localScale.x) - cardCorners[i].localScale.x;
float posY = pageSize.y / 2 * Mathf.Sign(cardCorners[i].localScale.y) - cardCorners[i].localScale.y;
nextCornerPos[i] = new Vector2(posX, posY);
}
///Same concept as the last loop.
for (int i = 0; i < cardEdges.Length; i++)
{
float posX = 0;
float posY = 0;
float scaleX = 0;
float scaleY = 0;
if (cardEdges[i].localPosition.x != 0)
{
posX = Mathf.Sign(cardEdges[i].localPosition.x) * ((pageSize.x / 2) - (cardEdges[i].localScale.x / 2));
posY = 0;
scaleX = cornerSize;
scaleY = pageSize.y - cornerSize * 2;
}
else if (cardEdges[i].localPosition.y != 0)
{
posX = 0;
posY = Mathf.Sign(cardEdges[i].localPosition.y) * ((pageSize.y / 2) - (cardEdges[i].localScale.y / 2));
scaleX = pageSize.x - cornerSize * 2;
scaleY = cornerSize;
}
nextEdgePos[i] = new Vector2(posX, posY);
nextEdgeScale[i] = new Vector2(scaleX, scaleY);
}
nextCenterScale = pageSize - new Vector2(cornerSize * 2, cornerSize * 2);
nextPos = pagePosition;
nextSuperness = pageSuperness;
nextMin = new Vector2(-pageSize.x / 2, -pageSize.y / 2) + nextPos;
nextMax = new Vector2(pageSize.x / 2, pageSize.y / 2) + nextPos;
}
else if (animationActive != -1)
{
animationActive = -1;
///Gets new corner positions.
for (int i = 0; i < cardCorners.Length; i++)
{
float posX = Mathf.Sign(cardCorners[i].localScale.x) * (cardSize.x / 2) - cardCorners[i].localScale.x;
float posY = Mathf.Sign(cardCorners[i].localScale.y) * (cardSize.y / 2) - cardCorners[i].localScale.y;
nextCornerPos[i] = new Vector2(posX, posY);
}
///Same concept as the last loop.
for (int i = 0; i < cardEdges.Length; i++)
{
float posX = 0;
float posY = 0;
float scaleX = 0;
float scaleY = 0;
if (cardEdges[i].localPosition.x != 0)
{
posX = Mathf.Sign(cardEdges[i].localPosition.x) * (cardSize.x / 2) - Mathf.Sign(cardEdges[i].localPosition.x) * (cardEdges[i].localScale.x / 2);
posY = 0;
scaleX = cornerSize;
scaleY = cardSize.y - cornerSize * 2;
}
else if (cardEdges[i].localPosition.y != 0)
{
posX = 0;
posY = Mathf.Sign(cardEdges[i].localPosition.y) * (cardSize.y / 2) - Mathf.Sign(cardEdges[i].localPosition.y) * (cardEdges[i].localScale.y / 2);
scaleX = cardSize.x - cornerSize * 2;
scaleY = cornerSize;
}
nextEdgePos[i] = new Vector2(posX, posY);
nextEdgeScale[i] = new Vector2(scaleX, scaleY);
}
nextCenterScale = cardSize - new Vector2(cornerSize * 2, cornerSize * 2);
nextPos = cardPosition;
nextSuperness = cardSuperness;
nextMin = new Vector2(-cardSize.x / 2, -cardSize.y / 2) + nextPos;
nextMax = new Vector2(cardSize.x / 2, cardSize.y / 2) + nextPos;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 113e4a3911e6c4f3e8427bd79605739c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 85c7285af214d46818136659e00053bd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,44 @@
/// <summary>
/// Credit - ryanslikesocool
/// Sourced from - https://github.com/ryanslikesocool/Unity-Card-UI
/// </summary>
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEngine.UI.Extensions
{
///Credit where credit is due
///https://wiki.unity3d.com/index.php?title=Triangulator
[ExecuteInEditMode]
public class MeshCreator : MonoBehaviour
{
public void CreateMesh(List<Vector2> points)
{
// Create Vector2 vertices
Vector2[] vertices2D = points.ToArray();
// Use the triangulator to get indices for creating triangles
Triangulator tr = new Triangulator(vertices2D);
int[] indices = tr.Triangulate();
// Create the Vector3 vertices
Vector3[] vertices = new Vector3[vertices2D.Length];
for (int i = 0; i < vertices.Length; i++)
{
vertices[i] = new Vector3(vertices2D[i].x, vertices2D[i].y, 0);
}
// Create the mesh
Mesh msh = new Mesh();
msh.vertices = vertices;
msh.triangles = indices;
msh.RecalculateNormals();
msh.RecalculateBounds();
// Set up game object with mesh;
GetComponent<MeshFilter>().mesh = msh;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c87d7e3a53e4c4bf9ae3657f0dff98b6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,91 @@
/// <summary>
/// Credit - ryanslikesocool
/// Sourced from - https://github.com/ryanslikesocool/Unity-Card-UI
/// </summary>
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEngine.UI.Extensions
{
///The formula for a basic superellipse is
///Mathf.Pow(Mathf.Abs(x / a), n) + Mathf.Pow(Mathf.Abs(y / b), n) = 1
[ExecuteInEditMode]
public class SuperellipsePoints : MonoBehaviour
{
public float xLimits = 1f;
public float yLimits = 1f;
[Range(1f, 96f)]
public float superness = 4f;
private float lastXLim;
private float lastYLim;
private float lastSuper;
[Space]
[Range(1, 32)]
public int levelOfDetail = 4;
private int lastLoD;
[Space]
public Material material;
private List<Vector2> pointList = new List<Vector2>();
void Start()
{
RecalculateSuperellipse();
GetComponent<MeshRenderer>().material = material;
lastXLim = xLimits;
lastYLim = yLimits;
lastSuper = superness;
lastLoD = levelOfDetail;
}
void Update()
{
if (lastXLim != xLimits || lastYLim != yLimits || lastSuper != superness || lastLoD != levelOfDetail)
{
RecalculateSuperellipse();
}
lastXLim = xLimits;
lastYLim = yLimits;
lastSuper = superness;
lastLoD = levelOfDetail;
}
void RecalculateSuperellipse()
{
pointList.Clear();
float realLoD = levelOfDetail * 4;
for (float i = 0; i < xLimits; i += 1 / realLoD)
{
float y = Superellipse(xLimits, yLimits, i, superness);
Vector2 tempVecTwo = new Vector2(i, y);
pointList.Add(tempVecTwo);
}
pointList.Add(new Vector2(xLimits, 0));
pointList.Add(Vector2.zero);
GetComponent<MeshCreator>().CreateMesh(pointList);
}
float Superellipse(float a, float b, float x, float n)
{
float alpha = Mathf.Pow((x / a), n);
float beta = 1 - alpha;
float y = Mathf.Pow(beta, 1 / n) * b;
return y;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: df6beaa1c87204b919c209b770d44bc9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,132 @@
/// <summary>
/// Credit - ryanslikesocool
/// Sourced from - https://github.com/ryanslikesocool/Unity-Card-UI
/// </summary>
using UnityEngine;
using System.Collections.Generic;
namespace UnityEngine.UI.Extensions
{
///Credit where credit is due
///https://wiki.unity3d.com/index.php?title=Triangulator
[ExecuteInEditMode]
public class Triangulator
{
private List<Vector2> m_points = new List<Vector2>();
public Triangulator(Vector2[] points)
{
m_points = new List<Vector2>(points);
}
public int[] Triangulate()
{
List<int> indices = new List<int>();
int n = m_points.Count;
if (n < 3)
return indices.ToArray();
int[] V = new int[n];
if (Area() > 0)
{
for (int v = 0; v < n; v++)
V[v] = v;
}
else
{
for (int v = 0; v < n; v++)
V[v] = (n - 1) - v;
}
int nv = n;
int count = 2 * nv;
for (int m = 0, v = nv - 1; nv > 2;)
{
if ((count--) <= 0)
return indices.ToArray();
int u = v;
if (nv <= u)
u = 0;
v = u + 1;
if (nv <= v)
v = 0;
int w = v + 1;
if (nv <= w)
w = 0;
if (Snip(u, v, w, nv, V))
{
int a, b, c, s, t;
a = V[u];
b = V[v];
c = V[w];
indices.Add(a);
indices.Add(b);
indices.Add(c);
m++;
for (s = v, t = v + 1; t < nv; s++, t++)
V[s] = V[t];
nv--;
count = 2 * nv;
}
}
indices.Reverse();
return indices.ToArray();
}
private float Area()
{
int n = m_points.Count;
float A = 0.0f;
for (int p = n - 1, q = 0; q < n; p = q++)
{
Vector2 pval = m_points[p];
Vector2 qval = m_points[q];
A += pval.x * qval.y - qval.x * pval.y;
}
return (A * 0.5f);
}
private bool Snip(int u, int v, int w, int n, int[] V)
{
int p;
Vector2 A = m_points[V[u]];
Vector2 B = m_points[V[v]];
Vector2 C = m_points[V[w]];
if (Mathf.Epsilon > (((B.x - A.x) * (C.y - A.y)) - ((B.y - A.y) * (C.x - A.x))))
return false;
for (p = 0; p < n; p++)
{
if ((p == u) || (p == v) || (p == w))
continue;
Vector2 P = m_points[V[p]];
if (InsideTriangle(A, B, C, P))
return false;
}
return true;
}
private bool InsideTriangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P)
{
float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
float cCROSSap, bCROSScp, aCROSSbp;
ax = C.x - B.x; ay = C.y - B.y;
bx = A.x - C.x; by = A.y - C.y;
cx = B.x - A.x; cy = B.y - A.y;
apx = P.x - A.x; apy = P.y - A.y;
bpx = P.x - B.x; bpy = P.y - B.y;
cpx = P.x - C.x; cpy = P.y - C.y;
aCROSSbp = ax * bpy - ay * bpx;
cCROSSap = cx * apy - cy * apx;
bCROSScp = bx * cpy - by * cpx;
return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ac33bc8ebf2744ad6ae91f80f8542546
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,676 @@
/// Credit Beka Westberg
/// Sourced from - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/pull-requests/28
/// Updated by SimonDarksideJ - Added some exception management and a SetNewItems to replace the content programmatically
using System.Collections;
using System.Collections.Generic;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using System;
namespace UnityEngine.UI.Extensions
{
[ExecuteInEditMode]
[RequireComponent(typeof(ScrollRect))]
[AddComponentMenu("UI/Extensions/ContentSnapScrollHorizontal")]
public class ContentScrollSnapHorizontal : MonoBehaviour, IBeginDragHandler, IEndDragHandler
{
[Serializable]
public class StartMovementEvent : UnityEvent { }
[Serializable]
public class CurrentItemChangeEvent : UnityEvent<int> { }
[Serializable]
public class FoundItemToSnapToEvent : UnityEvent<int> { }
[Serializable]
public class SnappedToItemEvent : UnityEvent<int> { }
public bool ignoreInactiveItems = true;
public MoveInfo startInfo = new MoveInfo(MoveInfo.IndexType.positionIndex, 0);
public GameObject prevButton;
public GameObject nextButton;
public GameObject pagination;
[Tooltip("The velocity below which the scroll rect will start to snap")]
public int snappingVelocityThreshold = 50;
[Header("Paging Info")]
[Tooltip("Should the pagination & buttons jump or lerp to the items")]
public bool jumpToItem = false;
[Tooltip("The time it will take for the pagination or buttons to move between items")]
public float lerpTime = .3f;
[Header("Events")]
[SerializeField]
[Tooltip("Event is triggered whenever the scroll rect starts to move, even when triggered programmatically")]
private StartMovementEvent m_StartMovementEvent = new StartMovementEvent();
public StartMovementEvent MovementStarted
{
get
{
return m_StartMovementEvent;
}
set
{
m_StartMovementEvent = value;
}
}
[SerializeField]
[Tooltip("Event is triggered whenever the closest item to the center of the scrollrect changes")]
private CurrentItemChangeEvent m_CurrentItemChangeEvent = new CurrentItemChangeEvent();
public CurrentItemChangeEvent CurrentItemChanged
{
get
{
return m_CurrentItemChangeEvent;
}
set
{
m_CurrentItemChangeEvent = value;
}
}
[SerializeField]
[Tooltip("Event is triggered when the ContentSnapScroll decides which item it is going to snap to. Returns the index of the closest position.")]
private FoundItemToSnapToEvent m_FoundItemToSnapToEvent = new FoundItemToSnapToEvent();
public FoundItemToSnapToEvent ItemFoundToSnap
{
get
{
return m_FoundItemToSnapToEvent;
}
set
{
m_FoundItemToSnapToEvent = value;
}
}
[SerializeField]
[Tooltip("Event is triggered when we finally settle on an element. Returns the index of the item's position.")]
private SnappedToItemEvent m_SnappedToItemEvent = new SnappedToItemEvent();
public SnappedToItemEvent ItemSnappedTo
{
get
{
return m_SnappedToItemEvent;
}
set
{
m_SnappedToItemEvent = value;
}
}
private ScrollRect scrollRect = null;
private RectTransform scrollRectTransform = null;
private RectTransform contentTransform = null;
private List<Vector3> contentPositions = new List<Vector3>();
private Vector3 lerpTarget = Vector3.zero;
private float totalScrollableWidth = 0;
private DrivenRectTransformTracker tracker ;
private float mLerpTime = 0;
private int _closestItem = 0;
private bool mSliding = false;
private bool mLerping = false;
private bool ContentIsHorizonalLayoutGroup
{
get
{
return contentTransform.GetComponent<HorizontalLayoutGroup>() != null;
}
}
#region Public Info
/// <summary>
/// Returns if the SnapScroll is moving
/// </summary>
public bool Moving
{
get
{
return Sliding || Lerping;
}
}
/// <summary>
/// Returns if the SnapScroll is moving because of a touch
/// </summary>
public bool Sliding
{
get
{
return mSliding;
}
}
/// <summary>
/// Returns if the SnapScroll is moving programmatically
/// </summary>
public bool Lerping
{
get
{
return mLerping;
}
}
/// <summary>
/// Returns the closest item's index
/// *Note this is zero based, and based on position not child order
/// </summary>
public int ClosestItemIndex
{
get
{
return contentPositions.IndexOf(FindClosestFrom(contentTransform.localPosition));
}
}
/// <summary>
/// Returns the lerpTarget's index
/// *Note this is zero-based, and based on position not child order
/// </summary>
public int LerpTargetIndex
{
get
{
return contentPositions.IndexOf(lerpTarget);
}
}
#endregion
#region Setup
private void Awake()
{
scrollRect = GetComponent<ScrollRect>();
scrollRectTransform = (RectTransform) scrollRect.transform;
contentTransform = scrollRect.content;
if (nextButton)
nextButton.GetComponent<Button>().onClick.AddListener(() => { NextItem(); });
if (prevButton)
prevButton.GetComponent<Button>().onClick.AddListener(() => { PreviousItem(); });
if (IsScrollRectAvailable)
{
SetupDrivenTransforms();
SetupSnapScroll();
scrollRect.horizontalNormalizedPosition = 0;
_closestItem = 0;
GoTo(startInfo);
}
}
public void SetNewItems(ref List<Transform> newItems)
{
if (scrollRect && contentTransform)
{
for (int i = scrollRect.content.childCount - 1; i >= 0; i--)
{
Transform child = contentTransform.GetChild(i);
child.SetParent(null);
GameObject.DestroyImmediate(child.gameObject);
}
foreach (Transform item in newItems)
{
GameObject newItem = item.gameObject;
if (newItem.IsPrefab())
{
newItem = Instantiate(item.gameObject, contentTransform);
}
else
{
newItem.transform.SetParent(contentTransform);
}
}
SetupDrivenTransforms();
SetupSnapScroll();
scrollRect.horizontalNormalizedPosition = 0;
_closestItem = 0;
GoTo(startInfo);
}
}
private bool IsScrollRectAvailable
{
get
{
if (scrollRect &&
contentTransform &&
contentTransform.childCount > 0)
{
return true;
}
return false;
}
}
private void OnDisable()
{
tracker.Clear();
}
private void SetupDrivenTransforms()
{
tracker = new DrivenRectTransformTracker();
tracker.Clear();
//So that we can calculate everything correctly
foreach (RectTransform child in contentTransform)
{
tracker.Add(this, child, DrivenTransformProperties.Anchors);
child.anchorMax = new Vector2(0, 1);
child.anchorMin = new Vector2(0, 1);
}
}
private void SetupSnapScroll()
{
if (ContentIsHorizonalLayoutGroup)
{
//because you can't get the anchored positions of UI elements
//when they are in a layout group (as far as I could tell)
SetupWithHorizontalLayoutGroup();
}
else
{
SetupWithCalculatedSpacing();
}
}
private void SetupWithHorizontalLayoutGroup()
{
HorizontalLayoutGroup horizLayoutGroup = contentTransform.GetComponent<HorizontalLayoutGroup>();
float childTotalWidths = 0;
int activeChildren = 0;
for (int i = 0; i < contentTransform.childCount; i++)
{
if (!ignoreInactiveItems || contentTransform.GetChild(i).gameObject.activeInHierarchy)
{
childTotalWidths += ((RectTransform)contentTransform.GetChild(i)).sizeDelta.x;
activeChildren++;
}
}
float spacingTotal = (activeChildren - 1) * horizLayoutGroup.spacing;
float totalWidth = childTotalWidths + spacingTotal + horizLayoutGroup.padding.left + horizLayoutGroup.padding.right;
contentTransform.sizeDelta = new Vector2(totalWidth, contentTransform.sizeDelta.y);
float scrollRectWidth = Mathf.Min(((RectTransform)contentTransform.GetChild(0)).sizeDelta.x, ((RectTransform)contentTransform.GetChild(contentTransform.childCount - 1)).sizeDelta.x);
/*---If the scroll view is set to stretch width this breaks stuff---*/
scrollRectTransform.sizeDelta = new Vector2(scrollRectWidth, scrollRectTransform.sizeDelta.y);
contentPositions = new List<Vector3>();
float widthOfScrollRect = scrollRectTransform.sizeDelta.x;
totalScrollableWidth = totalWidth - widthOfScrollRect;
float checkedChildrenTotalWidths = horizLayoutGroup.padding.left;
int activeChildrenBeforeSelf = 0;
for (int i = 0; i < contentTransform.childCount; i++)
{
if (!ignoreInactiveItems || contentTransform.GetChild(i).gameObject.activeInHierarchy)
{
float widthOfSelf = ((RectTransform)contentTransform.GetChild(i)).sizeDelta.x;
float offset = checkedChildrenTotalWidths + (horizLayoutGroup.spacing * activeChildrenBeforeSelf) + ((widthOfSelf - widthOfScrollRect) / 2);
scrollRect.horizontalNormalizedPosition = offset / totalScrollableWidth;
contentPositions.Add(contentTransform.localPosition);
checkedChildrenTotalWidths += widthOfSelf;
activeChildrenBeforeSelf++;
}
}
}
private void SetupWithCalculatedSpacing()
{
//we need them in order from left to right for pagination & buttons & our scrollRectWidth
List<RectTransform> childrenFromLeftToRight = new List<RectTransform>();
for (int i = 0; i < contentTransform.childCount; i++)
{
if (!ignoreInactiveItems || contentTransform.GetChild(i).gameObject.activeInHierarchy)
{
RectTransform childBeingSorted = ((RectTransform)contentTransform.GetChild(i));
int insertIndex = childrenFromLeftToRight.Count;
for (int j = 0; j < childrenFromLeftToRight.Count; j++)
{
if (DstFromTopLeftOfTransformToTopLeftOfParent(childBeingSorted).x < DstFromTopLeftOfTransformToTopLeftOfParent(childrenFromLeftToRight[j]).x)
{
insertIndex = j;
break;
}
}
childrenFromLeftToRight.Insert(insertIndex, childBeingSorted);
}
}
RectTransform childFurthestToTheRight = childrenFromLeftToRight[childrenFromLeftToRight.Count - 1];
float totalWidth = DstFromTopLeftOfTransformToTopLeftOfParent(childFurthestToTheRight).x + childFurthestToTheRight.sizeDelta.x;
contentTransform.sizeDelta = new Vector2(totalWidth, contentTransform.sizeDelta.y);
float scrollRectWidth = Mathf.Min(childrenFromLeftToRight[0].sizeDelta.x, childrenFromLeftToRight[childrenFromLeftToRight.Count - 1].sizeDelta.x);
// Note: sizeDelta will not be calculated properly if the scroll view is set to stretch width.
scrollRectTransform.sizeDelta = new Vector2(scrollRectWidth, scrollRectTransform.sizeDelta.y);
contentPositions = new List<Vector3>();
float widthOfScrollRect = scrollRectTransform.sizeDelta.x;
totalScrollableWidth = totalWidth - widthOfScrollRect;
for (int i = 0; i < childrenFromLeftToRight.Count; i++)
{
float offset = DstFromTopLeftOfTransformToTopLeftOfParent(childrenFromLeftToRight[i]).x + ((childrenFromLeftToRight[i].sizeDelta.x - widthOfScrollRect) / 2);
scrollRect.horizontalNormalizedPosition = offset / totalScrollableWidth;
contentPositions.Add(contentTransform.localPosition);
}
}
#endregion
#region Public Movement Functions
/// <summary>
/// Function for going to a specific screen.
/// *Note the index is based on a zero-starting index.
/// </summary>
/// <param name="info">All of the info about how you want it to move</param>
public void GoTo(MoveInfo info)
{
if (!Moving && info.index != ClosestItemIndex)
{
MovementStarted.Invoke();
}
if (info.indexType == MoveInfo.IndexType.childIndex)
{
mLerpTime = info.duration;
GoToChild(info.index, info.jump);
}
else if (info.indexType == MoveInfo.IndexType.positionIndex)
{
mLerpTime = info.duration;
GoToContentPos(info.index, info.jump);
}
}
private void GoToChild(int index, bool jump)
{
int clampedIndex = Mathf.Clamp(index, 0, contentPositions.Count - 1); //contentPositions amount == the amount of available children
if (ContentIsHorizonalLayoutGroup) //the contentPositions are in child order
{
lerpTarget = contentPositions[clampedIndex];
if (jump)
{
contentTransform.localPosition = lerpTarget;
}
else
{
StopMovement();
StartCoroutine("LerpToContent");
}
}
else //the contentPositions are in order from left -> right;
{
int availableChildIndex = 0; //an available child is one we can snap to
Vector3 previousContentTransformPos = contentTransform.localPosition;
for (int i = 0; i < contentTransform.childCount; i++)
{
if (!ignoreInactiveItems || contentTransform.GetChild(i).gameObject.activeInHierarchy)
{
if (availableChildIndex == clampedIndex)
{
RectTransform startChild = (RectTransform) contentTransform.GetChild(i);
float offset = DstFromTopLeftOfTransformToTopLeftOfParent(startChild).x + ((startChild.sizeDelta.x - scrollRectTransform.sizeDelta.x) / 2);
scrollRect.horizontalNormalizedPosition = offset / totalScrollableWidth;
lerpTarget = contentTransform.localPosition;
if (!jump)
{
contentTransform.localPosition = previousContentTransformPos;
StopMovement();
StartCoroutine("LerpToContent");
}
return;
}
availableChildIndex++;
}
}
}
}
private void GoToContentPos(int index, bool jump)
{
int clampedIndex = Mathf.Clamp(index, 0, contentPositions.Count - 1); //contentPositions amount == the amount of available children
//the content positions are all in order from left -> right
//which is what we want so there's no need to check
lerpTarget = contentPositions[clampedIndex];
if (jump)
{
contentTransform.localPosition = lerpTarget;
}
else
{
StopMovement();
StartCoroutine("LerpToContent");
}
}
/// <summary>
/// Function for going to the next item
/// *Note the next item is the item to the right of the current item, this is not based on child order
/// </summary>
public void NextItem()
{
int index;
if (Sliding)
{
index = ClosestItemIndex + 1;
}
else
{
index = LerpTargetIndex + 1;
}
MoveInfo info = new MoveInfo(MoveInfo.IndexType.positionIndex, index, jumpToItem, lerpTime);
GoTo(info);
}
/// <summary>
/// Function for going to the previous item
/// *Note the next item is the item to the left of the current item, this is not based on child order
/// </summary>
public void PreviousItem()
{
int index;
if (Sliding)
{
index = ClosestItemIndex - 1;
}
else
{
index = LerpTargetIndex - 1;
}
MoveInfo info = new MoveInfo(MoveInfo.IndexType.positionIndex, index, jumpToItem, lerpTime);
GoTo(info);
}
/// <summary>
/// Function for recalculating the size of the content & the snap positions, such as when you remove or add a child
/// </summary>
public void UpdateLayout()
{
SetupDrivenTransforms();
SetupSnapScroll();
}
/// <summary>
/// Recalculates the size of the content & snap positions, and moves to a new item afterwards.
/// </summary>
/// <param name="info">All of the info about how you want it to move</param>
public void UpdateLayoutAndMoveTo(MoveInfo info)
{
SetupDrivenTransforms();
SetupSnapScroll();
GoTo(info);
}
#endregion
#region Behind the Scenes Movement stuff
public void OnBeginDrag(PointerEventData ped)
{
if (contentPositions.Count < 2)
{
return;
}
StopMovement();
if (!Moving)
{
MovementStarted.Invoke();
}
}
public void OnEndDrag(PointerEventData ped)
{
if (contentPositions.Count <= 1)
{
return;
}
if (IsScrollRectAvailable)
{
StartCoroutine("SlideAndLerp");
}
}
private void Update()
{
if (IsScrollRectAvailable)
{
if (_closestItem != ClosestItemIndex)
{
CurrentItemChanged.Invoke(ClosestItemIndex);
ChangePaginationInfo(ClosestItemIndex);
_closestItem = ClosestItemIndex;
}
}
}
private IEnumerator SlideAndLerp()
{
mSliding = true;
while (Mathf.Abs(scrollRect.velocity.x) > snappingVelocityThreshold)
{
yield return null;
}
lerpTarget = FindClosestFrom(contentTransform.localPosition);
ItemFoundToSnap.Invoke(LerpTargetIndex);
while (Vector3.Distance(contentTransform.localPosition, lerpTarget) > 1)
{
contentTransform.localPosition = Vector3.Lerp(scrollRect.content.localPosition, lerpTarget, 7.5f * Time.deltaTime);
yield return null;
}
mSliding = false;
scrollRect.velocity = Vector2.zero;
contentTransform.localPosition = lerpTarget;
ItemSnappedTo.Invoke(LerpTargetIndex);
}
private IEnumerator LerpToContent()
{
ItemFoundToSnap.Invoke(LerpTargetIndex);
mLerping = true;
Vector3 originalContentPos = contentTransform.localPosition;
float elapsedTime = 0;
while (elapsedTime < mLerpTime)
{
elapsedTime += Time.deltaTime;
contentTransform.localPosition = Vector3.Lerp(originalContentPos, lerpTarget, (elapsedTime / mLerpTime));
yield return null;
}
ItemSnappedTo.Invoke(LerpTargetIndex);
mLerping = false;
}
#endregion
private void StopMovement()
{
scrollRect.velocity = Vector2.zero;
StopCoroutine("SlideAndLerp");
StopCoroutine("LerpToContent");
}
private void ChangePaginationInfo(int targetScreen)
{
if (pagination)
for (int i = 0; i < pagination.transform.childCount; i++)
{
pagination.transform.GetChild(i).GetComponent<Toggle>().isOn = (targetScreen == i);
}
}
private Vector2 DstFromTopLeftOfTransformToTopLeftOfParent(RectTransform rt)
{
//gets rid of any pivot weirdness
return new Vector2(rt.anchoredPosition.x - (rt.sizeDelta.x * rt.pivot.x), rt.anchoredPosition.y + (rt.sizeDelta.y * (1 - rt.pivot.y)));
}
private Vector3 FindClosestFrom(Vector3 start)
{
Vector3 closest = Vector3.zero;
float distance = Mathf.Infinity;
foreach (Vector3 position in contentPositions)
{
if (Vector3.Distance(start, position) < distance)
{
distance = Vector3.Distance(start, position);
closest = position;
}
}
return closest;
}
[System.Serializable]
public struct MoveInfo
{
public enum IndexType { childIndex, positionIndex }
[Tooltip("Child Index means the Index corresponds to the content item at that index in the hierarchy.\n" +
"Position Index means the Index corresponds to the content item in that snap position.\n" +
"A higher Position Index in a Horizontal Scroll Snap means it would be further to the right.")]
public IndexType indexType;
[Tooltip("Zero based")]
public int index;
[Tooltip("If this is true the snap scroll will jump to the index, otherwise it will lerp there.")]
public bool jump;
[Tooltip("If jump is false this is the time it will take to lerp to the index")]
public float duration;
/// <summary>
/// Creates a MoveInfo that jumps to the index
/// </summary>
/// <param name="_indexType">Whether you want to get the child at the index or the snap position at the index</param>
/// <param name="_index">Where you want it to jump</param>
public MoveInfo(IndexType _indexType, int _index)
{
indexType = _indexType;
index = _index;
jump = true;
duration = 0;
}
/// <summary>
/// Creates a MoveInfo
/// </summary>
/// <param name="_indexType">Whether you want to get the child at the index or the snap position at the index</param>
/// <param name="_index">Where you want it to jump</param>
/// <param name="_jump">Whether you want it to jump or lerp to the index</param>
/// <param name="_duration">How long it takes to lerp to the index</param>
public MoveInfo(IndexType _indexType, int _index, bool _jump, float _duration)
{
indexType = _indexType;
index = _index;
jump = _jump;
duration = _duration;
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6b5e39a5eb1058947a287d21183b661c
timeCreated: 1511208372
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,93 @@
/// <summary>
/// Curved Layout Group Created by Freezy - http://www.ElicitIce.com
/// Posted on Unity Forums http://forum.unity3d.com/threads/curved-layout.403985/
///
/// Free for any use and alteration, source code may not be sold without my permission.
/// If you make improvements on this script please share them with the community.
///
/// </summary>
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// TODO:
/// - add automatic child sizing, like in the HorizontalOrVerticalLayoutGroup.cs
/// - nicer anchor handling for initial child positions
/// </summary>
[AddComponentMenu("Layout/Extensions/Curved Layout")]
public class CurvedLayout : LayoutGroup {
public Vector3 CurveOffset;
// Yes these two could be combined into a single vector
// but this makes it easier to use?
[Tooltip("axis along which to place the items, Normalized before use")]
public Vector3 itemAxis;
[Tooltip("size of each item along the Normalized axis")]
public float itemSize;
// the slope can be moved by altering this setting, it could be constrained to the 0-1 range, but other values are useful for animations
public float centerpoint = 0.5f;
protected override void OnEnable() { base.OnEnable(); CalculateRadial(); }
public override void SetLayoutHorizontal() {
}
public override void SetLayoutVertical() {
}
public override void CalculateLayoutInputVertical() {
CalculateRadial();
}
public override void CalculateLayoutInputHorizontal() {
CalculateRadial();
}
#if UNITY_EDITOR
protected override void OnValidate() {
base.OnValidate();
CalculateRadial();
}
#endif
void CalculateRadial() {
m_Tracker.Clear();
if (transform.childCount == 0)
return;
//one liner for figuring out the desired pivot (should be moved into a utility function)
Vector2 pivot = new Vector2(((int)childAlignment % 3) * 0.5f, ((int)childAlignment / 3) * 0.5f);
//this seems to work ok-ish
Vector3 lastPos = new Vector3(
GetStartOffset(0, GetTotalPreferredSize(0)),
GetStartOffset(1, GetTotalPreferredSize(1)),
0f
);
// 0 = first, 1 = last child
float lerp = 0;
//no need to catch divide by 0 as childCount > 0
float step = 1f / transform.childCount;
//normalize and create a distance between items
var dist = itemAxis.normalized * itemSize;
for (int i = 0; i < transform.childCount; i++) {
RectTransform child = (RectTransform)transform.GetChild(i);
if (child != null) {
//stop the user from altering certain values in the editor
m_Tracker.Add(this, child,
DrivenTransformProperties.Anchors |
DrivenTransformProperties.AnchoredPosition |
DrivenTransformProperties.Pivot);
Vector3 vPos = lastPos + dist;
child.localPosition = lastPos = vPos + (lerp - centerpoint) * CurveOffset;
child.pivot = pivot;
//child anchors are not yet calculated, each child should set it's own size for now
child.anchorMin = child.anchorMax = new Vector2(0.5f, 0.5f);
lerp += step;
}
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8de815c5a2c592d4da033da3146168c5
timeCreated: 1463330156
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d1ddf7c4a2094c8429c834b8c71bb812
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6096cd9225d267f4da3b54947c75809c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,72 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// <see cref="FancyScrollView{TItemData, TContext}"/> のセルを実装するための抽象基底クラス.
/// <see cref="FancyCell{TItemData, TContext}.Context"/> が不要な場合は
/// 代わりに <see cref="FancyCell{TItemData}"/> を使用します.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <typeparam name="TContext"><see cref="Context"/> の型.</typeparam>
public abstract class FancyCell<TItemData, TContext> : MonoBehaviour where TContext : class, new()
{
/// <summary>
/// このセルで表示しているデータのインデックス.
/// </summary>
public int Index { get; set; } = -1;
/// <summary>
/// このセルの可視状態.
/// </summary>
public virtual bool IsVisible => gameObject.activeSelf;
/// <summary>
/// <see cref="FancyScrollView{TItemData, TContext}.Context"/> の参照.
/// セルとスクロールビュー間で同じインスタンスが共有されます. 情報の受け渡しや状態の保持に使用します.
/// </summary>
protected TContext Context { get; private set; }
/// <summary>
/// <see cref="Context"/> をセットします.
/// </summary>
/// <param name="context">コンテキスト.</param>
public virtual void SetContext(TContext context) => Context = context;
/// <summary>
/// 初期化を行います.
/// </summary>
public virtual void Initialize() { }
/// <summary>
/// このセルの可視状態を設定します.
/// </summary>
/// <param name="visible">可視状態なら <c>true</c>, 非可視状態なら <c>false</c>.</param>
public virtual void SetVisible(bool visible) => gameObject.SetActive(visible);
/// <summary>
/// アイテムデータに基づいてこのセルの表示内容を更新します.
/// </summary>
/// <param name="itemData">アイテムデータ.</param>
public abstract void UpdateContent(TItemData itemData);
/// <summary>
/// <c>0.0f</c> ~ <c>1.0f</c> の値に基づいてこのセルのスクロール位置を更新します.
/// </summary>
/// <param name="position">ビューポート範囲の正規化されたスクロール位置.</param>
public abstract void UpdatePosition(float position);
}
/// <summary>
/// <see cref="FancyScrollView{TItemData}"/> のセルを実装するための抽象基底クラス.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <seealso cref="FancyCell{TItemData, TContext}"/>
public abstract class FancyCell<TItemData> : FancyCell<TItemData, NullContext>
{
/// <inheritdoc/>
public sealed override void SetContext(NullContext context) => base.SetContext(context);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6610dee308450ec40899aeedfd85e972
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,216 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System.Collections.Generic;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// スクロールビューを実装するための抽象基底クラス.
/// 無限スクロールおよびスナップに対応しています.
/// <see cref="FancyScrollView{TItemData, TContext}.Context"/> が不要な場合は
/// 代わりに <see cref="FancyScrollView{TItemData}"/> を使用します.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <typeparam name="TContext"><see cref="Context"/> の型.</typeparam>
public abstract class FancyScrollView<TItemData, TContext> : MonoBehaviour where TContext : class, new()
{
/// <summary>
/// セル同士の間隔.
/// </summary>
[SerializeField, Range(1e-2f, 1f)] protected float cellInterval = 0.2f;
/// <summary>
/// スクロール位置の基準.
/// </summary>
/// <remarks>
/// たとえば、 <c>0.5</c> を指定してスクロール位置が <c>0</c> の場合, 中央に最初のセルが配置されます.
/// </remarks>
[SerializeField, Range(0f, 1f)] protected float scrollOffset = 0.5f;
/// <summary>
/// セルを循環して配置させるどうか.
/// </summary>
/// <remarks>
/// <c>true</c> にすると最後のセルの後に最初のセル, 最初のセルの前に最後のセルが並ぶようになります.
/// 無限スクロールを実装する場合は <c>true</c> を指定します.
/// </remarks>
[SerializeField] protected bool loop = false;
/// <summary>
/// セルの親要素となる <c>Transform</c>.
/// </summary>
[SerializeField] protected Transform cellContainer = default;
readonly IList<FancyCell<TItemData, TContext>> pool = new List<FancyCell<TItemData, TContext>>();
/// <summary>
/// 初期化済みかどうか.
/// </summary>
protected bool initialized;
/// <summary>
/// 現在のスクロール位置.
/// </summary>
protected float currentPosition;
/// <summary>
/// セルの Prefab.
/// </summary>
protected abstract GameObject CellPrefab { get; }
/// <summary>
/// アイテム一覧のデータ.
/// </summary>
protected IList<TItemData> ItemsSource { get; set; } = new List<TItemData>();
/// <summary>
/// <typeparamref name="TContext"/> のインスタンス.
/// セルとスクロールビュー間で同じインスタンスが共有されます. 情報の受け渡しや状態の保持に使用します.
/// </summary>
protected TContext Context { get; } = new TContext();
/// <summary>
/// 初期化を行います.
/// </summary>
/// <remarks>
/// 最初にセルが生成される直前に呼び出されます.
/// </remarks>
protected virtual void Initialize() { }
/// <summary>
/// 渡されたアイテム一覧に基づいて表示内容を更新します.
/// </summary>
/// <param name="itemsSource">アイテム一覧.</param>
protected virtual void UpdateContents(IList<TItemData> itemsSource)
{
ItemsSource = itemsSource;
Refresh();
}
/// <summary>
/// セルのレイアウトを強制的に更新します.
/// </summary>
protected virtual void Relayout() => UpdatePosition(currentPosition, false);
/// <summary>
/// セルのレイアウトと表示内容を強制的に更新します.
/// </summary>
protected virtual void Refresh() => UpdatePosition(currentPosition, true);
/// <summary>
/// スクロール位置を更新します.
/// </summary>
/// <param name="position">スクロール位置.</param>
protected virtual void UpdatePosition(float position) => UpdatePosition(position, false);
void UpdatePosition(float position, bool forceRefresh)
{
if (!initialized)
{
Initialize();
initialized = true;
}
currentPosition = position;
var p = position - scrollOffset / cellInterval;
var firstIndex = Mathf.CeilToInt(p);
var firstPosition = (Mathf.Ceil(p) - p) * cellInterval;
if (firstPosition + pool.Count * cellInterval < 1f)
{
ResizePool(firstPosition);
}
UpdateCells(firstPosition, firstIndex, forceRefresh);
}
void ResizePool(float firstPosition)
{
Debug.Assert(CellPrefab != null);
Debug.Assert(cellContainer != null);
var addCount = Mathf.CeilToInt((1f - firstPosition) / cellInterval) - pool.Count;
for (var i = 0; i < addCount; i++)
{
var cell = Instantiate(CellPrefab, cellContainer).GetComponent<FancyCell<TItemData, TContext>>();
if (cell == null)
{
throw new MissingComponentException(string.Format(
"FancyCell<{0}, {1}> component not found in {2}.",
typeof(TItemData).FullName, typeof(TContext).FullName, CellPrefab.name));
}
cell.SetContext(Context);
cell.Initialize();
cell.SetVisible(false);
pool.Add(cell);
}
}
void UpdateCells(float firstPosition, int firstIndex, bool forceRefresh)
{
for (var i = 0; i < pool.Count; i++)
{
var index = firstIndex + i;
var position = firstPosition + i * cellInterval;
var cell = pool[CircularIndex(index, pool.Count)];
if (loop)
{
index = CircularIndex(index, ItemsSource.Count);
}
if (index < 0 || index >= ItemsSource.Count || position > 1f)
{
cell.SetVisible(false);
continue;
}
if (forceRefresh || cell.Index != index || !cell.IsVisible)
{
cell.Index = index;
cell.SetVisible(true);
cell.UpdateContent(ItemsSource[index]);
}
cell.UpdatePosition(position);
}
}
int CircularIndex(int i, int size) => size < 1 ? 0 : i < 0 ? size - 1 + (i + 1) % size : i % size;
#if UNITY_EDITOR
bool cachedLoop;
float cachedCellInterval, cachedScrollOffset;
void LateUpdate()
{
if (cachedLoop != loop ||
cachedCellInterval != cellInterval ||
cachedScrollOffset != scrollOffset)
{
cachedLoop = loop;
cachedCellInterval = cellInterval;
cachedScrollOffset = scrollOffset;
UpdatePosition(currentPosition);
}
}
#endif
}
/// <summary>
/// <see cref="FancyScrollView{TItemData}"/> のコンテキストクラス.
/// </summary>
public sealed class NullContext { }
/// <summary>
/// スクロールビューを実装するための抽象基底クラス.
/// 無限スクロールおよびスナップに対応しています.
/// </summary>
/// <typeparam name="TItemData"></typeparam>
/// <seealso cref="FancyScrollView{TItemData, TContext}"/>
public abstract class FancyScrollView<TItemData> : FancyScrollView<TItemData, NullContext> { }
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0e38f4de7b1b5a5429228884014d12ec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c7768f2982b0142ab876d2bb4b597646
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,72 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System.Linq;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// 複数の <see cref="FancyCell{TItemData, TContext}"/> を持つセルグループ実装するための抽象基底クラス.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <typeparam name="TContext"><see cref="FancyCell{TItemData, TContext}.Context"/> の型.</typeparam>
public abstract class FancyCellGroup<TItemData, TContext> : FancyCell<TItemData[], TContext>
where TContext : class, IFancyCellGroupContext, new()
{
/// <summary>
/// このグループで表示するセルの配列.
/// </summary>
protected virtual FancyCell<TItemData, TContext>[] Cells { get; private set; }
/// <summary>
/// このグループで表示するセルの配列をインスタンス化します.
/// </summary>
/// <returns>このグループで表示するセルの配列.</returns>
protected virtual FancyCell<TItemData, TContext>[] InstantiateCells()
{
return Enumerable.Range(0, Context.GetGroupCount())
.Select(_ => Instantiate(Context.CellTemplate, transform))
.Select(x => x.GetComponent<FancyCell<TItemData, TContext>>())
.ToArray();
}
/// <inheritdoc/>
public override void Initialize()
{
Cells = InstantiateCells();
Debug.Assert(Cells.Length == Context.GetGroupCount());
for (var i = 0; i < Cells.Length; i++)
{
Cells[i].SetContext(Context);
Cells[i].Initialize();
}
}
/// <inheritdoc/>
public override void UpdateContent(TItemData[] contents)
{
var firstCellIndex = Index * Context.GetGroupCount();
for (var i = 0; i < Cells.Length; i++)
{
Cells[i].Index = i + firstCellIndex;
Cells[i].SetVisible(i < contents.Length);
if (Cells[i].IsVisible)
{
Cells[i].UpdateContent(contents[i]);
}
}
}
/// <inheritdoc/>
public override void UpdatePosition(float position)
{
for (var i = 0; i < Cells.Length; i++)
{
Cells[i].UpdatePosition(position);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5d97e25c7748b8d44acd2298e509c8f1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,181 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.UI.Extensions.EasingCore;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// グリッドレイアウトのスクロールビューを実装するための抽象基底クラス.
/// 無限スクロールおよびスナップには対応していません.
/// <see cref="FancyScrollView{TItemData, TContext}.Context"/> が不要な場合は
/// 代わりに <see cref="FancyGridView{TItemData}"/> を使用します.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <typeparam name="TContext"><see cref="FancyScrollView{TItemData, TContext}.Context"/> の型.</typeparam>
public abstract class FancyGridView<TItemData, TContext> : FancyScrollRect<TItemData[], TContext>
where TContext : class, IFancyGridViewContext, new()
{
/// <summary>
/// デフォルトのセルグループクラス.
/// </summary>
protected abstract class DefaultCellGroup : FancyCellGroup<TItemData, TContext> { }
/// <summary>
/// 最初にセルを配置する軸方向のセル同士の余白.
/// </summary>
[SerializeField] protected float startAxisSpacing = 0f;
/// <summary>
/// 最初にセルを配置する軸方向のセル数.
/// </summary>
[SerializeField] protected int startAxisCellCount = 4;
/// <summary>
/// セルのサイズ.
/// </summary>
[SerializeField] protected Vector2 cellSize = new Vector2(100f, 100f);
/// <summary>
/// セルのグループ Prefab.
/// </summary>
/// <remarks>
/// <see cref="FancyGridView{TItemData, TContext}"/> では,
/// <see cref="FancyScrollView{TItemData, TContext}.CellPrefab"/> を最初にセルを配置する軸方向のセルコンテナとして使用します.
/// </remarks>
protected sealed override GameObject CellPrefab => cellGroupTemplate;
/// <inheritdoc/>
protected override float CellSize => Scroller.ScrollDirection == ScrollDirection.Horizontal
? cellSize.x
: cellSize.y;
/// <summary>
/// アイテムの総数.
/// </summary>
public int DataCount { get; private set; }
GameObject cellGroupTemplate;
/// <inheritdoc/>
protected override void Initialize()
{
base.Initialize();
Debug.Assert(startAxisCellCount > 0);
Context.ScrollDirection = Scroller.ScrollDirection;
Context.GetGroupCount = () => startAxisCellCount;
Context.GetStartAxisSpacing = () => startAxisSpacing;
Context.GetCellSize = () => Scroller.ScrollDirection == ScrollDirection.Horizontal
? cellSize.y
: cellSize.x;
SetupCellTemplate();
}
/// <summary>
/// 最初にセルが生成される直前に呼び出されます.
/// <see cref="Setup{TGroup}(FancyCell{TItemData, TContext})"/> メソッドを使用してセルテンプレートのセットアップを行ってください.
/// </summary>
/// <example>
/// <code><![CDATA[
/// using UnityEngine;
/// using FancyScrollView;
///
/// public class MyGridView : FancyGridView<ItemData, Context>
/// {
/// class CellGroup : DefaultCellGroup { }
///
/// [SerializeField] Cell cellPrefab = default;
///
/// protected override void SetupCellTemplate() => Setup<CellGroup>(cellPrefab);
/// }
/// ]]></code>
/// </example>
protected abstract void SetupCellTemplate();
/// <summary>
/// セルテンプレートのセットアップを行います.
/// </summary>
/// <param name="cellTemplate">セルのテンプレート.</param>
/// <typeparam name="TGroup">セルグループの型.</typeparam>
protected virtual void Setup<TGroup>(FancyCell<TItemData, TContext> cellTemplate)
where TGroup : FancyCell<TItemData[], TContext>
{
Context.CellTemplate = cellTemplate.gameObject;
cellGroupTemplate = new GameObject("Group").AddComponent<TGroup>().gameObject;
cellGroupTemplate.transform.SetParent(cellContainer, false);
cellGroupTemplate.SetActive(false);
}
/// <summary>
/// 渡されたアイテム一覧に基づいて表示内容を更新します.
/// </summary>
/// <param name="items">アイテム一覧.</param>
public virtual void UpdateContents(IList<TItemData> items)
{
DataCount = items.Count;
var itemGroups = items
.Select((item, index) => (item, index))
.GroupBy(
x => x.index / startAxisCellCount,
x => x.item)
.Select(group => group.ToArray())
.ToArray();
UpdateContents(itemGroups);
}
/// <summary>
/// 指定したアイテムの位置までジャンプします.
/// </summary>
/// <param name="itemIndex">アイテムのインデックス.</param>
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
protected override void JumpTo(int itemIndex, float alignment = 0.5f)
{
var groupIndex = itemIndex / startAxisCellCount;
base.JumpTo(groupIndex, alignment);
}
/// <summary>
/// 指定したアイテムの位置まで移動します.
/// </summary>
/// <param name="itemIndex">アイテムのインデックス.</param>
/// <param name="duration">移動にかける秒数.</param>
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
protected override void ScrollTo(int itemIndex, float duration, float alignment = 0.5f, Action onComplete = null)
{
var groupIndex = itemIndex / startAxisCellCount;
base.ScrollTo(groupIndex, duration, alignment, onComplete);
}
/// <summary>
/// 指定したアイテムの位置まで移動します.
/// </summary>
/// <param name="itemIndex">アイテムのインデックス.</param>
/// <param name="duration">移動にかける秒数.</param>
/// <param name="easing">移動に使用するイージング.</param>
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
protected override void ScrollTo(int itemIndex, float duration, Ease easing, float alignment = 0.5f, Action onComplete = null)
{
var groupIndex = itemIndex / startAxisCellCount;
base.ScrollTo(groupIndex, duration, easing, alignment, onComplete);
}
}
/// <summary>
/// グリッドレイアウトのスクロールビューを実装するための抽象基底クラス.
/// 無限スクロールおよびスナップには対応していません.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <seealso cref="FancyGridView{TItemData, TContext}"/>
public abstract class FancyGridView<TItemData> : FancyGridView<TItemData, FancyGridViewContext> { }
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e806c8d18b1ff45bb87e9a5b87ec85e3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,42 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// <see cref="FancyGridView{TItemData, TContext}"/> のセルを実装するための抽象基底クラス.
/// <see cref="FancyCell{TItemData, TContext}.Context"/> が不要な場合は
/// 代わりに <see cref="FancyGridViewCell{TItemData}"/> を使用します.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <typeparam name="TContext"><see cref="FancyCell{TItemData, TContext}.Context"/> の型.</typeparam>
public abstract class FancyGridViewCell<TItemData, TContext> : FancyScrollRectCell<TItemData, TContext>
where TContext : class, IFancyGridViewContext, new()
{
/// <inheritdoc/>
protected override void UpdatePosition(float normalizedPosition, float localPosition)
{
var cellSize = Context.GetCellSize();
var spacing = Context.GetStartAxisSpacing();
var groupCount = Context.GetGroupCount();
var indexInGroup = Index % groupCount;
var positionInGroup = (cellSize + spacing) * (indexInGroup - (groupCount - 1) * 0.5f);
transform.localPosition = Context.ScrollDirection == ScrollDirection.Horizontal
? new Vector2(-localPosition, -positionInGroup)
: new Vector2(positionInGroup, localPosition);
}
}
/// <summary>
/// <see cref="FancyGridView{TItemData}"/> のセルを実装するための抽象基底クラス.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <seealso cref="FancyGridViewCell{TItemData, TContext}"/>
public abstract class FancyGridViewCell<TItemData> : FancyGridViewCell<TItemData, FancyGridViewContext>
{
/// <inheritdoc/>
public sealed override void SetContext(FancyGridViewContext context) => base.SetContext(context);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ab8a59bbf5118824ab084e32342ad86b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,20 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// <see cref="FancyGridView{TItemData, TContext}"/> のコンテキスト基底クラス.
/// </summary>
public class FancyGridViewContext : IFancyGridViewContext
{
ScrollDirection IFancyScrollRectContext.ScrollDirection { get; set; }
Func<(float ScrollSize, float ReuseMargin)> IFancyScrollRectContext.CalculateScrollSize { get; set; }
GameObject IFancyCellGroupContext.CellTemplate { get; set; }
Func<int> IFancyCellGroupContext.GetGroupCount { get; set; }
Func<float> IFancyGridViewContext.GetStartAxisSpacing { get; set; }
Func<float> IFancyGridViewContext.GetCellSize { get; set; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 954a17398bfb54ee7baac3d7ab7e822c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,16 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// <see cref="FancyCellGroup{TItemData, TContext}"/> のコンテキストインターフェース.
/// </summary>
public interface IFancyCellGroupContext
{
GameObject CellTemplate { get; set; }
Func<int> GetGroupCount { get; set; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1dc086f250206754aa8449e252d50388
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,16 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// <see cref="FancyGridView{TItemData, TContext}"/> のコンテキストインターフェース.
/// </summary>
public interface IFancyGridViewContext : IFancyScrollRectContext, IFancyCellGroupContext
{
Func<float> GetStartAxisSpacing { get; set; }
Func<float> GetCellSize { get; set ; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3cf2d53d9c81945c28f7c558a7c40ba3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e2c5beedd885c4490a86bb4973f965bf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,303 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System;
using System.Collections.Generic;
using UnityEngine.UI.Extensions.EasingCore;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// ScrollRect スタイルのスクロールビューを実装するための抽象基底クラス.
/// 無限スクロールおよびスナップには対応していません.
/// <see cref="FancyScrollView{TItemData, TContext}.Context"/> が不要な場合は
/// 代わりに <see cref="FancyScrollRect{TItemData}"/> を使用します.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <typeparam name="TContext"><see cref="FancyScrollView{TItemData, TContext}.Context"/> の型.</typeparam>
[RequireComponent(typeof(Scroller))]
public abstract class FancyScrollRect<TItemData, TContext> : FancyScrollView<TItemData, TContext>
where TContext : class, IFancyScrollRectContext, new()
{
/// <summary>
/// スクロール中にセルが再利用されるまでの余白のセル数.
/// </summary>
/// <remarks>
/// <c>0</c> を指定するとセルが完全に隠れた直後に再利用されます.
/// <c>1</c> 以上を指定すると, そのセル数だけ余分にスクロールしてから再利用されます.
/// </remarks>
[SerializeField] protected float reuseCellMarginCount = 0f;
/// <summary>
/// コンテンツ先頭の余白.
/// </summary>
[SerializeField] protected float paddingHead = 0f;
/// <summary>
/// コンテンツ末尾の余白.
/// </summary>
[SerializeField] protected float paddingTail = 0f;
/// <summary>
/// スクロール軸方向のセル同士の余白.
/// </summary>
[SerializeField] protected float spacing = 0f;
/// <summary>
/// セルのサイズ.
/// </summary>
protected abstract float CellSize { get; }
/// <summary>
/// スクロール可能かどうか.
/// </summary>
/// <remarks>
/// アイテム数が十分少なくビューポート内に全てのセルが収まっている場合は <c>false</c>, それ以外は <c>true</c> になります.
/// </remarks>
protected virtual bool Scrollable => MaxScrollPosition > 0f;
Scroller cachedScroller;
/// <summary>
/// スクロール位置を制御する <see cref="FancyScrollView.Scroller"/> のインスタンス.
/// </summary>
/// <remarks>
/// <see cref="Scroller"/> のスクロール位置を変更する際は必ず <see cref="ToScrollerPosition(float)"/> を使用して変換した位置を使用してください.
/// </remarks>
protected Scroller Scroller => cachedScroller ?? (cachedScroller = GetComponent<Scroller>());
float ScrollLength => 1f / Mathf.Max(cellInterval, 1e-2f) - 1f;
float ViewportLength => ScrollLength - reuseCellMarginCount * 2f;
float PaddingHeadLength => (paddingHead - spacing * 0.5f) / (CellSize + spacing);
float MaxScrollPosition => ItemsSource.Count
- ScrollLength
+ reuseCellMarginCount * 2f
+ (paddingHead + paddingTail - spacing) / (CellSize + spacing);
/// <inheritdoc/>
protected override void Initialize()
{
base.Initialize();
Context.ScrollDirection = Scroller.ScrollDirection;
Context.CalculateScrollSize = () =>
{
var interval = CellSize + spacing;
var reuseMargin = interval * reuseCellMarginCount;
var scrollSize = Scroller.ViewportSize + interval + reuseMargin * 2f;
return (scrollSize, reuseMargin);
};
AdjustCellIntervalAndScrollOffset();
Scroller.OnValueChanged(OnScrollerValueChanged);
}
/// <summary>
/// <see cref="Scroller"/> のスクロール位置が変更された際の処理.
/// </summary>
/// <param name="p"><see cref="Scroller"/> のスクロール位置.</param>
void OnScrollerValueChanged(float p)
{
base.UpdatePosition(Scrollable ? ToFancyScrollViewPosition(p) : 0f);
if (Scroller.Scrollbar)
{
if (p > ItemsSource.Count - 1)
{
ShrinkScrollbar(p - (ItemsSource.Count - 1));
}
else if (p < 0f)
{
ShrinkScrollbar(-p);
}
}
}
/// <summary>
/// スクロール範囲を超えてスクロールされた量に基づいて, スクロールバーのサイズを縮小します.
/// </summary>
/// <param name="offset">スクロール範囲を超えてスクロールされた量.</param>
void ShrinkScrollbar(float offset)
{
var scale = 1f - ToFancyScrollViewPosition(offset) / (ViewportLength - PaddingHeadLength);
UpdateScrollbarSize((ViewportLength - PaddingHeadLength) * scale);
}
/// <inheritdoc/>
protected override void Refresh()
{
AdjustCellIntervalAndScrollOffset();
RefreshScroller();
base.Refresh();
}
/// <inheritdoc/>
protected override void Relayout()
{
AdjustCellIntervalAndScrollOffset();
RefreshScroller();
base.Relayout();
}
/// <summary>
/// <see cref="Scroller"/> の各種状態を更新します.
/// </summary>
protected void RefreshScroller()
{
Scroller.Draggable = Scrollable;
Scroller.ScrollSensitivity = ToScrollerPosition(ViewportLength - PaddingHeadLength);
Scroller.Position = ToScrollerPosition(currentPosition);
if (Scroller.Scrollbar)
{
Scroller.Scrollbar.gameObject.SetActive(Scrollable);
UpdateScrollbarSize(ViewportLength);
}
}
/// <inheritdoc/>
protected override void UpdateContents(IList<TItemData> items)
{
Debug.Assert(Context.CalculateScrollSize != null);
AdjustCellIntervalAndScrollOffset();
base.UpdateContents(items);
Scroller.SetTotalCount(items.Count);
RefreshScroller();
}
/// <summary>
/// スクロール位置を更新します.
/// </summary>
/// <param name="position">スクロール位置.</param>
protected new void UpdatePosition(float position)
{
Scroller.Position = ToScrollerPosition(position, 0.5f);
}
/// <summary>
/// 指定したアイテムの位置までジャンプします.
/// </summary>
/// <param name="itemIndex">アイテムのインデックス.</param>
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
protected virtual void JumpTo(int itemIndex, float alignment = 0.5f)
{
Scroller.Position = ToScrollerPosition(itemIndex, alignment);
}
/// <summary>
/// 指定したアイテムの位置まで移動します.
/// </summary>
/// <param name="index">アイテムのインデックス.</param>
/// <param name="duration">移動にかける秒数.</param>
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
protected virtual void ScrollTo(int index, float duration, float alignment = 0.5f, Action onComplete = null)
{
Scroller.ScrollTo(ToScrollerPosition(index, alignment), duration, onComplete);
}
/// <summary>
/// 指定したアイテムの位置まで移動します.
/// </summary>
/// <param name="index">アイテムのインデックス.</param>
/// <param name="duration">移動にかける秒数.</param>
/// <param name="easing">移動に使用するイージング.</param>
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
protected virtual void ScrollTo(int index, float duration, Ease easing, float alignment = 0.5f, Action onComplete = null)
{
Scroller.ScrollTo(ToScrollerPosition(index, alignment), duration, easing, onComplete);
}
/// <summary>
/// ビューポートとコンテンツの長さに基づいてスクロールバーのサイズを更新します.
/// </summary>
/// <param name="viewportLength">ビューポートのサイズ.</param>
protected void UpdateScrollbarSize(float viewportLength)
{
var contentLength = Mathf.Max(ItemsSource.Count + (paddingHead + paddingTail - spacing) / (CellSize + spacing), 1);
Scroller.Scrollbar.size = Scrollable ? Mathf.Clamp01(viewportLength / contentLength) : 1f;
}
/// <summary>
/// <see cref="Scroller"/> が扱うスクロール位置を <see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置に変換します.
/// </summary>
/// <param name="position"><see cref="Scroller"/> が扱うスクロール位置.</param>
/// <returns><see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置.</returns>
protected float ToFancyScrollViewPosition(float position)
{
return position / Mathf.Max(ItemsSource.Count - 1, 1) * MaxScrollPosition - PaddingHeadLength;
}
/// <summary>
/// <see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置を <see cref="Scroller"/> が扱うスクロール位置に変換します.
/// </summary>
/// <param name="position"><see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置.</param>
/// <returns><see cref="Scroller"/> が扱うスクロール位置.</returns>
protected float ToScrollerPosition(float position)
{
return (position + PaddingHeadLength) / MaxScrollPosition * Mathf.Max(ItemsSource.Count - 1, 1);
}
/// <summary>
/// <see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置を <see cref="Scroller"/> が扱うスクロール位置に変換します.
/// </summary>
/// <param name="position"><see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置.</param>
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
/// <returns><see cref="Scroller"/> が扱うスクロール位置.</returns>
protected float ToScrollerPosition(float position, float alignment = 0.5f)
{
var offset = alignment * (ScrollLength - (1f + reuseCellMarginCount * 2f))
+ (1f - alignment - 0.5f) * spacing / (CellSize + spacing);
return ToScrollerPosition(Mathf.Clamp(position - offset, 0f, MaxScrollPosition));
}
/// <summary>
/// 指定された設定を実現するための
/// <see cref="FancyScrollView{TItemData,TContext}.cellInterval"/> と
/// <see cref="FancyScrollView{TItemData,TContext}.scrollOffset"/> を計算して適用します.
/// </summary>
protected void AdjustCellIntervalAndScrollOffset()
{
var totalSize = Scroller.ViewportSize + (CellSize + spacing) * (1f + reuseCellMarginCount * 2f);
cellInterval = (CellSize + spacing) / totalSize;
scrollOffset = cellInterval * (1f + reuseCellMarginCount);
}
protected virtual void OnValidate()
{
AdjustCellIntervalAndScrollOffset();
if (loop)
{
loop = false;
Debug.LogError("Loop is currently not supported in FancyScrollRect.");
}
if (Scroller.SnapEnabled)
{
Scroller.SnapEnabled = false;
Debug.LogError("Snap is currently not supported in FancyScrollRect.");
}
if (Scroller.MovementType == MovementType.Unrestricted)
{
Scroller.MovementType = MovementType.Elastic;
Debug.LogError("MovementType.Unrestricted is currently not supported in FancyScrollRect.");
}
}
}
/// <summary>
/// ScrollRect スタイルのスクロールビューを実装するための抽象基底クラス.
/// 無限スクロールおよびスナップには対応していません.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <seealso cref="FancyScrollRect{TItemData, TContext}"/>
public abstract class FancyScrollRect<TItemData> : FancyScrollRect<TItemData, FancyScrollRectContext> { }
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 66c8eb84fdbde4a4a8273b98227a282d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,56 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// <see cref="FancyScrollRect{TItemData, TContext}"/> のセルを実装するための抽象基底クラス.
/// <see cref="FancyCell{TItemData, TContext}.Context"/> が不要な場合は
/// 代わりに <see cref="FancyScrollRectCell{TItemData}"/> を使用します.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <typeparam name="TContext"><see cref="FancyCell{TItemData, TContext}.Context"/> の型.</typeparam>
public abstract class FancyScrollRectCell<TItemData, TContext> : FancyCell<TItemData, TContext>
where TContext : class, IFancyScrollRectContext, new()
{
/// <inheritdoc/>
public override void UpdatePosition(float position)
{
var (scrollSize, reuseMargin) = Context.CalculateScrollSize();
var normalizedPosition = (Mathf.Lerp(0f, scrollSize, position) - reuseMargin) / (scrollSize - reuseMargin * 2f);
var start = 0.5f * scrollSize;
var end = -start;
UpdatePosition(normalizedPosition, Mathf.Lerp(start, end, position));
}
/// <summary>
/// このセルの位置を更新します.
/// </summary>
/// <param name="normalizedPosition">
/// ビューポートの範囲で正規化されたスクロール位置.
/// <see cref="FancyScrollRect{TItemData, TContext}.reuseCellMarginCount"/> の値に基づいて
/// <c>0.0</c> ~ <c>1.0</c> の範囲を超えた値が渡されることがあります.
/// </param>
/// <param name="localPosition">ローカル位置.</param>
protected virtual void UpdatePosition(float normalizedPosition, float localPosition)
{
transform.localPosition = Context.ScrollDirection == ScrollDirection.Horizontal
? new Vector2(-localPosition, 0)
: new Vector2(0, localPosition);
}
}
/// <summary>
/// <see cref="FancyScrollRect{TItemData}"/> のセルを実装するための抽象基底クラス.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <seealso cref="FancyScrollRectCell{TItemData, TContext}"/>
public abstract class FancyScrollRectCell<TItemData> : FancyScrollRectCell<TItemData, FancyScrollRectContext>
{
/// <inheritdoc/>
public sealed override void SetContext(FancyScrollRectContext context) => base.SetContext(context);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 09f137a55810740eab42e24ef242dcfa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,16 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// <see cref="FancyScrollRect{TItemData, TContext}"/> のコンテキスト基底クラス.
/// </summary>
public class FancyScrollRectContext : IFancyScrollRectContext
{
ScrollDirection IFancyScrollRectContext.ScrollDirection { get; set; }
Func<(float ScrollSize, float ReuseMargin)> IFancyScrollRectContext.CalculateScrollSize { get; set; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 91383bd46cee541a7a03e08cfaa47c16
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,16 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// <see cref="FancyScrollRect{TItemData, TContext}"/> のコンテキストインターフェース.
/// </summary>
public interface IFancyScrollRectContext
{
ScrollDirection ScrollDirection { get; set; }
Func<(float ScrollSize, float ReuseMargin)> CalculateScrollSize { get; set; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e425de6354b6946c7a3b9f2c807b60fe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 691a9f5ee4aec4112a01b9a4332cac51
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,194 @@
/*
* EasingCore (https://github.com/setchi/EasingCore)
* Copyright (c) 2020 setchi
* Licensed under MIT (https://github.com/setchi/EasingCore/blob/master/LICENSE)
*/
namespace UnityEngine.UI.Extensions.EasingCore
{
public enum Ease
{
Linear,
InBack,
InBounce,
InCirc,
InCubic,
InElastic,
InExpo,
InQuad,
InQuart,
InQuint,
InSine,
OutBack,
OutBounce,
OutCirc,
OutCubic,
OutElastic,
OutExpo,
OutQuad,
OutQuart,
OutQuint,
OutSine,
InOutBack,
InOutBounce,
InOutCirc,
InOutCubic,
InOutElastic,
InOutExpo,
InOutQuad,
InOutQuart,
InOutQuint,
InOutSine,
}
public delegate float EasingFunction(float t);
public static class Easing
{
/// <summary>
/// Gets the easing function
/// </summary>
/// <param name="type">Ease type</param>
/// <returns>Easing function</returns>
public static EasingFunction Get(Ease type)
{
switch (type)
{
case Ease.Linear: return linear;
case Ease.InBack: return inBack;
case Ease.InBounce: return inBounce;
case Ease.InCirc: return inCirc;
case Ease.InCubic: return inCubic;
case Ease.InElastic: return inElastic;
case Ease.InExpo: return inExpo;
case Ease.InQuad: return inQuad;
case Ease.InQuart: return inQuart;
case Ease.InQuint: return inQuint;
case Ease.InSine: return inSine;
case Ease.OutBack: return outBack;
case Ease.OutBounce: return outBounce;
case Ease.OutCirc: return outCirc;
case Ease.OutCubic: return outCubic;
case Ease.OutElastic: return outElastic;
case Ease.OutExpo: return outExpo;
case Ease.OutQuad: return outQuad;
case Ease.OutQuart: return outQuart;
case Ease.OutQuint: return outQuint;
case Ease.OutSine: return outSine;
case Ease.InOutBack: return inOutBack;
case Ease.InOutBounce: return inOutBounce;
case Ease.InOutCirc: return inOutCirc;
case Ease.InOutCubic: return inOutCubic;
case Ease.InOutElastic: return inOutElastic;
case Ease.InOutExpo: return inOutExpo;
case Ease.InOutQuad: return inOutQuad;
case Ease.InOutQuart: return inOutQuart;
case Ease.InOutQuint: return inOutQuint;
case Ease.InOutSine: return inOutSine;
default: return linear;
}
float linear(float t) => t;
float inBack(float t) => t * t * t - t * Mathf.Sin(t * Mathf.PI);
float outBack(float t) => 1f - inBack(1f - t);
float inOutBack(float t) =>
t < 0.5f
? 0.5f * inBack(2f * t)
: 0.5f * outBack(2f * t - 1f) + 0.5f;
float inBounce(float t) => 1f - outBounce(1f - t);
float outBounce(float t) =>
t < 4f / 11.0f ?
(121f * t * t) / 16.0f :
t < 8f / 11.0f ?
(363f / 40.0f * t * t) - (99f / 10.0f * t) + 17f / 5.0f :
t < 9f / 10.0f ?
(4356f / 361.0f * t * t) - (35442f / 1805.0f * t) + 16061f / 1805.0f :
(54f / 5.0f * t * t) - (513f / 25.0f * t) + 268f / 25.0f;
float inOutBounce(float t) =>
t < 0.5f
? 0.5f * inBounce(2f * t)
: 0.5f * outBounce(2f * t - 1f) + 0.5f;
float inCirc(float t) => 1f - Mathf.Sqrt(1f - (t * t));
float outCirc(float t) => Mathf.Sqrt((2f - t) * t);
float inOutCirc(float t) =>
t < 0.5f
? 0.5f * (1 - Mathf.Sqrt(1f - 4f * (t * t)))
: 0.5f * (Mathf.Sqrt(-((2f * t) - 3f) * ((2f * t) - 1f)) + 1f);
float inCubic(float t) => t * t * t;
float outCubic(float t) => inCubic(t - 1f) + 1f;
float inOutCubic(float t) =>
t < 0.5f
? 4f * t * t * t
: 0.5f * inCubic(2f * t - 2f) + 1f;
float inElastic(float t) => Mathf.Sin(13f * (Mathf.PI * 0.5f) * t) * Mathf.Pow(2f, 10f * (t - 1f));
float outElastic(float t) => Mathf.Sin(-13f * (Mathf.PI * 0.5f) * (t + 1)) * Mathf.Pow(2f, -10f * t) + 1f;
float inOutElastic(float t) =>
t < 0.5f
? 0.5f * Mathf.Sin(13f * (Mathf.PI * 0.5f) * (2f * t)) * Mathf.Pow(2f, 10f * ((2f * t) - 1f))
: 0.5f * (Mathf.Sin(-13f * (Mathf.PI * 0.5f) * ((2f * t - 1f) + 1f)) * Mathf.Pow(2f, -10f * (2f * t - 1f)) + 2f);
float inExpo(float t) => Mathf.Approximately(0.0f, t) ? t : Mathf.Pow(2f, 10f * (t - 1f));
float outExpo(float t) => Mathf.Approximately(1.0f, t) ? t : 1f - Mathf.Pow(2f, -10f * t);
float inOutExpo(float v) =>
Mathf.Approximately(0.0f, v) || Mathf.Approximately(1.0f, v)
? v
: v < 0.5f
? 0.5f * Mathf.Pow(2f, (20f * v) - 10f)
: -0.5f * Mathf.Pow(2f, (-20f * v) + 10f) + 1f;
float inQuad(float t) => t * t;
float outQuad(float t) => -t * (t - 2f);
float inOutQuad(float t) =>
t < 0.5f
? 2f * t * t
: -2f * t * t + 4f * t - 1f;
float inQuart(float t) => t * t * t * t;
float outQuart(float t)
{
var u = t - 1f;
return u * u * u * (1f - t) + 1f;
}
float inOutQuart(float t) =>
t < 0.5f
? 8f * inQuart(t)
: -8f * inQuart(t - 1f) + 1f;
float inQuint(float t) => t * t * t * t * t;
float outQuint(float t) => inQuint(t - 1f) + 1f;
float inOutQuint(float t) =>
t < 0.5f
? 16f * inQuint(t)
: 0.5f * inQuint(2f * t - 2f) + 1f;
float inSine(float t) => Mathf.Sin((t - 1f) * (Mathf.PI * 0.5f)) + 1f;
float outSine(float t) => Mathf.Sin(t * (Mathf.PI * 0.5f));
float inOutSine(float t) => 0.5f * (1f - Mathf.Cos(t * Mathf.PI));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3b2a35f9ff1c8487582b74620e380486
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,13 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
namespace UnityEngine.UI.Extensions
{
public enum MovementDirection
{
Left,
Right,
Up,
Down,
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 11769de5e972543c7a4132ceff94a5c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,12 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
namespace UnityEngine.UI.Extensions
{
public enum MovementType
{
Unrestricted = ScrollRect.MovementType.Unrestricted,
Elastic = ScrollRect.MovementType.Elastic,
Clamped = ScrollRect.MovementType.Clamped
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 585de8f4f272547c89041f94f2ad92b8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,11 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
namespace UnityEngine.UI.Extensions
{
public enum ScrollDirection
{
Vertical,
Horizontal,
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3392a30c085c642139c92e94ec0d6309
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,597 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System;
using UnityEngine.EventSystems;
using UnityEngine.UI.Extensions.EasingCore;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// スクロール位置の制御を行うコンポーネント.
/// </summary>
public class Scroller : UIBehaviour, IPointerUpHandler, IPointerDownHandler, IBeginDragHandler, IEndDragHandler, IDragHandler, IScrollHandler
{
[SerializeField] RectTransform viewport = default;
/// <summary>
/// ビューポートのサイズ.
/// </summary>
public float ViewportSize => scrollDirection == ScrollDirection.Horizontal
? viewport.rect.size.x
: viewport.rect.size.y;
[SerializeField] ScrollDirection scrollDirection = ScrollDirection.Vertical;
/// <summary>
/// スクロール方向.
/// </summary>
public ScrollDirection ScrollDirection => scrollDirection;
[SerializeField] MovementType movementType = MovementType.Elastic;
/// <summary>
/// コンテンツがスクロール範囲を越えて移動するときに使用する挙動.
/// </summary>
public MovementType MovementType
{
get => movementType;
set => movementType = value;
}
[SerializeField] float elasticity = 0.1f;
/// <summary>
/// コンテンツがスクロール範囲を越えて移動するときに使用する弾力性の量.
/// </summary>
public float Elasticity
{
get => elasticity;
set => elasticity = value;
}
[SerializeField] float scrollSensitivity = 1f;
/// <summary>
/// <see cref="ViewportSize"/> の端から端まで Drag したときのスクロール位置の変化量.
/// </summary>
public float ScrollSensitivity
{
get => scrollSensitivity;
set => scrollSensitivity = value;
}
[SerializeField] bool inertia = true;
/// <summary>
/// 慣性を使用するかどうか. <c>true</c> を指定すると慣性が有効に, <c>false</c> を指定すると慣性が無効になります.
/// </summary>
public bool Inertia
{
get => inertia;
set => inertia = value;
}
[SerializeField] float decelerationRate = 0.03f;
/// <summary>
/// スクロールの減速率. <see cref="Inertia"/> が <c>true</c> の場合のみ有効です.
/// </summary>
public float DecelerationRate
{
get => decelerationRate;
set => decelerationRate = value;
}
[SerializeField] Snap snap = new Snap {
Enable = true,
VelocityThreshold = 0.5f,
Duration = 0.3f,
Easing = Ease.InOutCubic
};
/// <summary>
/// <c>true</c> ならスナップし, <c>false</c>ならスナップしません.
/// </summary>
/// <remarks>
/// スナップを有効にすると, 慣性でスクロールが止まる直前に最寄りのセルへ移動します.
/// </remarks>
public bool SnapEnabled
{
get => snap.Enable;
set => snap.Enable = value;
}
[SerializeField] bool draggable = true;
/// <summary>
/// Drag 入力を受付けるかどうか.
/// </summary>
public bool Draggable
{
get => draggable;
set => draggable = value;
}
[SerializeField] Scrollbar scrollbar = default;
/// <summary>
/// スクロールバーのオブジェクト.
/// </summary>
public Scrollbar Scrollbar => scrollbar;
/// <summary>
/// 現在のスクロール位置.
/// </summary>
/// <value></value>
public float Position
{
get => currentPosition;
set
{
autoScrollState.Reset();
velocity = 0f;
dragging = false;
UpdatePosition(value);
}
}
readonly AutoScrollState autoScrollState = new AutoScrollState();
Action<float> onValueChanged;
Action<int> onSelectionChanged;
Vector2 beginDragPointerPosition;
float scrollStartPosition;
float prevPosition;
float currentPosition;
int totalCount;
bool hold;
bool scrolling;
bool dragging;
float velocity;
[Serializable]
class Snap
{
public bool Enable;
public float VelocityThreshold;
public float Duration;
public Ease Easing;
}
static readonly EasingFunction DefaultEasingFunction = Easing.Get(Ease.OutCubic);
class AutoScrollState
{
public bool Enable;
public bool Elastic;
public float Duration;
public EasingFunction EasingFunction;
public float StartTime;
public float EndPosition;
public Action OnComplete;
public void Reset()
{
Enable = false;
Elastic = false;
Duration = 0f;
StartTime = 0f;
EasingFunction = DefaultEasingFunction;
EndPosition = 0f;
OnComplete = null;
}
public void Complete()
{
OnComplete?.Invoke();
Reset();
}
}
protected override void Start()
{
base.Start();
if (scrollbar)
{
scrollbar.onValueChanged.AddListener(x => UpdatePosition(x * (totalCount - 1f), false));
}
}
/// <summary>
/// スクロール位置が変化したときのコールバックを設定します.
/// </summary>
/// <param name="callback">スクロール位置が変化したときのコールバック.</param>
public void OnValueChanged(Action<float> callback) => onValueChanged = callback;
/// <summary>
/// 選択位置が変化したときのコールバックを設定します.
/// </summary>
/// <param name="callback">選択位置が変化したときのコールバック.</param>
public void OnSelectionChanged(Action<int> callback) => onSelectionChanged = callback;
/// <summary>
/// アイテムの総数を設定します.
/// </summary>
/// <remarks>
/// <paramref name="totalCount"/> を元に最大スクロール位置を計算します.
/// </remarks>
/// <param name="totalCount">アイテムの総数.</param>
public void SetTotalCount(int totalCount) => this.totalCount = totalCount;
/// <summary>
/// 指定した位置まで移動します.
/// </summary>
/// <param name="position">スクロール位置. <c>0f</c> ~ <c>totalCount - 1f</c> の範囲.</param>
/// <param name="duration">移動にかける秒数.</param>
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
public void ScrollTo(float position, float duration, Action onComplete = null) => ScrollTo(position, duration, Ease.OutCubic, onComplete);
/// <summary>
/// 指定した位置まで移動します.
/// </summary>
/// <param name="position">スクロール位置. <c>0f</c> ~ <c>totalCount - 1f</c> の範囲.</param>
/// <param name="duration">移動にかける秒数.</param>
/// <param name="easing">移動に使用するイージング.</param>
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
public void ScrollTo(float position, float duration, Ease easing, Action onComplete = null) => ScrollTo(position, duration, Easing.Get(easing), onComplete);
/// <summary>
/// 指定した位置まで移動します.
/// </summary>
/// <param name="position">スクロール位置. <c>0f</c> ~ <c>totalCount - 1f</c> の範囲.</param>
/// <param name="duration">移動にかける秒数.</param>
/// <param name="easingFunction">移動に使用するイージング関数.</param>
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
public void ScrollTo(float position, float duration, EasingFunction easingFunction, Action onComplete = null)
{
if (duration <= 0f)
{
Position = CircularPosition(position, totalCount);
onComplete?.Invoke();
return;
}
autoScrollState.Reset();
autoScrollState.Enable = true;
autoScrollState.Duration = duration;
autoScrollState.EasingFunction = easingFunction ?? DefaultEasingFunction;
autoScrollState.StartTime = Time.unscaledTime;
autoScrollState.EndPosition = currentPosition + CalculateMovementAmount(currentPosition, position);
autoScrollState.OnComplete = onComplete;
velocity = 0f;
scrollStartPosition = currentPosition;
UpdateSelection(Mathf.RoundToInt(CircularPosition(autoScrollState.EndPosition, totalCount)));
}
/// <summary>
/// 指定したインデックスの位置までジャンプします.
/// </summary>
/// <param name="index">アイテムのインデックス.</param>
public void JumpTo(int index)
{
if (index < 0 || index > totalCount - 1)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
UpdateSelection(index);
Position = index;
}
/// <summary>
/// <paramref name="sourceIndex"/> から <paramref name="destIndex"/> に移動する際の移動方向を返します.
/// スクロール範囲が無制限に設定されている場合は, 最短距離の移動方向を返します.
/// </summary>
/// <param name="sourceIndex">移動元のインデックス.</param>
/// <param name="destIndex">移動先のインデックス.</param>
/// <returns></returns>
public MovementDirection GetMovementDirection(int sourceIndex, int destIndex)
{
var movementAmount = CalculateMovementAmount(sourceIndex, destIndex);
return scrollDirection == ScrollDirection.Horizontal
? movementAmount > 0
? MovementDirection.Left
: MovementDirection.Right
: movementAmount > 0
? MovementDirection.Up
: MovementDirection.Down;
}
/// <inheritdoc/>
void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
{
if (!draggable || eventData.button != PointerEventData.InputButton.Left)
{
return;
}
hold = true;
velocity = 0f;
autoScrollState.Reset();
}
/// <inheritdoc/>
void IPointerUpHandler.OnPointerUp(PointerEventData eventData)
{
if (!draggable || eventData.button != PointerEventData.InputButton.Left)
{
return;
}
if (hold && snap.Enable)
{
UpdateSelection(Mathf.Clamp(Mathf.RoundToInt(currentPosition), 0, totalCount - 1));
ScrollTo(Mathf.RoundToInt(currentPosition), snap.Duration, snap.Easing);
}
hold = false;
}
/// <inheritdoc/>
void IScrollHandler.OnScroll(PointerEventData eventData)
{
if (!draggable)
{
return;
}
var delta = eventData.scrollDelta;
// Down is positive for scroll events, while in UI system up is positive.
delta.y *= -1;
var scrollDelta = scrollDirection == ScrollDirection.Horizontal
? Mathf.Abs(delta.y) > Mathf.Abs(delta.x)
? delta.y
: delta.x
: Mathf.Abs(delta.x) > Mathf.Abs(delta.y)
? delta.x
: delta.y;
if (eventData.IsScrolling())
{
scrolling = true;
}
var position = currentPosition + scrollDelta / ViewportSize * scrollSensitivity;
if (movementType == MovementType.Clamped)
{
position += CalculateOffset(position);
}
if (autoScrollState.Enable)
{
autoScrollState.Reset();
}
UpdatePosition(position);
}
/// <inheritdoc/>
void IBeginDragHandler.OnBeginDrag(PointerEventData eventData)
{
if (!draggable || eventData.button != PointerEventData.InputButton.Left)
{
return;
}
hold = false;
RectTransformUtility.ScreenPointToLocalPointInRectangle(
viewport,
eventData.position,
eventData.pressEventCamera,
out beginDragPointerPosition);
scrollStartPosition = currentPosition;
dragging = true;
autoScrollState.Reset();
}
/// <inheritdoc/>
void IDragHandler.OnDrag(PointerEventData eventData)
{
if (!draggable || eventData.button != PointerEventData.InputButton.Left || !dragging)
{
return;
}
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(
viewport,
eventData.position,
eventData.pressEventCamera,
out var dragPointerPosition))
{
return;
}
var pointerDelta = dragPointerPosition - beginDragPointerPosition;
var position = (scrollDirection == ScrollDirection.Horizontal ? -pointerDelta.x : pointerDelta.y)
/ ViewportSize
* scrollSensitivity
+ scrollStartPosition;
var offset = CalculateOffset(position);
position += offset;
if (movementType == MovementType.Elastic)
{
if (offset != 0f)
{
position -= RubberDelta(offset, scrollSensitivity);
}
}
UpdatePosition(position);
}
/// <inheritdoc/>
void IEndDragHandler.OnEndDrag(PointerEventData eventData)
{
if (!draggable || eventData.button != PointerEventData.InputButton.Left)
{
return;
}
dragging = false;
}
float CalculateOffset(float position)
{
if (movementType == MovementType.Unrestricted)
{
return 0f;
}
if (position < 0f)
{
return -position;
}
if (position > totalCount - 1)
{
return totalCount - 1 - position;
}
return 0f;
}
void UpdatePosition(float position, bool updateScrollbar = true)
{
onValueChanged?.Invoke(currentPosition = position);
if (scrollbar && updateScrollbar)
{
scrollbar.value = Mathf.Clamp01(position / Mathf.Max(totalCount - 1f, 1e-4f));
}
}
void UpdateSelection(int index) => onSelectionChanged?.Invoke(index);
float RubberDelta(float overStretching, float viewSize) =>
(1 - 1 / (Mathf.Abs(overStretching) * 0.55f / viewSize + 1)) * viewSize * Mathf.Sign(overStretching);
void Update()
{
var deltaTime = Time.unscaledDeltaTime;
var offset = CalculateOffset(currentPosition);
if (autoScrollState.Enable)
{
var position = 0f;
if (autoScrollState.Elastic)
{
position = Mathf.SmoothDamp(currentPosition, currentPosition + offset, ref velocity,
elasticity, Mathf.Infinity, deltaTime);
if (Mathf.Abs(velocity) < 0.01f)
{
position = Mathf.Clamp(Mathf.RoundToInt(position), 0, totalCount - 1);
velocity = 0f;
autoScrollState.Complete();
}
}
else
{
var alpha = Mathf.Clamp01((Time.unscaledTime - autoScrollState.StartTime) /
Mathf.Max(autoScrollState.Duration, float.Epsilon));
position = Mathf.LerpUnclamped(scrollStartPosition, autoScrollState.EndPosition,
autoScrollState.EasingFunction(alpha));
if (Mathf.Approximately(alpha, 1f))
{
autoScrollState.Complete();
}
}
UpdatePosition(position);
}
else if (!(dragging || scrolling) && (!Mathf.Approximately(offset, 0f) || !Mathf.Approximately(velocity, 0f)))
{
var position = currentPosition;
if (movementType == MovementType.Elastic && !Mathf.Approximately(offset, 0f))
{
autoScrollState.Reset();
autoScrollState.Enable = true;
autoScrollState.Elastic = true;
UpdateSelection(Mathf.Clamp(Mathf.RoundToInt(position), 0, totalCount - 1));
}
else if (inertia)
{
velocity *= Mathf.Pow(decelerationRate, deltaTime);
if (Mathf.Abs(velocity) < 0.001f)
{
velocity = 0f;
}
position += velocity * deltaTime;
if (snap.Enable && Mathf.Abs(velocity) < snap.VelocityThreshold)
{
ScrollTo(Mathf.RoundToInt(currentPosition), snap.Duration, snap.Easing);
}
}
else
{
velocity = 0f;
}
if (!Mathf.Approximately(velocity, 0f))
{
if (movementType == MovementType.Clamped)
{
offset = CalculateOffset(position);
position += offset;
if (Mathf.Approximately(position, 0f) || Mathf.Approximately(position, totalCount - 1f))
{
velocity = 0f;
UpdateSelection(Mathf.RoundToInt(position));
}
}
UpdatePosition(position);
}
}
if (!autoScrollState.Enable && (dragging || scrolling) && inertia)
{
var newVelocity = (currentPosition - prevPosition) / deltaTime;
velocity = Mathf.Lerp(velocity, newVelocity, deltaTime * 10f);
}
prevPosition = currentPosition;
scrolling = false;
}
float CalculateMovementAmount(float sourcePosition, float destPosition)
{
if (movementType != MovementType.Unrestricted)
{
return Mathf.Clamp(destPosition, 0, totalCount - 1) - sourcePosition;
}
var amount = CircularPosition(destPosition, totalCount) - CircularPosition(sourcePosition, totalCount);
if (Mathf.Abs(amount) > totalCount * 0.5f)
{
amount = Mathf.Sign(-amount) * (totalCount - Mathf.Abs(amount));
}
return amount;
}
float CircularPosition(float p, int size) => size < 1 ? 0 : p < 0 ? size - 1 + (p + 1) % size : p % size;
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9f0e995f494626a4f878eedbded37c8d
timeCreated: 1487408581
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,428 @@
/// Credit Simie
/// Sourced from - http://forum.unity3d.com/threads/flowlayoutgroup.296709/
/// Example http://forum.unity3d.com/threads/flowlayoutgroup.296709/
/// Update by Martin Sharkbomb - http://forum.unity3d.com/threads/flowlayoutgroup.296709/#post-1977028
/// Last item alignment fix by Vicente Russo - https://bitbucket.org/SimonDarksideJ/unity-ui-extensions/issues/22/flow-layout-group-align
/// Vertical Flow by Ramon Molossi
using System.Collections.Generic;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// Layout Group controller that arranges children in bars, fitting as many on a line until total size exceeds parent bounds
/// </summary>
[AddComponentMenu("Layout/Extensions/Flow Layout Group")]
public class FlowLayoutGroup : LayoutGroup
{
public enum Axis { Horizontal = 0, Vertical = 1 }
public float SpacingX = 0f;
public float SpacingY = 0f;
public bool ExpandHorizontalSpacing = false;
public bool ChildForceExpandWidth = false;
public bool ChildForceExpandHeight = false;
public bool invertOrder = false;
private float _layoutHeight;
private float _layoutWidth;
[SerializeField] protected Axis m_StartAxis = Axis.Horizontal;
public Axis startAxis { get { return m_StartAxis; } set { SetProperty(ref m_StartAxis, value); } }
public override void CalculateLayoutInputHorizontal()
{
if (startAxis == Axis.Horizontal) {
base.CalculateLayoutInputHorizontal ();
var minWidth = GetGreatestMinimumChildWidth () + padding.left + padding.right;
SetLayoutInputForAxis (minWidth, -1, -1, 0);
} else {
_layoutWidth = SetLayout (0, true);
}
}
public override void SetLayoutHorizontal()
{
SetLayout(0, false);
}
public override void SetLayoutVertical()
{
SetLayout(1, false);
}
public override void CalculateLayoutInputVertical()
{
if (startAxis == Axis.Horizontal) {
_layoutHeight = SetLayout (1, true);
} else {
base.CalculateLayoutInputHorizontal ();
var minHeight = GetGreatestMinimumChildHeigth () + padding.bottom + padding.top;
SetLayoutInputForAxis (minHeight, -1, -1, 1);
}
}
protected bool IsCenterAlign
{
get
{
return childAlignment == TextAnchor.LowerCenter || childAlignment == TextAnchor.MiddleCenter ||
childAlignment == TextAnchor.UpperCenter;
}
}
protected bool IsRightAlign
{
get
{
return childAlignment == TextAnchor.LowerRight || childAlignment == TextAnchor.MiddleRight ||
childAlignment == TextAnchor.UpperRight;
}
}
protected bool IsMiddleAlign
{
get
{
return childAlignment == TextAnchor.MiddleLeft || childAlignment == TextAnchor.MiddleRight ||
childAlignment == TextAnchor.MiddleCenter;
}
}
protected bool IsLowerAlign
{
get
{
return childAlignment == TextAnchor.LowerLeft || childAlignment == TextAnchor.LowerRight ||
childAlignment == TextAnchor.LowerCenter;
}
}
/// <summary>
/// Holds the rects that will make up the current bar being processed
/// </summary>
private readonly IList<RectTransform> _itemList = new List<RectTransform>();
/// <summary>
/// Main layout method
/// </summary>
/// <param name="width">Width to calculate the layout with</param>
/// <param name="axis">0 for horizontal axis, 1 for vertical</param>
/// <param name="layoutInput">If true, sets the layout input for the axis. If false, sets child position for axis</param>
public float SetLayout(int axis, bool layoutInput)
{
//container height and width
var groupHeight = rectTransform.rect.height;
var groupWidth = rectTransform.rect.width;
float spacingBetweenBars = 0;
float spacingBetweenElements = 0;
float offset = 0;
float counterOffset = 0;
float groupSize = 0;
float workingSize = 0;
if (startAxis == Axis.Horizontal) {
groupSize = groupHeight;
workingSize = groupWidth - padding.left - padding.right;
if (IsLowerAlign) {
offset = (float)padding.bottom;
counterOffset = (float)padding.top;
} else {
offset = (float)padding.top;
counterOffset = (float)padding.bottom;
}
spacingBetweenBars = SpacingY;
spacingBetweenElements = SpacingX;
} else if (startAxis == Axis.Vertical) {
groupSize = groupWidth;
workingSize = groupHeight - padding.top - padding.bottom;
if (IsRightAlign) {
offset = (float)padding.right;
counterOffset = (float)padding.left;
} else {
offset = (float)padding.left;
counterOffset = (float)padding.right;
}
spacingBetweenBars = SpacingX;
spacingBetweenElements = SpacingY;
}
var currentBarSize = 0f;
var currentBarSpace = 0f;
for (var i = 0; i < rectChildren.Count; i++) {
int index = i;
var child = rectChildren [index];
float childSize = 0;
float childOtherSize = 0;
//get height and width of elements.
if (startAxis == Axis.Horizontal) {
if (invertOrder) {
index = IsLowerAlign ? rectChildren.Count - 1 - i : i;
}
child = rectChildren [index];
childSize = LayoutUtility.GetPreferredSize (child, 0);
childSize = Mathf.Min (childSize, workingSize);
childOtherSize = LayoutUtility.GetPreferredSize (child, 1);
childOtherSize = Mathf.Min (childOtherSize, workingSize);
} else if (startAxis == Axis.Vertical) {
if (invertOrder) {
index = IsRightAlign ? rectChildren.Count - 1 - i : i;
}
child = rectChildren [index];
childSize = LayoutUtility.GetPreferredSize (child, 1);
childSize = Mathf.Min (childSize, workingSize);
childOtherSize = LayoutUtility.GetPreferredSize (child, 0);
childOtherSize = Mathf.Min (childOtherSize, workingSize);
}
// If adding this element would exceed the bounds of the container,
// go to a new bar after processing the current bar
if (currentBarSize + childSize > workingSize) {
currentBarSize -= spacingBetweenElements;
// Process current bar elements positioning
if (!layoutInput) {
if (startAxis == Axis.Horizontal) {
float newOffset = CalculateRowVerticalOffset (groupSize, offset, currentBarSpace);
LayoutRow (_itemList, currentBarSize, currentBarSpace, workingSize, padding.left, newOffset, axis);
} else if (startAxis == Axis.Vertical) {
float newOffset = CalculateColHorizontalOffset (groupSize, offset, currentBarSpace);
LayoutCol (_itemList, currentBarSpace, currentBarSize, workingSize, newOffset, padding.top, axis);
}
}
// Clear existing bar
_itemList.Clear ();
// Add the current bar space to total barSpace accumulator, and reset to 0 for the next row
offset += currentBarSpace;
offset += spacingBetweenBars;
currentBarSpace = 0;
currentBarSize = 0;
}
currentBarSize += childSize;
_itemList.Add (child);
// We need the largest element height to determine the starting position of the next line
if (childOtherSize > currentBarSpace) {
currentBarSpace = childOtherSize;
}
// Don't do this for the last one
if (i < rectChildren.Count - 1){
currentBarSize += spacingBetweenElements;
}
}
// Layout the final bar
if (!layoutInput) {
if (startAxis == Axis.Horizontal) {
float newOffset = CalculateRowVerticalOffset (groupHeight, offset, currentBarSpace);
currentBarSize -= spacingBetweenElements;
LayoutRow (_itemList, currentBarSize, currentBarSpace, workingSize - (ChildForceExpandWidth ? 0 : spacingBetweenElements), padding.left, newOffset, axis);
}else if (startAxis == Axis.Vertical) {
float newOffset = CalculateColHorizontalOffset(groupWidth, offset, currentBarSpace);
currentBarSize -= spacingBetweenElements;
LayoutCol(_itemList, currentBarSpace, currentBarSize, workingSize - (ChildForceExpandHeight ? 0 : spacingBetweenElements), newOffset, padding.top, axis);
}
}
_itemList.Clear();
// Add the last bar space to the barSpace accumulator
offset += currentBarSpace;
offset += counterOffset;
if (layoutInput) {
SetLayoutInputForAxis(offset, offset, -1, axis);
}
return offset;
}
private float CalculateRowVerticalOffset(float groupHeight, float yOffset, float currentRowHeight)
{
if (IsLowerAlign) {
return groupHeight - yOffset - currentRowHeight;
} else if (IsMiddleAlign) {
return groupHeight * 0.5f - _layoutHeight * 0.5f + yOffset;
} else {
return yOffset;
}
}
private float CalculateColHorizontalOffset(float groupWidth, float xOffset, float currentColWidth)
{
if (IsRightAlign) {
return groupWidth - xOffset - currentColWidth;
} else if (IsCenterAlign) {
return groupWidth * 0.5f - _layoutWidth * 0.5f + xOffset;
} else {
return xOffset;
}
}
protected void LayoutRow(IList<RectTransform> contents, float rowWidth, float rowHeight, float maxWidth, float xOffset, float yOffset, int axis)
{
var xPos = xOffset;
if (!ChildForceExpandWidth && IsCenterAlign) {
xPos += (maxWidth - rowWidth) * 0.5f;
} else if (!ChildForceExpandWidth && IsRightAlign) {
xPos += (maxWidth - rowWidth);
}
var extraWidth = 0f;
var extraSpacing = 0f;
if (ChildForceExpandWidth) {
extraWidth = (maxWidth - rowWidth)/_itemList.Count;
}
else if (ExpandHorizontalSpacing) {
extraSpacing = (maxWidth - rowWidth)/(_itemList.Count - 1);
if (_itemList.Count > 1) {
if (IsCenterAlign) {
xPos -= extraSpacing * 0.5f * (_itemList.Count - 1);
} else if (IsRightAlign) {
xPos -= extraSpacing * (_itemList.Count - 1);
}
}
}
for (var j = 0; j < _itemList.Count; j++) {
var index = IsLowerAlign ? _itemList.Count - 1 - j : j;
var rowChild = _itemList[index];
var rowChildWidth = LayoutUtility.GetPreferredSize(rowChild, 0) + extraWidth;
var rowChildHeight = LayoutUtility.GetPreferredSize(rowChild, 1);
if (ChildForceExpandHeight)
rowChildHeight = rowHeight;
rowChildWidth = Mathf.Min(rowChildWidth, maxWidth);
var yPos = yOffset;
if (IsMiddleAlign) {
yPos += (rowHeight - rowChildHeight) * 0.5f;
} else if (IsLowerAlign) {
yPos += (rowHeight - rowChildHeight);
}
if (ExpandHorizontalSpacing && j > 0) {
xPos += extraSpacing;
}
if (axis == 0) {
SetChildAlongAxis (rowChild, 0, xPos, rowChildWidth);
} else {
SetChildAlongAxis (rowChild, 1, yPos, rowChildHeight);
}
// Don't do horizontal spacing for the last one
if (j < _itemList.Count - 1) {
xPos += rowChildWidth + SpacingX;
}
}
}
protected void LayoutCol(IList<RectTransform> contents, float colWidth, float colHeight, float maxHeight, float xOffset, float yOffset, int axis)
{
var yPos = yOffset;
if (!ChildForceExpandHeight && IsMiddleAlign) {
yPos += (maxHeight - colHeight) * 0.5f;
} else if (!ChildForceExpandHeight && IsLowerAlign) {
yPos += (maxHeight - colHeight);
}
var extraHeight = 0f;
var extraSpacing = 0f;
if (ChildForceExpandHeight) {
extraHeight = (maxHeight - colHeight)/_itemList.Count;
}
else if (ExpandHorizontalSpacing) {
extraSpacing = (maxHeight - colHeight)/(_itemList.Count - 1);
if (_itemList.Count > 1) {
if (IsMiddleAlign) {
yPos -= extraSpacing * 0.5f * (_itemList.Count - 1);
} else if (IsLowerAlign) {
yPos -= extraSpacing * (_itemList.Count - 1);
}
}
}
for (var j = 0; j < _itemList.Count; j++) {
var index = IsRightAlign ? _itemList.Count - 1 - j : j;
var rowChild = _itemList[index];
var rowChildWidth = LayoutUtility.GetPreferredSize(rowChild, 0) ;
var rowChildHeight = LayoutUtility.GetPreferredSize(rowChild, 1) + extraHeight;
if (ChildForceExpandWidth) {
rowChildWidth = colWidth;
}
rowChildHeight = Mathf.Min(rowChildHeight, maxHeight);
var xPos = xOffset;
if (IsCenterAlign) {
xPos += (colWidth - rowChildWidth) * 0.5f;
} else if (IsRightAlign) {
xPos += (colWidth - rowChildWidth);
}
//
if (ExpandHorizontalSpacing && j > 0) {
yPos += extraSpacing;
}
if (axis == 0) {
SetChildAlongAxis (rowChild, 0, xPos, rowChildWidth);
} else {
SetChildAlongAxis (rowChild, 1, yPos, rowChildHeight);
}
// Don't do vertical spacing for the last one
if (j < _itemList.Count - 1) {
yPos += rowChildHeight + SpacingY;
}
}
}
public float GetGreatestMinimumChildWidth()
{
var max = 0f;
for (var i = 0; i < rectChildren.Count; i++) {
var w = LayoutUtility.GetMinWidth(rectChildren[i]);
max = Mathf.Max(w, max);
}
return max;
}
public float GetGreatestMinimumChildHeigth()
{
var max = 0f;
for (var i = 0; i < rectChildren.Count; i++) {
var w = LayoutUtility.GetMinHeight(rectChildren[i]);
max = Mathf.Max(w, max);
}
return max;
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 558b109da67a27b4686138b955f3a7e8
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,323 @@
/// Credit BinaryX
/// Sourced from - http://forum.unity3d.com/threads/scripts-useful-4-6-scripts-collection.264161/page-2#post-1945602
/// Updated by SimonDarksideJ - removed dependency on a custom ScrollRect script. Now implements drag interfaces and standard Scroll Rect.
using System;
using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions
{
[RequireComponent(typeof(ScrollRect))]
[AddComponentMenu("Layout/Extensions/Horizontal Scroll Snap")]
public class HorizontalScrollSnap : ScrollSnapBase
{
private bool updated = true;
void Start()
{
_isVertical = false;
_childAnchorPoint = new Vector2(0, 0.5f);
_currentPage = StartingScreen;
panelDimensions = gameObject.GetComponent<RectTransform>().rect;
UpdateLayout();
}
void Update()
{
updated = false;
if (!_lerp && (_scroll_rect.velocity == Vector2.zero && _scroll_rect.inertia))
{
if (!_settled && !_pointerDown)
{
if (!IsRectSettledOnaPage(_screensContainer.anchoredPosition))
{
ScrollToClosestElement();
}
}
return;
}
else if (_lerp)
{
_screensContainer.anchoredPosition = Vector3.Lerp(_screensContainer.anchoredPosition, _lerp_target, transitionSpeed * (UseTimeScale ? Time.deltaTime : Time.unscaledDeltaTime));
if (Vector3.Distance(_screensContainer.anchoredPosition, _lerp_target) < 0.2f)
{
_screensContainer.anchoredPosition = _lerp_target;
_lerp = false;
EndScreenChange();
}
}
if (UseHardSwipe) return;
CurrentPage = GetPageforPosition(_screensContainer.anchoredPosition);
//If the container is moving check if it needs to settle on a page
if (!_pointerDown)
{
if (_scroll_rect.velocity.x > 0.01 || _scroll_rect.velocity.x < -0.01)
{
//if the pointer is released and is moving slower than the threshold, then just land on a page
if (IsRectMovingSlowerThanThreshold(0))
{
ScrollToClosestElement();
}
}
}
}
private bool IsRectMovingSlowerThanThreshold(float startingSpeed)
{
return (_scroll_rect.velocity.x > startingSpeed && _scroll_rect.velocity.x < SwipeVelocityThreshold) ||
(_scroll_rect.velocity.x < startingSpeed && _scroll_rect.velocity.x > -SwipeVelocityThreshold);
}
public void DistributePages()
{
_screens = _screensContainer.childCount;
_scroll_rect.horizontalNormalizedPosition = 0;
float _offset = 0;
float _dimension = 0;
Rect panelDimensions = gameObject.GetComponent<RectTransform>().rect;
float currentXPosition = 0;
var pageStepValue = _childSize = (int)panelDimensions.width * ((PageStep == 0) ? 3 : PageStep);
for (int i = 0; i < _screensContainer.transform.childCount; i++)
{
RectTransform child = _screensContainer.transform.GetChild(i).gameObject.GetComponent<RectTransform>();
currentXPosition = _offset + i * pageStepValue;
child.sizeDelta = new Vector2(panelDimensions.width, panelDimensions.height);
child.anchoredPosition = new Vector2(currentXPosition, 0f);
child.anchorMin = child.anchorMax = child.pivot = _childAnchorPoint;
}
_dimension = currentXPosition + _offset * -1;
_screensContainer.GetComponent<RectTransform>().offsetMax = new Vector2(_dimension, 0f);
}
/// <summary>
/// Add a new child to this Scroll Snap and recalculate it's children
/// </summary>
/// <param name="GO">GameObject to add to the ScrollSnap</param>
public void AddChild(GameObject GO)
{
AddChild(GO, false);
}
/// <summary>
/// Add a new child to this Scroll Snap and recalculate it's children
/// </summary>
/// <param name="GO">GameObject to add to the ScrollSnap</param>
/// <param name="WorldPositionStays">Should the world position be updated to it's parent transform?</param>
public void AddChild(GameObject GO, bool WorldPositionStays)
{
_scroll_rect.horizontalNormalizedPosition = 0;
GO.transform.SetParent(_screensContainer, WorldPositionStays);
InitialiseChildObjectsFromScene();
DistributePages();
if (MaskArea)
UpdateVisible();
SetScrollContainerPosition();
}
/// <summary>
/// Remove a new child to this Scroll Snap and recalculate it's children
/// *Note, this is an index address (0-x)
/// </summary>
/// <param name="index">Index element of child to remove</param>
/// <param name="ChildRemoved">Resulting removed GO</param>
public void RemoveChild(int index, out GameObject ChildRemoved)
{
RemoveChild(index, false, out ChildRemoved);
}
/// <summary>
/// Remove a new child to this Scroll Snap and recalculate it's children
/// *Note, this is an index address (0-x)
/// </summary>
/// <param name="index">Index element of child to remove</param>
/// <param name="WorldPositionStays">If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before</param>
/// <param name="ChildRemoved">Resulting removed GO</param>
public void RemoveChild(int index, bool WorldPositionStays, out GameObject ChildRemoved)
{
ChildRemoved = null;
if (index < 0 || index > _screensContainer.childCount)
{
return;
}
_scroll_rect.horizontalNormalizedPosition = 0;
Transform child = _screensContainer.transform.GetChild(index);
child.SetParent(null, WorldPositionStays);
ChildRemoved = child.gameObject;
InitialiseChildObjectsFromScene();
DistributePages();
if (MaskArea)
UpdateVisible();
if (_currentPage > _screens - 1)
{
CurrentPage = _screens - 1;
}
SetScrollContainerPosition();
}
/// <summary>
/// Remove all children from this ScrollSnap
/// </summary>
/// <param name="ChildrenRemoved">Array of child GO's removed</param>
public void RemoveAllChildren(out GameObject[] ChildrenRemoved)
{
RemoveAllChildren(false, out ChildrenRemoved);
}
/// <summary>
/// Remove all children from this ScrollSnap
/// </summary>
/// <param name="WorldPositionStays">If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before</param>
/// <param name="ChildrenRemoved">Array of child GO's removed</param>
public void RemoveAllChildren(bool WorldPositionStays, out GameObject[] ChildrenRemoved)
{
var _screenCount = _screensContainer.childCount;
ChildrenRemoved = new GameObject[_screenCount];
for (int i = _screenCount - 1; i >= 0; i--)
{
ChildrenRemoved[i] = _screensContainer.GetChild(i).gameObject;
ChildrenRemoved[i].transform.SetParent(null, WorldPositionStays);
}
_scroll_rect.horizontalNormalizedPosition = 0;
CurrentPage = 0;
InitialiseChildObjectsFromScene();
DistributePages();
if (MaskArea)
UpdateVisible();
}
private void SetScrollContainerPosition()
{
_scrollStartPosition = _screensContainer.anchoredPosition.x;
_scroll_rect.horizontalNormalizedPosition = (float)(_currentPage) / (_screens - 1);
OnCurrentScreenChange(_currentPage);
}
/// <summary>
/// used for changing / updating between screen resolutions
/// </summary>
public void UpdateLayout()
{
_lerp = false;
DistributePages();
if (MaskArea)
UpdateVisible();
SetScrollContainerPosition();
OnCurrentScreenChange(_currentPage);
}
private void OnRectTransformDimensionsChange()
{
if (_childAnchorPoint != Vector2.zero)
{
UpdateLayout();
}
}
private void OnEnable()
{
InitialiseChildObjectsFromScene();
DistributePages();
if (MaskArea)
UpdateVisible();
if (JumpOnEnable || !RestartOnEnable)
SetScrollContainerPosition();
if (RestartOnEnable)
GoToScreen(StartingScreen);
}
/// <summary>
/// Release screen to swipe
/// </summary>
/// <param name="eventData"></param>
public override void OnEndDrag(PointerEventData eventData)
{
if (updated)
{
return;
}
// to prevent double dragging, only act on EndDrag once per frame
updated = true;
_pointerDown = false;
if (_scroll_rect.horizontal)
{
if (UseSwipeDeltaThreshold && Math.Abs(eventData.delta.x) < SwipeDeltaThreshold)
{
ScrollToClosestElement();
}
else
{
var distance = Vector3.Distance(_startPosition, _screensContainer.anchoredPosition);
if (UseHardSwipe)
{
_scroll_rect.velocity = Vector3.zero;
if (distance > FastSwipeThreshold)
{
if (_startPosition.x - _screensContainer.anchoredPosition.x > 0)
{
NextScreen();
}
else
{
PreviousScreen();
}
}
else
{
ScrollToClosestElement();
}
}
else
{
if (UseFastSwipe && distance < panelDimensions.width && distance >= FastSwipeThreshold)
{
_scroll_rect.velocity = Vector3.zero;
if (_startPosition.x - _screensContainer.anchoredPosition.x > 0)
{
if (_startPosition.x - _screensContainer.anchoredPosition.x > _childSize / 3)
{
ScrollToClosestElement();
}
else
{
NextScreen();
}
}
else
{
if (_startPosition.x - _screensContainer.anchoredPosition.x < -_childSize / 3)
{
ScrollToClosestElement();
}
else
{
PreviousScreen();
}
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 609dcc22aadcc16418bfac22716ee9a6
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,14 @@
/// Credit SimonDarksideJ
/// Required for scrollbar support to work across ALL scroll snaps
namespace UnityEngine.UI.Extensions
{
internal interface IScrollSnap
{
void ChangePage(int page);
void SetLerp(bool value);
int CurrentPage();
void StartScreenChange();
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 08ba470d8c8dc384e9d5828136742452
timeCreated: 1498921617
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,101 @@
/// Credit Danny Goodayle
/// Sourced from - http://www.justapixel.co.uk/radial-layouts-nice-and-simple-in-unity3ds-ui-system/
/// Updated by SimonDarksideJ - removed dependency on a custom ScrollRect script. Now implements drag interfaces and standard Scroll Rect.
/// Child Layout fix by John Hattan - enables an options
/*
Radial Layout Group by Just a Pixel (Danny Goodayle) - http://www.justapixel.co.uk
Copyright (c) 2015
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
namespace UnityEngine.UI.Extensions
{
[AddComponentMenu("Layout/Extensions/Radial Layout")]
public class RadialLayout : LayoutGroup
{
public float fDistance;
[Range(0f, 360f)]
public float MinAngle, MaxAngle, StartAngle;
public bool OnlyLayoutVisible = false;
protected override void OnEnable() { base.OnEnable(); CalculateRadial(); }
public override void SetLayoutHorizontal()
{
}
public override void SetLayoutVertical()
{
}
public override void CalculateLayoutInputVertical()
{
CalculateRadial();
}
public override void CalculateLayoutInputHorizontal()
{
CalculateRadial();
}
#if UNITY_EDITOR
protected override void OnValidate()
{
base.OnValidate();
CalculateRadial();
}
#endif
void CalculateRadial()
{
m_Tracker.Clear();
if (transform.childCount == 0)
return;
int ChildrenToFormat = 0;
if (OnlyLayoutVisible)
{
for (int i = 0; i < transform.childCount; i++)
{
RectTransform child = (RectTransform)transform.GetChild(i);
if ((child != null) && child.gameObject.activeSelf)
++ChildrenToFormat;
}
}
else
{
ChildrenToFormat = transform.childCount;
}
float fOffsetAngle = (MaxAngle - MinAngle) / ChildrenToFormat;
float fAngle = StartAngle;
for (int i = 0; i < transform.childCount; i++)
{
RectTransform child = (RectTransform)transform.GetChild(i);
if ((child != null) && (!OnlyLayoutVisible || child.gameObject.activeSelf))
{
//Adding the elements to the tracker stops the user from modifying their positions via the editor.
m_Tracker.Add(this, child,
DrivenTransformProperties.Anchors |
DrivenTransformProperties.AnchoredPosition |
DrivenTransformProperties.Pivot);
Vector3 vPos = new Vector3(Mathf.Cos(fAngle * Mathf.Deg2Rad), Mathf.Sin(fAngle * Mathf.Deg2Rad), 0);
child.localPosition = vPos * fDistance;
//Force objects to be center aligned, this can be changed however I'd suggest you keep all of the objects with the same anchor points.
child.anchorMin = child.anchorMax = child.pivot = new Vector2(0.5f, 0.5f);
fAngle += fOffsetAngle;
}
}
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1ebce7906e5d20a4fb26d8b510b81926
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,383 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System;
using UnityEngine.Events;
using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions
{
public class ScrollPositionController : UIBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler
{
[SerializeField]
RectTransform viewport = null;
[SerializeField]
ScrollDirection directionOfRecognize = ScrollDirection.Vertical;
[SerializeField]
MovementType movementType = MovementType.Elastic;
[SerializeField]
float elasticity = 0.1f;
[SerializeField]
float scrollSensitivity = 1f;
[SerializeField]
bool inertia = true;
[SerializeField, Tooltip("Only used when inertia is enabled")]
float decelerationRate = 0.03f;
[SerializeField, Tooltip("Only used when inertia is enabled")]
Snap snap = new Snap { Enable = true, VelocityThreshold = 0.5f, Duration = 0.3f };
[SerializeField]
int dataCount;
readonly AutoScrollState autoScrollState = new AutoScrollState();
Action<float> onUpdatePosition;
Action<int> onItemSelected;
Vector2 pointerStartLocalPosition;
float dragStartScrollPosition;
float prevScrollPosition;
float currentScrollPosition;
bool dragging;
float velocity;
enum ScrollDirection
{
Vertical,
Horizontal,
}
enum MovementType
{
Unrestricted = ScrollRect.MovementType.Unrestricted,
Elastic = ScrollRect.MovementType.Elastic,
Clamped = ScrollRect.MovementType.Clamped
}
[Serializable]
struct Snap
{
public bool Enable;
public float VelocityThreshold;
public float Duration;
}
class AutoScrollState
{
public bool Enable;
public bool Elastic;
public float Duration;
public float StartTime;
public float EndScrollPosition;
public void Reset()
{
Enable = false;
Elastic = false;
Duration = 0f;
StartTime = 0f;
EndScrollPosition = 0f;
}
}
public void OnUpdatePosition(Action<float> onUpdatePosition)
{
this.onUpdatePosition = onUpdatePosition;
}
public void OnItemSelected(Action<int> onItemSelected)
{
this.onItemSelected = onItemSelected;
}
public void SetDataCount(int dataCount)
{
this.dataCount = dataCount;
}
public void ScrollTo(int index, float duration)
{
autoScrollState.Reset();
autoScrollState.Enable = true;
autoScrollState.Duration = duration;
autoScrollState.StartTime = Time.unscaledTime;
autoScrollState.EndScrollPosition = CalculateDestinationIndex(index);
velocity = 0f;
dragStartScrollPosition = currentScrollPosition;
ItemSelected(Mathf.RoundToInt(GetCircularPosition(autoScrollState.EndScrollPosition, dataCount)));
}
public void JumpTo(int index)
{
autoScrollState.Reset();
velocity = 0f;
dragging = false;
index = CalculateDestinationIndex(index);
ItemSelected(index);
UpdatePosition(index);
}
void IBeginDragHandler.OnBeginDrag(PointerEventData eventData)
{
if (eventData.button != PointerEventData.InputButton.Left)
{
return;
}
pointerStartLocalPosition = Vector2.zero;
RectTransformUtility.ScreenPointToLocalPointInRectangle(
viewport,
eventData.position,
eventData.pressEventCamera,
out pointerStartLocalPosition);
dragStartScrollPosition = currentScrollPosition;
dragging = true;
autoScrollState.Reset();
}
void IDragHandler.OnDrag(PointerEventData eventData)
{
if (eventData.button != PointerEventData.InputButton.Left)
{
return;
}
if (!dragging)
{
return;
}
Vector2 localCursor;
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(
viewport,
eventData.position,
eventData.pressEventCamera,
out localCursor))
{
return;
}
var pointerDelta = localCursor - pointerStartLocalPosition;
var position = (directionOfRecognize == ScrollDirection.Horizontal ? -pointerDelta.x : pointerDelta.y)
/ GetViewportSize()
* scrollSensitivity
+ dragStartScrollPosition;
var offset = CalculateOffset(position);
position += offset;
if (movementType == MovementType.Elastic)
{
if (offset != 0f)
{
position -= RubberDelta(offset, scrollSensitivity);
}
}
UpdatePosition(position);
}
void IEndDragHandler.OnEndDrag(PointerEventData eventData)
{
if (eventData.button != PointerEventData.InputButton.Left)
{
return;
}
dragging = false;
}
float GetViewportSize()
{
return directionOfRecognize == ScrollDirection.Horizontal
? viewport.rect.size.x
: viewport.rect.size.y;
}
float CalculateOffset(float position)
{
if (movementType == MovementType.Unrestricted)
{
return 0f;
}
if (position < 0f)
{
return -position;
}
if (position > dataCount - 1)
{
return dataCount - 1 - position;
}
return 0f;
}
void UpdatePosition(float position)
{
currentScrollPosition = position;
if (onUpdatePosition != null)
{
onUpdatePosition(currentScrollPosition);
}
}
void ItemSelected(int index)
{
if (onItemSelected != null)
{
onItemSelected(index);
}
}
float RubberDelta(float overStretching, float viewSize)
{
return (1 - (1 / ((Mathf.Abs(overStretching) * 0.55f / viewSize) + 1))) * viewSize * Mathf.Sign(overStretching);
}
void Update()
{
var deltaTime = Time.unscaledDeltaTime;
var offset = CalculateOffset(currentScrollPosition);
if (autoScrollState.Enable)
{
var position = 0f;
if (autoScrollState.Elastic)
{
var speed = velocity;
position = Mathf.SmoothDamp(currentScrollPosition, currentScrollPosition + offset, ref speed, elasticity, Mathf.Infinity, deltaTime);
velocity = speed;
if (Mathf.Abs(velocity) < 0.01f)
{
position = Mathf.Clamp(Mathf.RoundToInt(position), 0, dataCount - 1);
velocity = 0f;
autoScrollState.Reset();
}
}
else
{
var alpha = Mathf.Clamp01((Time.unscaledTime - autoScrollState.StartTime) / Mathf.Max(autoScrollState.Duration, float.Epsilon));
position = Mathf.Lerp(dragStartScrollPosition, autoScrollState.EndScrollPosition, EaseInOutCubic(0, 1, alpha));
if (Mathf.Approximately(alpha, 1f))
{
autoScrollState.Reset();
}
}
UpdatePosition(position);
}
else if (!dragging && (!Mathf.Approximately(offset, 0f) || !Mathf.Approximately(velocity, 0f)))
{
var position = currentScrollPosition;
if (movementType == MovementType.Elastic && !Mathf.Approximately(offset, 0f))
{
autoScrollState.Reset();
autoScrollState.Enable = true;
autoScrollState.Elastic = true;
ItemSelected(Mathf.Clamp(Mathf.RoundToInt(position), 0, dataCount - 1));
}
else if (inertia)
{
velocity *= Mathf.Pow(decelerationRate, deltaTime);
if (Mathf.Abs(velocity) < 0.001f)
{
velocity = 0f;
}
position += velocity * deltaTime;
if (snap.Enable && Mathf.Abs(velocity) < snap.VelocityThreshold)
{
ScrollTo(Mathf.RoundToInt(currentScrollPosition), snap.Duration);
}
}
else
{
velocity = 0f;
}
if (!Mathf.Approximately(velocity, 0f))
{
if (movementType == MovementType.Clamped)
{
offset = CalculateOffset(position);
position += offset;
if (Mathf.Approximately(position, 0f) || Mathf.Approximately(position, dataCount - 1f))
{
velocity = 0f;
ItemSelected(Mathf.RoundToInt(position));
}
}
UpdatePosition(position);
}
}
if (!autoScrollState.Enable && dragging && inertia)
{
var newVelocity = (currentScrollPosition - prevScrollPosition) / deltaTime;
velocity = Mathf.Lerp(velocity, newVelocity, deltaTime * 10f);
}
if (currentScrollPosition != prevScrollPosition)
{
prevScrollPosition = currentScrollPosition;
}
}
int CalculateDestinationIndex(int index)
{
return movementType == MovementType.Unrestricted
? CalculateClosestIndex(index)
: Mathf.Clamp(index, 0, dataCount - 1);
}
int CalculateClosestIndex(int index)
{
var diff = GetCircularPosition(index, dataCount)
- GetCircularPosition(currentScrollPosition, dataCount);
if (Mathf.Abs(diff) > dataCount * 0.5f)
{
diff = Mathf.Sign(-diff) * (dataCount - Mathf.Abs(diff));
}
return Mathf.RoundToInt(diff + currentScrollPosition);
}
float GetCircularPosition(float position, int length)
{
return position < 0 ? length - 1 + (position + 1) % length : position % length;
}
float EaseInOutCubic(float start, float end, float value)
{
value /= 0.5f;
end -= start;
if (value < 1f)
{
return end * 0.5f * value * value * value + start;
}
value -= 2f;
return end * 0.5f * (value * value * value + 2f) + start;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: cc9ad31350b1b6348b57c626195a562d
timeCreated: 1501610618
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,572 @@
/// Credit BinaryX
/// Sourced from - http://forum.unity3d.com/threads/scripts-useful-4-6-scripts-collection.264161/page-2#post-1945602
/// Updated by simonDarksideJ - removed dependency on a custom ScrollRect script. Now implements drag interfaces and standard Scroll Rect.
/// Update by xesenix - rewrote almost the entire code
/// - configuration for direction move instead of 2 concurrent class (easier to change direction in editor)
/// - supports list layout with horizontal or vertical layout need to match direction with type of layout used
/// - dynamic checks if scrolled list size changes and recalculates anchor positions
/// and item size based on itemsVisibleAtOnce and size of root container
/// if you don't wish to use this auto resize turn of autoLayoutItems
/// - fixed current page made it independent from pivot
/// - replaced pagination with delegate function
using System;
using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions
{
[ExecuteInEditMode]
[RequireComponent(typeof(ScrollRect))]
[AddComponentMenu("UI/Extensions/Scroll Snap")]
public class ScrollSnap : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler, IScrollSnap
{
// needed because of reversed behaviour of axis Y compared to X
// (positions of children lower in children list in horizontal directions grows when in vertical it gets smaller)
public enum ScrollDirection
{
Horizontal,
Vertical
}
private ScrollRect _scroll_rect;
private RectTransform _scrollRectTransform;
private Transform _listContainerTransform;
//private RectTransform _rectTransform;
private int _pages;
private int _startingPage = 0;
// anchor points to lerp to see child on certain indexes
private Vector3[] _pageAnchorPositions;
private Vector3 _lerpTarget;
private bool _lerp;
// item list related
private float _listContainerMinPosition;
private float _listContainerMaxPosition;
private float _listContainerSize;
private RectTransform _listContainerRectTransform;
private Vector2 _listContainerCachedSize;
private float _itemSize;
private int _itemsCount = 0;
// drag related
private bool _startDrag = true;
private Vector3 _positionOnDragStart = new Vector3();
private int _pageOnDragStart;
private bool _fastSwipeTimer = false;
private int _fastSwipeCounter = 0;
private int _fastSwipeTarget = 10;
[Tooltip("Button to go to the next page. (optional)")]
public Button NextButton;
[Tooltip("Button to go to the previous page. (optional)")]
public Button PrevButton;
[Tooltip("Number of items visible in one page of scroll frame.")]
[RangeAttribute(1, 100)]
public int ItemsVisibleAtOnce = 1;
[Tooltip("Sets minimum width of list items to 1/itemsVisibleAtOnce.")]
public bool AutoLayoutItems = true;
[Tooltip("If you wish to update scrollbar numberOfSteps to number of active children on list.")]
public bool LinkScrolbarSteps = false;
[Tooltip("If you wish to update scrollrect sensitivity to size of list element.")]
public bool LinkScrolrectScrollSensitivity = false;
public Boolean UseFastSwipe = true;
public int FastSwipeThreshold = 100;
public delegate void PageSnapChange(int page);
public event PageSnapChange onPageChange;
public ScrollDirection direction = ScrollDirection.Horizontal;
// Use this for initialization
void Start()
{
_lerp = false;
_scroll_rect = gameObject.GetComponent<ScrollRect>();
_scrollRectTransform = gameObject.GetComponent<RectTransform>();
_listContainerTransform = _scroll_rect.content;
_listContainerRectTransform = _listContainerTransform.GetComponent<RectTransform>();
//_rectTransform = _listContainerTransform.gameObject.GetComponent<RectTransform>();
UpdateListItemsSize();
UpdateListItemPositions();
PageChanged(CurrentPage());
if (NextButton)
{
NextButton.GetComponent<Button>().onClick.AddListener(() =>
{
NextScreen();
});
}
if (PrevButton)
{
PrevButton.GetComponent<Button>().onClick.AddListener(() =>
{
PreviousScreen();
});
}
if (_scroll_rect.horizontalScrollbar != null && _scroll_rect.horizontal)
{
var hscroll = _scroll_rect.horizontalScrollbar.gameObject.GetOrAddComponent<ScrollSnapScrollbarHelper>();
hscroll.ss = this;
}
if (_scroll_rect.verticalScrollbar != null && _scroll_rect.vertical)
{
var vscroll = _scroll_rect.verticalScrollbar.gameObject.GetOrAddComponent<ScrollSnapScrollbarHelper>();
vscroll.ss = this;
}
}
public void UpdateListItemsSize()
{
float size = 0;
float currentSize = 0;
if (direction == ScrollSnap.ScrollDirection.Horizontal)
{
size = _scrollRectTransform.rect.width / ItemsVisibleAtOnce;
currentSize = _listContainerRectTransform.rect.width / _itemsCount;
}
else
{
size = _scrollRectTransform.rect.height / ItemsVisibleAtOnce;
currentSize = _listContainerRectTransform.rect.height / _itemsCount;
}
_itemSize = size;
if (LinkScrolrectScrollSensitivity)
{
_scroll_rect.scrollSensitivity = _itemSize;
}
if (AutoLayoutItems && currentSize != size && _itemsCount > 0)
{
if (direction == ScrollSnap.ScrollDirection.Horizontal)
{
foreach (var tr in _listContainerTransform)
{
GameObject child = ((Transform)tr).gameObject;
if (child.activeInHierarchy)
{
var childLayout = child.GetComponent<LayoutElement>();
if (childLayout == null)
{
childLayout = child.AddComponent<LayoutElement>();
}
childLayout.minWidth = _itemSize;
}
}
}
else
{
foreach (var tr in _listContainerTransform)
{
GameObject child = ((Transform)tr).gameObject;
if (child.activeInHierarchy)
{
var childLayout = child.GetComponent<LayoutElement>();
if (childLayout == null)
{
childLayout = child.AddComponent<LayoutElement>();
}
childLayout.minHeight = _itemSize;
}
}
}
}
}
public void UpdateListItemPositions()
{
if (!_listContainerRectTransform.rect.size.Equals(_listContainerCachedSize))
{
// checking how many children of list are active
int activeCount = 0;
foreach (var tr in _listContainerTransform)
{
if (((Transform)tr).gameObject.activeInHierarchy)
{
activeCount++;
}
}
// if anything changed since last check reinitialize anchors list
_itemsCount = 0;
Array.Resize(ref _pageAnchorPositions, activeCount);
if (activeCount > 0)
{
_pages = Mathf.Max(activeCount - ItemsVisibleAtOnce + 1, 1);
if (direction == ScrollDirection.Horizontal)
{
// looking for list spanning range min/max
_scroll_rect.horizontalNormalizedPosition = 0;
_listContainerMaxPosition = _listContainerTransform.localPosition.x;
_scroll_rect.horizontalNormalizedPosition = 1;
_listContainerMinPosition = _listContainerTransform.localPosition.x;
_listContainerSize = _listContainerMaxPosition - _listContainerMinPosition;
for (var i = 0; i < _pages; i++)
{
_pageAnchorPositions[i] = new Vector3(
_listContainerMaxPosition - _itemSize * i,
_listContainerTransform.localPosition.y,
_listContainerTransform.localPosition.z
);
}
}
else
{
//Debug.Log ("-------------looking for list spanning range----------------");
// looking for list spanning range
_scroll_rect.verticalNormalizedPosition = 1;
_listContainerMinPosition = _listContainerTransform.localPosition.y;
_scroll_rect.verticalNormalizedPosition = 0;
_listContainerMaxPosition = _listContainerTransform.localPosition.y;
_listContainerSize = _listContainerMaxPosition - _listContainerMinPosition;
for (var i = 0; i < _pages; i++)
{
_pageAnchorPositions[i] = new Vector3(
_listContainerTransform.localPosition.x,
_listContainerMinPosition + _itemSize * i,
_listContainerTransform.localPosition.z
);
}
}
UpdateScrollbar(LinkScrolbarSteps);
_startingPage = Mathf.Min(_startingPage, _pages);
ResetPage();
}
if (_itemsCount != activeCount)
{
PageChanged(CurrentPage());
}
_itemsCount = activeCount;
_listContainerCachedSize.Set(_listContainerRectTransform.rect.size.x, _listContainerRectTransform.rect.size.y);
}
}
public void ResetPage()
{
if (direction == ScrollDirection.Horizontal)
{
_scroll_rect.horizontalNormalizedPosition = _pages > 1 ? (float)_startingPage / (float)(_pages - 1) : 0;
}
else
{
_scroll_rect.verticalNormalizedPosition = _pages > 1 ? (float)(_pages - _startingPage - 1) / (float)(_pages - 1) : 0;
}
}
private void UpdateScrollbar(bool linkSteps)
{
if (linkSteps)
{
if (direction == ScrollDirection.Horizontal)
{
if (_scroll_rect.horizontalScrollbar != null)
{
_scroll_rect.horizontalScrollbar.numberOfSteps = _pages;
}
}
else
{
if (_scroll_rect.verticalScrollbar != null)
{
_scroll_rect.verticalScrollbar.numberOfSteps = _pages;
}
}
}
else
{
if (direction == ScrollDirection.Horizontal)
{
if (_scroll_rect.horizontalScrollbar != null)
{
_scroll_rect.horizontalScrollbar.numberOfSteps = 0;
}
}
else
{
if (_scroll_rect.verticalScrollbar != null)
{
_scroll_rect.verticalScrollbar.numberOfSteps = 0;
}
}
}
}
void LateUpdate()
{
UpdateListItemsSize();
UpdateListItemPositions();
if (_lerp)
{
UpdateScrollbar(false);
_listContainerTransform.localPosition = Vector3.Lerp(_listContainerTransform.localPosition, _lerpTarget, 7.5f * Time.deltaTime);
if (Vector3.Distance(_listContainerTransform.localPosition, _lerpTarget) < 0.001f)
{
_listContainerTransform.localPosition = _lerpTarget;
_lerp = false;
UpdateScrollbar(LinkScrolbarSteps);
}
//change the info bullets at the bottom of the screen. Just for visual effect
if (Vector3.Distance(_listContainerTransform.localPosition, _lerpTarget) < 10f)
{
PageChanged(CurrentPage());
}
}
if (_fastSwipeTimer)
{
_fastSwipeCounter++;
}
}
private bool fastSwipe = false; //to determine if a fast swipe was performed
//Function for switching screens with buttons
public void NextScreen()
{
UpdateListItemPositions();
if (CurrentPage() < _pages - 1)
{
_lerp = true;
_lerpTarget = _pageAnchorPositions[CurrentPage() + 1];
PageChanged(CurrentPage() + 1);
}
}
//Function for switching screens with buttons
public void PreviousScreen()
{
UpdateListItemPositions();
if (CurrentPage() > 0)
{
_lerp = true;
_lerpTarget = _pageAnchorPositions[CurrentPage() - 1];
PageChanged(CurrentPage() - 1);
}
}
//Because the CurrentScreen function is not so reliable, these are the functions used for swipes
private void NextScreenCommand()
{
if (_pageOnDragStart < _pages - 1)
{
int targetPage = Mathf.Min(_pages - 1, _pageOnDragStart + ItemsVisibleAtOnce);
_lerp = true;
_lerpTarget = _pageAnchorPositions[targetPage];
PageChanged(targetPage);
}
}
//Because the CurrentScreen function is not so reliable, these are the functions used for swipes
private void PrevScreenCommand()
{
if (_pageOnDragStart > 0)
{
int targetPage = Mathf.Max(0, _pageOnDragStart - ItemsVisibleAtOnce);
_lerp = true;
_lerpTarget = _pageAnchorPositions[targetPage];
PageChanged(targetPage);
}
}
//returns the current screen that the is seeing
public int CurrentPage()
{
float pos;
if (direction == ScrollDirection.Horizontal)
{
pos = _listContainerMaxPosition - _listContainerTransform.localPosition.x;
pos = Mathf.Clamp(pos, 0, _listContainerSize);
}
else
{
pos = _listContainerTransform.localPosition.y - _listContainerMinPosition;
pos = Mathf.Clamp(pos, 0, _listContainerSize);
}
float page = pos / _itemSize;
return Mathf.Clamp(Mathf.RoundToInt(page), 0, _pages);
}
/// <summary>
/// Added to provide a uniform interface for the ScrollBarHelper
/// </summary>
public void SetLerp(bool value)
{
_lerp = value;
}
public void ChangePage(int page)
{
if (0 <= page && page < _pages)
{
_lerp = true;
_lerpTarget = _pageAnchorPositions[page];
PageChanged(page);
}
}
//changes the bullets on the bottom of the page - pagination
private void PageChanged(int currentPage)
{
_startingPage = currentPage;
if (NextButton)
{
NextButton.interactable = currentPage < _pages - 1;
}
if (PrevButton)
{
PrevButton.interactable = currentPage > 0;
}
if (onPageChange != null)
{
onPageChange(currentPage);
}
}
#region Interfaces
public void OnBeginDrag(PointerEventData eventData)
{
UpdateScrollbar(false);
_fastSwipeCounter = 0;
_fastSwipeTimer = true;
_positionOnDragStart = eventData.position;
_pageOnDragStart = CurrentPage();
}
public void OnEndDrag(PointerEventData eventData)
{
_startDrag = true;
float change = 0;
if (direction == ScrollDirection.Horizontal)
{
change = _positionOnDragStart.x - eventData.position.x;
}
else
{
change = -_positionOnDragStart.y + eventData.position.y;
}
if (UseFastSwipe)
{
fastSwipe = false;
_fastSwipeTimer = false;
if (_fastSwipeCounter <= _fastSwipeTarget)
{
if (Math.Abs(change) > FastSwipeThreshold)
{
fastSwipe = true;
}
}
if (fastSwipe)
{
if (change > 0)
{
NextScreenCommand();
}
else
{
PrevScreenCommand();
}
}
else
{
_lerp = true;
_lerpTarget = _pageAnchorPositions[CurrentPage()];
}
}
else
{
_lerp = true;
_lerpTarget = _pageAnchorPositions[CurrentPage()];
}
}
public void OnDrag(PointerEventData eventData)
{
_lerp = false;
if (_startDrag)
{
OnBeginDrag(eventData);
_startDrag = false;
}
}
public void StartScreenChange() { }
#endregion
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c598b387777d96643991be3f0b6c98c2
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,650 @@
/// Credit BinaryX
/// Sourced from - http://forum.unity3d.com/threads/scripts-useful-4-6-scripts-collection.264161/page-2#post-1945602
/// Updated by simonDarksideJ - removed dependency on a custom ScrollRect script. Now implements drag interfaces and standard Scroll Rect.
using System;
using UnityEngine.Events;
using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions
{
public class ScrollSnapBase : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IScrollSnap, IPointerClickHandler
{
internal Rect panelDimensions;
internal RectTransform _screensContainer;
internal bool _isVertical;
internal int _screens = 1;
internal float _scrollStartPosition;
internal float _childSize;
private float _childPos, _maskSize;
internal Vector2 _childAnchorPoint;
internal ScrollRect _scroll_rect;
internal Vector3 _lerp_target;
internal bool _lerp;
internal bool _pointerDown = false;
internal bool _settled = true;
internal Vector3 _startPosition = new Vector3();
[Tooltip("The currently active page")]
internal int _currentPage;
internal int _previousPage;
internal int _halfNoVisibleItems;
internal bool _isInfinite; // Is a UI Infinite scroller attached to the control
internal int _infiniteWindow; // The infinite window the control is in
internal float _infiniteOffset; // How much to offset a repositioning
private int _bottomItem, _topItem;
internal bool _startEventCalled = false;
internal bool _endEventCalled = false;
internal bool _suspendEvents = false;
[Serializable]
public class SelectionChangeStartEvent : UnityEvent { }
[Serializable]
public class SelectionPageChangedEvent : UnityEvent<int> { }
[Serializable]
public class SelectionChangeEndEvent : UnityEvent<int> { }
[Tooltip("The screen / page to start the control on\n*Note, this is a 0 indexed array")]
[SerializeField]
public int StartingScreen = 0;
[Tooltip("The distance between two pages based on page height, by default pages are next to each other")]
[SerializeField]
[Range(0, 8)]
public float PageStep = 1;
[Tooltip("The gameobject that contains toggles which suggest pagination. (optional)")]
public GameObject Pagination;
[Tooltip("Button to go to the previous page. (optional)")]
public GameObject PrevButton;
[Tooltip("Button to go to the next page. (optional)")]
public GameObject NextButton;
[Tooltip("Transition speed between pages. (optional)")]
public float transitionSpeed = 7.5f;
[Tooltip("Hard Swipe forces to swiping to the next / previous page (optional)")]
public Boolean UseHardSwipe = false;
[Tooltip("Fast Swipe makes swiping page next / previous (optional)")]
public Boolean UseFastSwipe = false;
[Tooltip("Swipe Delta Threshold looks at the speed of input to decide if a swipe will be initiated (optional)")]
public Boolean UseSwipeDeltaThreshold = false;
[Tooltip("Offset for how far a swipe has to travel to initiate a page change (optional)")]
public int FastSwipeThreshold = 100;
[Tooltip("Speed at which the ScrollRect will keep scrolling before slowing down and stopping (optional)")]
public int SwipeVelocityThreshold = 100;
[Tooltip("Threshold for swipe speed to initiate a swipe, below threshold will return to closest page (optional)")]
public float SwipeDeltaThreshold = 5.0f;
[Tooltip("Use time scale instead of unscaled time (optional)")]
public Boolean UseTimeScale = true;
[Tooltip("The visible bounds area, controls which items are visible/enabled. *Note Should use a RectMask. (optional)")]
public RectTransform MaskArea;
[Tooltip("Pixel size to buffer around Mask Area. (optional)")]
public float MaskBuffer = 1;
public int CurrentPage
{
get
{
return _currentPage;
}
internal set
{
if (_isInfinite)
{
//Work out which infinite window we are in
float infWindow = (float)value / (float)_screensContainer.childCount;
if (infWindow < 0)
{
_infiniteWindow = (int)(Math.Floor(infWindow));
}
else
{
_infiniteWindow = value / _screensContainer.childCount;
}
//Invert the value if negative and differentiate from Window 0
_infiniteWindow = value < 0 ? (-_infiniteWindow) : _infiniteWindow;
//Calculate the page within the child count range
value = value % _screensContainer.childCount;
if (value < 0)
{
value = _screensContainer.childCount + value;
}
else if (value > _screensContainer.childCount - 1)
{
value = value - _screensContainer.childCount;
}
}
if ((value != _currentPage && value >= 0 && value < _screensContainer.childCount) || (value == 0 && _screensContainer.childCount == 0))
{
_previousPage = _currentPage;
_currentPage = value;
if (MaskArea) UpdateVisible();
if (!_lerp) ScreenChange();
OnCurrentScreenChange(_currentPage);
}
}
}
[Tooltip("By default the container will lerp to the start when enabled in the scene, this option overrides this and forces it to simply jump without lerping")]
public bool JumpOnEnable = false;
[Tooltip("By default the container will return to the original starting page when enabled, this option overrides this behaviour and stays on the current selection")]
public bool RestartOnEnable = false;
[Tooltip("(Experimental)\nBy default, child array objects will use the parent transform\nHowever you can disable this for some interesting effects")]
public bool UseParentTransform = true;
[Tooltip("Scroll Snap children. (optional)\nEither place objects in the scene as children OR\nPrefabs in this array, NOT BOTH")]
public GameObject[] ChildObjects;
[SerializeField]
[Tooltip("Event fires when a user starts to change the selection")]
private SelectionChangeStartEvent m_OnSelectionChangeStartEvent = new SelectionChangeStartEvent();
public SelectionChangeStartEvent OnSelectionChangeStartEvent { get { return m_OnSelectionChangeStartEvent; } set { m_OnSelectionChangeStartEvent = value; } }
[SerializeField]
[Tooltip("Event fires as the page changes, while dragging or jumping")]
private SelectionPageChangedEvent m_OnSelectionPageChangedEvent = new SelectionPageChangedEvent();
public SelectionPageChangedEvent OnSelectionPageChangedEvent { get { return m_OnSelectionPageChangedEvent; } set { m_OnSelectionPageChangedEvent = value; } }
[SerializeField]
[Tooltip("Event fires when the page settles after a user has dragged")]
private SelectionChangeEndEvent m_OnSelectionChangeEndEvent = new SelectionChangeEndEvent();
public SelectionChangeEndEvent OnSelectionChangeEndEvent { get { return m_OnSelectionChangeEndEvent; } set { m_OnSelectionChangeEndEvent = value; } }
// Use this for initialization
void Awake()
{
if (_scroll_rect == null)
{
_scroll_rect = gameObject.GetComponent<ScrollRect>();
}
if (_scroll_rect.horizontalScrollbar && _scroll_rect.horizontal)
{
var hscroll = _scroll_rect.horizontalScrollbar.gameObject.AddComponent<ScrollSnapScrollbarHelper>();
hscroll.ss = this;
}
if (_scroll_rect.verticalScrollbar && _scroll_rect.vertical)
{
var vscroll = _scroll_rect.verticalScrollbar.gameObject.AddComponent<ScrollSnapScrollbarHelper>();
vscroll.ss = this;
}
panelDimensions = gameObject.GetComponent<RectTransform>().rect;
if (StartingScreen < 0)
{
StartingScreen = 0;
}
_screensContainer = _scroll_rect.content;
InitialiseChildObjects();
if (NextButton)
NextButton.GetComponent<Button>().onClick.AddListener(() => { NextScreen(); });
if (PrevButton)
PrevButton.GetComponent<Button>().onClick.AddListener(() => { PreviousScreen(); });
_isInfinite = GetComponent<UI_InfiniteScroll>() != null;
}
internal void InitialiseChildObjects()
{
if (ChildObjects != null && ChildObjects.Length > 0)
{
if (_screensContainer.transform.childCount > 0)
{
Debug.LogError("ScrollRect Content has children, this is not supported when using managed Child Objects\n Either remove the ScrollRect Content children or clear the ChildObjects array");
return;
}
InitialiseChildObjectsFromArray();
if (GetComponent<UI_InfiniteScroll>() != null)
{
GetComponent<UI_InfiniteScroll>().Init();
}
}
else
{
InitialiseChildObjectsFromScene();
}
}
internal void InitialiseChildObjectsFromScene()
{
int childCount = _screensContainer.childCount;
ChildObjects = new GameObject[childCount];
for (int i = 0; i < childCount; i++)
{
ChildObjects[i] = _screensContainer.transform.GetChild(i).gameObject;
if (MaskArea && ChildObjects[i].activeSelf)
{
ChildObjects[i].SetActive(false);
}
}
}
internal void InitialiseChildObjectsFromArray()
{
int childCount = ChildObjects.Length;
RectTransform childRect;
GameObject child;
for (int i = 0; i < childCount; i++)
{
child = GameObject.Instantiate(ChildObjects[i]);
//Optionally, use original GO transform when initialising, by default will use parent RectTransform position/rotation
if (UseParentTransform)
{
childRect = child.GetComponent<RectTransform>();
childRect.rotation = _screensContainer.rotation;
childRect.localScale = _screensContainer.localScale;
childRect.position = _screensContainer.position;
}
child.transform.SetParent(_screensContainer.transform);
ChildObjects[i] = child;
if (MaskArea && ChildObjects[i].activeSelf)
{
ChildObjects[i].SetActive(false);
}
}
}
internal void UpdateVisible()
{
//If there are no objects in the scene or a mask, exit
if (!MaskArea || ChildObjects == null || ChildObjects.Length < 1 || _screensContainer.childCount < 1)
{
return;
}
_maskSize = _isVertical ? MaskArea.rect.height : MaskArea.rect.width;
_halfNoVisibleItems = (int)Math.Round(_maskSize / (_childSize * MaskBuffer), MidpointRounding.AwayFromZero) / 2;
_bottomItem = _topItem = 0;
//work out how many items below the current page can be visible
for (int i = _halfNoVisibleItems + 1; i > 0; i--)
{
_bottomItem = _currentPage - i < 0 ? 0 : i;
if (_bottomItem > 0) break;
}
//work out how many items above the current page can be visible
for (int i = _halfNoVisibleItems + 1; i > 0; i--)
{
_topItem = _screensContainer.childCount - _currentPage - i < 0 ? 0 : i;
if (_topItem > 0) break;
}
//Set the active items active
for (int i = CurrentPage - _bottomItem; i < CurrentPage + _topItem; i++)
{
try
{
ChildObjects[i].SetActive(true);
}
catch
{
Debug.Log("Failed to setactive child [" + i + "]");
}
}
//Deactivate items out of visibility at the bottom of the ScrollRect Mask (only on scroll)
if (_currentPage > _halfNoVisibleItems) ChildObjects[CurrentPage - _bottomItem].SetActive(false);
//Deactivate items out of visibility at the top of the ScrollRect Mask (only on scroll)
if (_screensContainer.childCount - _currentPage > _topItem) ChildObjects[CurrentPage + _topItem].SetActive(false);
}
//Function for switching screens with buttons
public void NextScreen()
{
if (_currentPage < _screens - 1 || _isInfinite)
{
if (!_lerp) StartScreenChange();
_lerp = true;
if (_isInfinite)
{
CurrentPage = GetPageforPosition(_screensContainer.anchoredPosition) + 1;
}
else
{
CurrentPage = _currentPage + 1;
}
GetPositionforPage(_currentPage, ref _lerp_target);
ScreenChange();
}
}
//Function for switching screens with buttons
public void PreviousScreen()
{
if (_currentPage > 0 || _isInfinite)
{
if (!_lerp) StartScreenChange();
_lerp = true;
if (_isInfinite)
{
CurrentPage = GetPageforPosition(_screensContainer.anchoredPosition) - 1;
}
else
{
CurrentPage = _currentPage - 1;
}
GetPositionforPage(_currentPage, ref _lerp_target);
ScreenChange();
}
}
/// <summary>
/// Function for switching to a specific screen
/// *Note, this is based on a 0 starting index - 0 to x
/// </summary>
/// <param name="screenIndex">0 starting index of page to jump to</param>
/// <param name="pagination">Override the screen movement if driven from a pagination control</param>
public void GoToScreen(int screenIndex, bool pagination = false)
{
if (screenIndex <= _screens - 1 && screenIndex >= 0)
{
if (!_lerp || pagination) StartScreenChange();
_lerp = true;
CurrentPage = screenIndex;
GetPositionforPage(_currentPage, ref _lerp_target);
ScreenChange();
}
}
/// <summary>
/// Gets the closest page for the current Scroll Rect container position
/// </summary>
/// <param name="pos">Position to test, normally the Scroll Rect container Local position</param>
/// <returns>Closest Page number (zero indexed array value)</returns>
internal int GetPageforPosition(Vector3 pos)
{
return _isVertical ?
(int)Math.Round((_scrollStartPosition - pos.y) / _childSize) :
(int)Math.Round((_scrollStartPosition - pos.x) / _childSize);
}
/// <summary>
/// Validates if the current Scroll Rect container position is within the bounds for a page
/// </summary>
/// <param name="pos">Position to test, normally the Scroll Rect container Local position</param>
/// <returns>True / False, is the position in the bounds of a page</returns>
internal bool IsRectSettledOnaPage(Vector3 pos)
{
return _isVertical ?
-((pos.y - _scrollStartPosition) / _childSize) == -(int)Math.Round((pos.y - _scrollStartPosition) / _childSize) :
-((pos.x - _scrollStartPosition) / _childSize) == -(int)Math.Round((pos.x - _scrollStartPosition) / _childSize);
}
/// <summary>
/// Returns the local position for a child page based on the required page number
/// </summary>
/// <param name="page">Page that the position is required for (Zero indexed array value)</param>
/// <param name="target">Outputs the local position for the selected page</param>
internal void GetPositionforPage(int page, ref Vector3 target)
{
_childPos = -_childSize * page;
if (_isVertical)
{
_infiniteOffset = _screensContainer.anchoredPosition.y < 0 ? -_screensContainer.sizeDelta.y * _infiniteWindow : _screensContainer.sizeDelta.y * _infiniteWindow;
_infiniteOffset = _infiniteOffset == 0 ? 0 : _infiniteOffset < 0 ? _infiniteOffset - _childSize * _infiniteWindow : _infiniteOffset + _childSize * _infiniteWindow;
target.y = _childPos + _scrollStartPosition + _infiniteOffset;
}
else
{
_infiniteOffset = _screensContainer.anchoredPosition.x < 0 ? -_screensContainer.sizeDelta.x * _infiniteWindow : _screensContainer.sizeDelta.x * _infiniteWindow;
_infiniteOffset = _infiniteOffset == 0 ? 0 : _infiniteOffset < 0 ? _infiniteOffset - _childSize * _infiniteWindow : _infiniteOffset + _childSize * _infiniteWindow;
target.x = _childPos + _scrollStartPosition + _infiniteOffset;
}
}
/// <summary>
/// Updates the _Lerp target to the closest page and updates the pagination bullets. Each control's update loop will then handle the move.
/// </summary>
internal void ScrollToClosestElement()
{
_lerp = true;
CurrentPage = GetPageforPosition(_screensContainer.anchoredPosition);
GetPositionforPage(_currentPage, ref _lerp_target);
OnCurrentScreenChange(_currentPage);
}
/// <summary>
/// notifies pagination indicator and navigation buttons of a screen change
/// </summary>
internal void OnCurrentScreenChange(int currentScreen)
{
ChangeBulletsInfo(currentScreen);
ToggleNavigationButtons(currentScreen);
}
/// <summary>
/// changes the bullets on the bottom of the page - pagination
/// </summary>
/// <param name="targetScreen"></param>
private void ChangeBulletsInfo(int targetScreen)
{
if (Pagination)
{
for (int i = 0; i < Pagination.transform.childCount; i++)
{
Pagination.transform.GetChild(i).GetComponent<Toggle>().isOn = (targetScreen == i) ? true : false;
}
}
}
// Make a lock function for pagination, to prevent event leaking
/// <summary>
/// disables the page navigation buttons when at the first or last screen
/// </summary>
/// <param name="targetScreen"></param>
private void ToggleNavigationButtons(int targetScreen)
{
//If this is using an Infinite Scroll, then don't disable
if (!_isInfinite)
{
if (PrevButton)
{
PrevButton.GetComponent<Button>().interactable = targetScreen > 0;
}
if (NextButton)
{
NextButton.GetComponent<Button>().interactable = targetScreen < _screensContainer.transform.childCount - 1;
}
}
}
private void OnValidate()
{
if (_scroll_rect == null)
{
_scroll_rect = GetComponent<ScrollRect>();
}
if (!_scroll_rect.horizontal && !_scroll_rect.vertical)
{
Debug.LogError("ScrollRect has to have a direction, please select either Horizontal OR Vertical with the appropriate control.");
}
if (_scroll_rect.horizontal && _scroll_rect.vertical)
{
Debug.LogError("ScrollRect has to be unidirectional, only use either Horizontal or Vertical on the ScrollRect, NOT both.");
}
var ScrollRectContent = gameObject.GetComponent<ScrollRect>().content;
if (ScrollRectContent != null)
{
var children = ScrollRectContent.childCount;
if (children != 0 || ChildObjects != null)
{
var childCount = ChildObjects == null || ChildObjects.Length == 0 ? children : ChildObjects.Length;
if (StartingScreen > childCount - 1)
{
StartingScreen = childCount - 1;
}
if (StartingScreen < 0)
{
StartingScreen = 0;
}
}
}
if (MaskBuffer <= 0)
{
MaskBuffer = 1;
}
if (PageStep < 0)
{
PageStep = 0;
}
if (PageStep > 8)
{
PageStep = 9;
}
var infiniteScroll = GetComponent<UI_InfiniteScroll>();
if (ChildObjects != null && ChildObjects.Length > 0 && infiniteScroll != null && !infiniteScroll.InitByUser)
{
Debug.LogError($"[{gameObject.name}]When using procedural children with a ScrollSnap (Adding Prefab ChildObjects) and the Infinite Scroll component\nYou must set the 'InitByUser' option to true, to enable late initialising");
}
}
/// <summary>
/// Event fires when the user starts to change the page, either via swipe or button.
/// </summary>
public void StartScreenChange()
{
if (!_startEventCalled)
{
_suspendEvents = true;
_startEventCalled = true;
_endEventCalled = false;
OnSelectionChangeStartEvent.Invoke();
}
}
/// <summary>
/// Event fires when the currently viewed page changes, also updates while the scroll is moving
/// </summary>
internal void ScreenChange()
{
OnSelectionPageChangedEvent.Invoke(_currentPage);
}
/// <summary>
/// Event fires when control settles on a page, outputs the new page number
/// </summary>
internal void EndScreenChange()
{
if (!_endEventCalled)
{
_suspendEvents = false;
_endEventCalled = true;
_startEventCalled = false;
_settled = true;
OnSelectionChangeEndEvent.Invoke(_currentPage);
}
}
/// <summary>
/// Returns the Transform of the Current page
/// </summary>
/// <returns>Currently selected Page Transform</returns>
public Transform CurrentPageObject()
{
return _screensContainer.GetChild(CurrentPage);
}
/// <summary>
/// Returns the Transform of the Current page in an out parameter for performance
/// </summary>
/// <param name="returnObject">Currently selected Page Transform</param>
public void CurrentPageObject(out Transform returnObject)
{
returnObject = _screensContainer.GetChild(CurrentPage);
}
#region Drag Interfaces
/// <summary>
/// Touch screen to start swiping
/// </summary>
/// <param name="eventData"></param>
public void OnBeginDrag(PointerEventData eventData)
{
_pointerDown = true;
_settled = false;
StartScreenChange();
_startPosition = _screensContainer.anchoredPosition;
}
/// <summary>
/// While dragging do
/// </summary>
/// <param name="eventData"></param>
public void OnDrag(PointerEventData eventData)
{
_lerp = false;
}
public virtual void OnEndDrag(PointerEventData eventData) { }
#endregion
#region IScrollSnap Interface
/// <summary>
/// Added to provide a uniform interface for the ScrollBarHelper
/// </summary>
int IScrollSnap.CurrentPage()
{
return CurrentPage = GetPageforPosition(_screensContainer.anchoredPosition);
}
/// <summary>
/// Added to provide a uniform interface for the ScrollBarHelper
/// </summary>
public void SetLerp(bool value)
{
_lerp = value;
}
/// <summary>
/// Added to provide a uniform interface for the ScrollBarHelper
/// </summary>
public void ChangePage(int page)
{
GoToScreen(page);
}
public void OnPointerClick(PointerEventData eventData)
{
var position = _screensContainer.anchoredPosition;
}
#endregion
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c2a77c45e5354bf40bbd63bd817dee47
timeCreated: 1480931407
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,54 @@
/// Credit Anonymous donation
/// Sourced from - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/issues/120/horizontal-scroll-snap-scroll-bar-fix
/// Updated by simonDarksideJ - Made extension support all types of scroll snap
using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions
{
[DisallowMultipleComponent]
public class ScrollSnapScrollbarHelper : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IBeginDragHandler, IEndDragHandler, IDragHandler
{
internal IScrollSnap ss;
public void OnBeginDrag(PointerEventData eventData)
{
OnScrollBarDown();
}
public void OnDrag(PointerEventData eventData)
{
ss.CurrentPage();
}
public void OnEndDrag(PointerEventData eventData)
{
OnScrollBarUp();
}
public void OnPointerDown(PointerEventData eventData)
{
OnScrollBarDown();
}
public void OnPointerUp(PointerEventData eventData)
{
OnScrollBarUp();
}
void OnScrollBarDown()
{
if (ss != null)
{
ss.SetLerp(false);
ss.StartScreenChange();
}
}
void OnScrollBarUp()
{
ss.SetLerp(true);
ss.ChangePage(ss.CurrentPage());
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2ec759101f5786b4b89aae8a1b47ca4b
timeCreated: 1498919960
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,288 @@
/// Credit RahulOfTheRamanEffect
/// Sourced from - https://forum.unity3d.com/members/rahuloftheramaneffect.773241/
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// Arranges child objects into a non-uniform grid, with fixed column widths and flexible row heights
/// </summary>
[AddComponentMenu("Layout/Extensions/Table Layout Group")]
public class TableLayoutGroup : LayoutGroup
{
public enum Corner
{
UpperLeft = 0,
UpperRight = 1,
LowerLeft = 2,
LowerRight = 3
}
[SerializeField]
protected Corner startCorner = Corner.UpperLeft;
/// <summary>
/// The corner starting from which the cells should be arranged
/// </summary>
public Corner StartCorner
{
get { return startCorner; }
set
{
SetProperty(ref startCorner, value);
}
}
[SerializeField]
protected float[] columnWidths = new float[1] { 96f };
/// <summary>
/// The widths of all the columns in the table
/// </summary>
public float[] ColumnWidths
{
get { return columnWidths; }
set
{
SetProperty(ref columnWidths, value);
}
}
[SerializeField]
protected float minimumRowHeight = 32f;
/// <summary>
/// The minimum height for any row in the table
/// </summary>
public float MinimumRowHeight
{
get { return minimumRowHeight; }
set
{
SetProperty(ref minimumRowHeight, value);
}
}
[SerializeField]
protected bool flexibleRowHeight = true;
/// <summary>
/// Expand rows to fit the cell with the highest preferred height?
/// </summary>
public bool FlexibleRowHeight
{
get { return flexibleRowHeight; }
set
{
SetProperty(ref flexibleRowHeight, value);
}
}
[SerializeField]
protected float columnSpacing = 0f;
/// <summary>
/// The horizontal spacing between each cell in the table
/// </summary>
public float ColumnSpacing
{
get { return columnSpacing; }
set
{
SetProperty(ref columnSpacing, value);
}
}
[SerializeField]
protected float rowSpacing = 0;
/// <summary>
/// The vertical spacing between each row in the table
/// </summary>
public float RowSpacing
{
get { return rowSpacing; }
set
{
SetProperty(ref rowSpacing, value);
}
}
// Temporarily stores data generated during the execution CalculateLayoutInputVertical for use in SetLayoutVertical
private float[] preferredRowHeights;
public override void CalculateLayoutInputHorizontal()
{
base.CalculateLayoutInputHorizontal();
float horizontalSize = padding.horizontal;
// We calculate the actual cell count for cases where the number of children is lesser than the number of columns
int actualCellCount = Mathf.Min(rectChildren.Count, columnWidths.Length);
for (int i = 0; i < actualCellCount; i++)
{
horizontalSize += columnWidths[i];
horizontalSize += columnSpacing;
}
horizontalSize -= columnSpacing;
SetLayoutInputForAxis(horizontalSize, horizontalSize, 0, 0);
}
public override void CalculateLayoutInputVertical()
{
int columnCount = columnWidths.Length;
int rowCount = Mathf.CeilToInt(rectChildren.Count / (float)columnCount);
preferredRowHeights = new float[rowCount];
float totalMinHeight = padding.vertical;
float totalPreferredHeight = padding.vertical;
if (rowCount > 1)
{
float heightFromSpacing = ((rowCount - 1) * rowSpacing);
totalMinHeight += heightFromSpacing;
totalPreferredHeight += heightFromSpacing;
}
if (flexibleRowHeight)
{
// If flexibleRowHeight is enabled, find the max value for minimum and preferred heights in each row
float maxMinimumHeightInRow = 0;
float maxPreferredHeightInRow = 0;
for (int i = 0; i < rowCount; i++)
{
maxMinimumHeightInRow = minimumRowHeight;
maxPreferredHeightInRow = minimumRowHeight;
for (int j = 0; j < columnCount; j++)
{
int childIndex = (i * columnCount) + j;
// Safeguard against tables with incomplete rows
if (childIndex == rectChildren.Count)
break;
maxPreferredHeightInRow = Mathf.Max(LayoutUtility.GetPreferredHeight(rectChildren[childIndex]), maxPreferredHeightInRow);
maxMinimumHeightInRow = Mathf.Max(LayoutUtility.GetMinHeight(rectChildren[childIndex]), maxMinimumHeightInRow);
}
totalMinHeight += maxMinimumHeightInRow;
totalPreferredHeight += maxPreferredHeightInRow;
// Add calculated row height to a commonly accessible array for reuse in SetLayoutVertical()
preferredRowHeights[i] = maxPreferredHeightInRow;
}
}
else
{
// If flexibleRowHeight is disabled, then use the minimumRowHeight to calculate vertical layout information
for (int i = 0; i < rowCount; i++)
preferredRowHeights[i] = minimumRowHeight;
totalMinHeight += rowCount * minimumRowHeight;
totalPreferredHeight = totalMinHeight;
}
totalPreferredHeight = Mathf.Max(totalMinHeight, totalPreferredHeight);
SetLayoutInputForAxis(totalMinHeight, totalPreferredHeight, 1, 1);
}
public override void SetLayoutHorizontal()
{
// If no column width is defined, then assign a reasonable default
if (columnWidths.Length == 0)
columnWidths = new float[1] { 0f };
int columnCount = columnWidths.Length;
int cornerX = (int)startCorner % 2;
float startOffset = 0;
float requiredSizeWithoutPadding = 0;
// We calculate the actual cell count for cases where the number of children is lesser than the number of columns
int actualCellCount = Mathf.Min(rectChildren.Count, columnWidths.Length);
for (int i = 0; i < actualCellCount; i++)
{
requiredSizeWithoutPadding += columnWidths[i];
requiredSizeWithoutPadding += columnSpacing;
}
requiredSizeWithoutPadding -= columnSpacing;
startOffset = GetStartOffset(0, requiredSizeWithoutPadding);
if (cornerX == 1)
startOffset += requiredSizeWithoutPadding;
float positionX = startOffset;
for (int i = 0; i < rectChildren.Count; i++)
{
int currentColumnIndex = i % columnCount;
// If it's the first cell in the row, reset positionX
if (currentColumnIndex == 0)
positionX = startOffset;
if (cornerX == 1)
positionX -= columnWidths[currentColumnIndex];
SetChildAlongAxis(rectChildren[i], 0, positionX, columnWidths[currentColumnIndex]);
if (cornerX == 1)
positionX -= columnSpacing;
else
positionX += columnWidths[currentColumnIndex] + columnSpacing;
}
}
public override void SetLayoutVertical()
{
int columnCount = columnWidths.Length;
int rowCount = preferredRowHeights.Length;
int cornerY = (int)startCorner / 2;
float startOffset = 0;
float requiredSizeWithoutPadding = 0;
for (int i = 0; i < rowCount; i++)
requiredSizeWithoutPadding += preferredRowHeights[i];
if (rowCount > 1)
requiredSizeWithoutPadding += (rowCount - 1) * rowSpacing;
startOffset = GetStartOffset(1, requiredSizeWithoutPadding);
if (cornerY == 1)
startOffset += requiredSizeWithoutPadding;
float positionY = startOffset;
for (int i = 0; i < rowCount; i++)
{
if (cornerY == 1)
positionY -= preferredRowHeights[i];
for (int j = 0; j < columnCount; j++)
{
int childIndex = (i * columnCount) + j;
// Safeguard against tables with incomplete rows
if (childIndex == rectChildren.Count)
break;
SetChildAlongAxis(rectChildren[childIndex], 1, positionY, preferredRowHeights[i]);
}
if (cornerY == 1)
positionY -= rowSpacing;
else
positionY += preferredRowHeights[i] + rowSpacing;
}
// Set preferredRowHeights to null to free memory
preferredRowHeights = null;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6cee1f3bb43ca9c4c9e00b54a998d9ce
timeCreated: 1483117134
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,106 @@
/// Credit Ges
/// Sourced from - http://forum.unity3d.com/threads/scripts-useful-4-6-scripts-collection.264161/page-3#post-2280109
using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions
{
[ExecuteInEditMode]
[RequireComponent(typeof(RectTransform))]
[AddComponentMenu("Layout/Extensions/Tile Size Fitter")]
public class TileSizeFitter : UIBehaviour, ILayoutSelfController
{
[SerializeField]
private Vector2 m_Border = Vector2.zero;
public Vector2 Border { get { return m_Border; } set { if (SetPropertyUtility.SetStruct(ref m_Border, value)) SetDirty(); } }
[SerializeField]
private Vector2 m_TileSize = Vector2.zero;
public Vector2 TileSize { get { return m_TileSize; } set { if (SetPropertyUtility.SetStruct(ref m_TileSize, value)) SetDirty(); } }
[System.NonSerialized]
private RectTransform m_Rect;
private RectTransform rectTransform { get { if (m_Rect == null) m_Rect = GetComponent<RectTransform>(); return m_Rect; } }
private DrivenRectTransformTracker m_Tracker;
#region Unity Lifetime calls
protected override void OnEnable()
{
base.OnEnable();
SetDirty();
}
protected override void OnDisable()
{
m_Tracker.Clear();
LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
base.OnDisable();
}
#endregion
protected override void OnRectTransformDimensionsChange()
{
UpdateRect();
}
private void UpdateRect()
{
if (!IsActive())
return;
m_Tracker.Clear();
m_Tracker.Add(this, rectTransform,
DrivenTransformProperties.Anchors |
DrivenTransformProperties.AnchoredPosition);
rectTransform.anchorMin = Vector2.zero;
rectTransform.anchorMax = Vector2.one;
rectTransform.anchoredPosition = Vector2.zero;
m_Tracker.Add(this, rectTransform,
DrivenTransformProperties.SizeDeltaX |
DrivenTransformProperties.SizeDeltaY);
Vector2 sizeDelta = GetParentSize() - Border;
if (TileSize.x > 0.001f)
sizeDelta.x -= Mathf.Floor(sizeDelta.x / TileSize.x) * TileSize.x;
else
sizeDelta.x = 0;
if (TileSize.y > 0.001f)
sizeDelta.y -= Mathf.Floor(sizeDelta.y / TileSize.y) * TileSize.y;
else
sizeDelta.y = 0;
rectTransform.sizeDelta = -sizeDelta;
}
private Vector2 GetParentSize()
{
RectTransform parent = rectTransform.parent as RectTransform;
if (!parent)
return Vector2.zero;
return parent.rect.size;
}
public virtual void SetLayoutHorizontal() { }
public virtual void SetLayoutVertical() { }
protected void SetDirty()
{
if (!IsActive())
return;
UpdateRect();
}
#if UNITY_EDITOR
protected override void OnValidate()
{
m_TileSize.x = Mathf.Clamp(m_TileSize.x, 0.001f, 1000f);
m_TileSize.y = Mathf.Clamp(m_TileSize.y, 0.001f, 1000f);
SetDirty();
}
#endif
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 614c629b8dffdb548b9bef9189606bb9
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,254 @@
/// Credit Mrs. YakaYocha
/// Sourced from - https://www.youtube.com/channel/UCHp8LZ_0-iCvl-5pjHATsgw
/// Please donate: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RJ8D9FRFQF9VS
using UnityEngine.Events;
namespace UnityEngine.UI.Extensions
{
[RequireComponent(typeof(ScrollRect))]
[AddComponentMenu("Layout/Extensions/Vertical Scroller")]
public class UIVerticalScroller : MonoBehaviour
{
[Tooltip("desired ScrollRect")]
public ScrollRect scrollRect;
[Tooltip("Center display area (position of zoomed content)")]
public RectTransform center;
[Tooltip("Size / spacing of elements")]
public RectTransform elementSize;
[Tooltip("Scale = 1/ (1+distance from center * shrinkage)")]
public Vector2 elementShrinkage = new Vector2(1f / 200, 1f / 200);
[Tooltip("Minimum element scale (furthest from center)")]
public Vector2 minScale = new Vector2(0.7f, 0.7f);
[Tooltip("Select the item to be in center on start.")]
public int startingIndex = -1;
[Tooltip("Stop scrolling past last element from inertia.")]
public bool stopMomentumOnEnd = true;
[Tooltip("Set Items out of center to not interactible.")]
public bool disableUnfocused = true;
[Tooltip("Button to go to the next page. (optional)")]
public GameObject scrollUpButton;
[Tooltip("Button to go to the previous page. (optional)")]
public GameObject scrollDownButton;
[Tooltip("Event fired when a specific item is clicked, exposes index number of item. (optional)")]
public IntEvent OnButtonClicked;
[Tooltip("Event fired when the focused item is Changed. (optional)")]
public IntEvent OnFocusChanged;
[HideInInspector]
public GameObject[] _arrayOfElements;
public int focusedElementIndex { get; private set; }
public string result { get; private set; }
private float[] distReposition;
private float[] distance;
//private int elementsDistance;
//Scrollable area (content of desired ScrollRect)
[HideInInspector]
public RectTransform scrollingPanel{ get { return scrollRect.content; } }
/// <summary>
/// Constructor when not used as component but called from other script, don't forget to set the non-optional properties.
/// </summary>
public UIVerticalScroller()
{
}
/// <summary>
/// Constructor when not used as component but called from other script
/// </summary>
public UIVerticalScroller(RectTransform center, RectTransform elementSize, ScrollRect scrollRect, GameObject[] arrayOfElements)
{
this.center = center;
this.elementSize = elementSize;
this.scrollRect = scrollRect;
_arrayOfElements = arrayOfElements;
}
/// <summary>
/// Awake this instance.
/// </summary>
public void Awake()
{
if (!scrollRect)
{
scrollRect = GetComponent<ScrollRect>();
}
if (!center)
{
Debug.LogError("Please define the RectTransform for the Center viewport of the scrollable area");
}
if (!elementSize)
{
elementSize = center;
}
if (_arrayOfElements == null || _arrayOfElements.Length == 0)
{
_arrayOfElements = new GameObject[scrollingPanel.childCount];
for (int i = 0; i < scrollingPanel.childCount; i++)
{
_arrayOfElements[i] = scrollingPanel.GetChild(i).gameObject;
}
}
}
/// <summary>
/// Recognises and resizes the children.
/// </summary>
/// <param name="startingIndex">Starting index.</param>
/// <param name="arrayOfElements">Array of elements.</param>
public void updateChildren(int startingIndex = -1, GameObject[] arrayOfElements = null)
{
// Set _arrayOfElements to arrayOfElements if given, otherwise to child objects of the scrolling panel.
if (arrayOfElements != null)
{
_arrayOfElements = arrayOfElements;
}
else
{
_arrayOfElements = new GameObject[scrollingPanel.childCount];
for (int i = 0; i < scrollingPanel.childCount; i++)
{
_arrayOfElements[i] = scrollingPanel.GetChild(i).gameObject;
}
}
// resize the elements to match elementSize rect
for (var i = 0; i < _arrayOfElements.Length; i++)
{
int j = i;
_arrayOfElements[i].GetComponent<Button>().onClick.RemoveAllListeners();
if (OnButtonClicked != null)
{
_arrayOfElements[i].GetComponent<Button>().onClick.AddListener(() => OnButtonClicked.Invoke(j));
}
RectTransform r = _arrayOfElements[i].GetComponent<RectTransform>();
r.anchorMax = r.anchorMin = r.pivot = new Vector2(0.5f, 0.5f);
r.localPosition = new Vector2(0, i * elementSize.rect.size.y);
r.sizeDelta = elementSize.rect.size;
}
// prepare for scrolling
distance = new float[_arrayOfElements.Length];
distReposition = new float[_arrayOfElements.Length];
focusedElementIndex = -1;
//scrollRect.scrollSensitivity = elementSize.rect.height / 5;
// if starting index is given, snap to respective element
if (startingIndex > -1)
{
startingIndex = startingIndex > _arrayOfElements.Length ? _arrayOfElements.Length - 1 : startingIndex;
SnapToElement(startingIndex);
}
}
public void Start()
{
if (scrollUpButton)
scrollUpButton.GetComponent<Button>().onClick.AddListener(() =>
{
ScrollUp();
});
if (scrollDownButton)
scrollDownButton.GetComponent<Button>().onClick.AddListener(() =>
{
ScrollDown();
});
updateChildren(startingIndex, _arrayOfElements);
}
public void Update()
{
if (_arrayOfElements.Length < 1)
{
return;
}
for (var i = 0; i < _arrayOfElements.Length; i++)
{
distReposition[i] = center.GetComponent<RectTransform>().position.y - _arrayOfElements[i].GetComponent<RectTransform>().position.y;
distance[i] = Mathf.Abs(distReposition[i]);
//Magnifying effect
Vector2 scale = Vector2.Max(minScale, new Vector2(1 / (1 + distance[i] * elementShrinkage.x), (1 / (1 + distance[i] * elementShrinkage.y))));
_arrayOfElements[i].GetComponent<RectTransform>().transform.localScale = new Vector3(scale.x, scale.y, 1f);
}
// detect focused element
float minDistance = Mathf.Min(distance);
int oldFocusedElement = focusedElementIndex;
for (var i = 0; i < _arrayOfElements.Length; i++)
{
_arrayOfElements[i].GetComponent<CanvasGroup>().interactable = !disableUnfocused || minDistance == distance[i];
if (minDistance == distance[i])
{
focusedElementIndex = i;
result = _arrayOfElements[i].GetComponentInChildren<Text>().text;
}
}
if (focusedElementIndex != oldFocusedElement && OnFocusChanged != null)
{
OnFocusChanged.Invoke(focusedElementIndex);
}
if (!UIExtensionsInputManager.GetMouseButton(0))
{
// scroll slowly to nearest element when not dragged
ScrollingElements();
}
// stop scrolling past last element from inertia
if (stopMomentumOnEnd
&& (_arrayOfElements[0].GetComponent<RectTransform>().position.y > center.position.y
|| _arrayOfElements[_arrayOfElements.Length - 1].GetComponent<RectTransform>().position.y < center.position.y))
{
scrollRect.velocity = Vector2.zero;
}
}
private void ScrollingElements()
{
float newY = Mathf.Lerp(scrollingPanel.anchoredPosition.y, scrollingPanel.anchoredPosition.y + distReposition[focusedElementIndex], Time.deltaTime * 2f);
Vector2 newPosition = new Vector2(scrollingPanel.anchoredPosition.x, newY);
scrollingPanel.anchoredPosition = newPosition;
}
public void SnapToElement(int element)
{
float deltaElementPositionY = elementSize.rect.height * element;
Vector2 newPosition = new Vector2(scrollingPanel.anchoredPosition.x, -deltaElementPositionY);
scrollingPanel.anchoredPosition = newPosition;
}
public void ScrollUp()
{
float deltaUp = elementSize.rect.height / 1.2f;
Vector2 newPositionUp = new Vector2(scrollingPanel.anchoredPosition.x, scrollingPanel.anchoredPosition.y - deltaUp);
scrollingPanel.anchoredPosition = Vector2.Lerp(scrollingPanel.anchoredPosition, newPositionUp, 1);
}
public void ScrollDown()
{
float deltaDown = elementSize.rect.height / 1.2f;
Vector2 newPositionDown = new Vector2(scrollingPanel.anchoredPosition.x, scrollingPanel.anchoredPosition.y + deltaDown);
scrollingPanel.anchoredPosition = newPositionDown;
}
[System.Serializable]
public class IntEvent:UnityEvent<int>
{
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b9f902652954d44beb3a916831d27435
timeCreated: 1449409849
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,318 @@
/// Credit BinaryX, SimonDarksideJ
/// Sourced from - http://forum.unity3d.com/threads/scripts-useful-4-6-scripts-collection.264161/page-2#post-1945602
/// Updated by SimonDarksideJ - removed dependency on a custom ScrollRect script. Now implements drag interfaces and standard Scroll Rect.
/// Updated by SimonDarksideJ - major refactoring on updating current position and scroll management
using System;
using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions
{
[RequireComponent(typeof(ScrollRect))]
[AddComponentMenu("Layout/Extensions/Vertical Scroll Snap")]
public class VerticalScrollSnap : ScrollSnapBase
{
private bool updated = true;
void Start()
{
_isVertical = true;
_childAnchorPoint = new Vector2(0.5f,0);
_currentPage = StartingScreen;
panelDimensions = gameObject.GetComponent<RectTransform>().rect;
UpdateLayout();
}
void Update()
{
updated = false;
if (!_lerp && _scroll_rect.velocity == Vector2.zero)
{
if (!_settled && !_pointerDown)
{
if (!IsRectSettledOnaPage(_screensContainer.anchoredPosition))
{
ScrollToClosestElement();
}
}
return;
}
else if (_lerp)
{
_screensContainer.anchoredPosition = Vector3.Lerp(_screensContainer.anchoredPosition, _lerp_target, transitionSpeed * (UseTimeScale ? Time.deltaTime : Time.unscaledDeltaTime));
if (Vector3.Distance(_screensContainer.anchoredPosition, _lerp_target) < 0.1f)
{
_screensContainer.anchoredPosition = _lerp_target;
_lerp = false;
EndScreenChange();
}
}
if (UseHardSwipe) return;
CurrentPage = GetPageforPosition(_screensContainer.anchoredPosition);
//If the container is moving check if it needs to settle on a page
if (!_pointerDown)
{
if (_scroll_rect.velocity.y > 0.01 || _scroll_rect.velocity.y < -0.01)
{
// if the pointer is released and is moving slower than the threshold, then just land on a page
if (IsRectMovingSlowerThanThreshold(0))
{
ScrollToClosestElement();
}
}
}
}
private bool IsRectMovingSlowerThanThreshold(float startingSpeed)
{
return (_scroll_rect.velocity.y > startingSpeed && _scroll_rect.velocity.y < SwipeVelocityThreshold) ||
(_scroll_rect.velocity.y < startingSpeed && _scroll_rect.velocity.y > -SwipeVelocityThreshold);
}
public void DistributePages()
{
_screens = _screensContainer.childCount;
_scroll_rect.verticalNormalizedPosition = 0;
float _offset = 0;
float _dimension = 0;
Rect panelDimensions = gameObject.GetComponent<RectTransform>().rect;
float currentYPosition = 0;
var pageStepValue = _childSize = (int)panelDimensions.height * ((PageStep == 0) ? 3 : PageStep);
for (int i = 0; i < _screensContainer.transform.childCount; i++)
{
RectTransform child = _screensContainer.transform.GetChild(i).gameObject.GetComponent<RectTransform>();
currentYPosition = _offset + i * pageStepValue;
child.sizeDelta = new Vector2(panelDimensions.width, panelDimensions.height);
child.anchoredPosition = new Vector2(0f, currentYPosition);
child.anchorMin = child.anchorMax = child.pivot = _childAnchorPoint;
}
_dimension = currentYPosition + _offset * -1;
_screensContainer.GetComponent<RectTransform>().offsetMax = new Vector2(0f, _dimension);
}
/// <summary>
/// Add a new child to this Scroll Snap and recalculate it's children
/// </summary>
/// <param name="GO">GameObject to add to the ScrollSnap</param>
public void AddChild(GameObject GO)
{
AddChild(GO, false);
}
/// <summary>
/// Add a new child to this Scroll Snap and recalculate it's children
/// </summary>
/// <param name="GO">GameObject to add to the ScrollSnap</param>
/// <param name="WorldPositionStays">Should the world position be updated to it's parent transform?</param>
public void AddChild(GameObject GO, bool WorldPositionStays)
{
_scroll_rect.verticalNormalizedPosition = 0;
GO.transform.SetParent(_screensContainer, WorldPositionStays);
InitialiseChildObjectsFromScene();
DistributePages();
if (MaskArea) UpdateVisible();
SetScrollContainerPosition();
}
/// <summary>
/// Remove a new child to this Scroll Snap and recalculate it's children
/// *Note, this is an index address (0-x)
/// </summary>
/// <param name="index">Index element of child to remove</param>
/// <param name="ChildRemoved">Resulting removed GO</param>
public void RemoveChild(int index, out GameObject ChildRemoved)
{
RemoveChild(index, false, out ChildRemoved);
}
/// <summary>
/// Remove a new child to this Scroll Snap and recalculate it's children
/// *Note, this is an index address (0-x)
/// </summary>
/// <param name="index">Index element of child to remove</param>
/// <param name="WorldPositionStays">If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before</param>
/// <param name="ChildRemoved">Resulting removed GO</param>
public void RemoveChild(int index, bool WorldPositionStays, out GameObject ChildRemoved)
{
ChildRemoved = null;
if (index < 0 || index > _screensContainer.childCount)
{
return;
}
_scroll_rect.verticalNormalizedPosition = 0;
Transform child = _screensContainer.transform.GetChild(index);
child.SetParent(null, WorldPositionStays);
ChildRemoved = child.gameObject;
InitialiseChildObjectsFromScene();
DistributePages();
if (MaskArea) UpdateVisible();
if (_currentPage > _screens - 1)
{
CurrentPage = _screens - 1;
}
SetScrollContainerPosition();
}
/// <summary>
/// Remove all children from this ScrollSnap
/// </summary>
/// <param name="ChildrenRemoved">Array of child GO's removed</param>
public void RemoveAllChildren(out GameObject[] ChildrenRemoved)
{
RemoveAllChildren(false, out ChildrenRemoved);
}
/// <summary>
/// Remove all children from this ScrollSnap
/// </summary>
/// <param name="WorldPositionStays">If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before</param>
/// <param name="ChildrenRemoved">Array of child GO's removed</param>
public void RemoveAllChildren(bool WorldPositionStays, out GameObject[] ChildrenRemoved)
{
var _screenCount = _screensContainer.childCount;
ChildrenRemoved = new GameObject[_screenCount];
for (int i = _screenCount - 1; i >= 0; i--)
{
ChildrenRemoved[i] = _screensContainer.GetChild(i).gameObject;
ChildrenRemoved[i].transform.SetParent(null, WorldPositionStays);
}
_scroll_rect.verticalNormalizedPosition = 0;
CurrentPage = 0;
InitialiseChildObjectsFromScene();
DistributePages();
if (MaskArea) UpdateVisible();
}
private void SetScrollContainerPosition()
{
_scrollStartPosition = _screensContainer.anchoredPosition.y;
_scroll_rect.verticalNormalizedPosition = (float)(_currentPage) / (_screens - 1);
OnCurrentScreenChange(_currentPage);
}
/// <summary>
/// used for changing / updating between screen resolutions
/// </summary>
public void UpdateLayout()
{
_lerp = false;
DistributePages();
if (MaskArea) UpdateVisible();
SetScrollContainerPosition();
OnCurrentScreenChange(_currentPage);
}
private void OnRectTransformDimensionsChange()
{
if (_childAnchorPoint != Vector2.zero)
{
UpdateLayout();
}
}
private void OnEnable()
{
InitialiseChildObjectsFromScene();
DistributePages();
if (MaskArea)
UpdateVisible();
if (JumpOnEnable || !RestartOnEnable)
SetScrollContainerPosition();
if (RestartOnEnable)
GoToScreen(StartingScreen);
}
/// <summary>
/// Release screen to swipe
/// </summary>
/// <param name="eventData"></param>
public override void OnEndDrag(PointerEventData eventData)
{
if (updated)
{
return;
}
// to prevent double dragging, only act on EndDrag once per frame
updated = true;
_pointerDown = false;
if (_scroll_rect.vertical)
{
if (UseSwipeDeltaThreshold && Math.Abs(eventData.delta.y) < SwipeDeltaThreshold)
{
ScrollToClosestElement();
}
else
{
var distance = Vector3.Distance(_startPosition, _screensContainer.anchoredPosition);
if (UseHardSwipe)
{
_scroll_rect.velocity = Vector3.zero;
if (distance > FastSwipeThreshold)
{
if (_startPosition.y - _screensContainer.anchoredPosition.y > 0)
{
NextScreen();
}
else
{
PreviousScreen();
}
}
else
{
ScrollToClosestElement();
}
}
else
{
if (UseFastSwipe && distance < panelDimensions.height + FastSwipeThreshold && distance >= 1f)
{
_scroll_rect.velocity = Vector3.zero;
if (_startPosition.y - _screensContainer.anchoredPosition.y > 0)
{
if (_startPosition.y - _screensContainer.anchoredPosition.y > _childSize / 3)
{
ScrollToClosestElement();
}
else
{
NextScreen();
}
}
else
{
if (_startPosition.y - _screensContainer.anchoredPosition.y > -_childSize / 3)
{
ScrollToClosestElement();
}
else
{
PreviousScreen();
}
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e9dccd2761b104249a8eb0636b550188
timeCreated: 1440928417
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: