publicclassTempListView : MonoBehaviour { #if UNITY_EDITOR #endif // check when prefab change // [OnValueChanged("OnPrefabChanged")] [SerializeField, Header("place holder, the listview should only contains one type of element")] TempListElementUI m_elementPrefab; RectTransform Container => transform as RectTransform; [SerializeField, ReadOnly] List<TempListElementUI> m_actualUsedComponents = new List<TempListElementUI>(0);
publicint Count => m_actualUsedComponents.Count; public TempListElementUI this[int index] => m_actualUsedComponents[index]; public IReadOnlyList<TempListElementUI> ElementList => m_actualUsedComponents;
public TempListElementUI Add() { TempListElementUI element = InternalAdd(); m_actualUsedComponents.Add(element); element.Show(); return element; }
publicvoidClear() { TempListElementUI element = null; while (m_actualUsedComponents.Count > 0) { element = m_actualUsedComponents[m_actualUsedComponents.Count - 1]; m_actualUsedComponents.RemoveAt(m_actualUsedComponents.Count - 1); InternalRemove(element); } }
publicvoidRemove(TempListElementUI instance) { if (m_actualUsedComponents.Remove(instance)) { instance.Hide(); } else { Debug.LogError($"listview_{this.gameObject.name} does not contains {instance.ElementRectTransform.name}, remove failed"); } }
publicvoidRemoveAt(int index) { if (index < 0 || index >= m_actualUsedComponents.Count) { Debug.LogError($"{index} is invalid for SimpleListView", this.gameObject); return; }
///<summary> /// ///</summary> ///<returns>-1 means element is not in the list</returns> publicintIndexOf(TempListElementUI instance) { try { return m_actualUsedComponents.IndexOf(instance); } catch (ArgumentOutOfRangeException) { return-1; } }
protectedvirtual TempListElementUI InternalAdd() { // can apply object pool here if (Application.isEditor && !Application.isPlaying) return UnityEditor.PrefabUtility.InstantiatePrefab(m_elementPrefab, Container) as TempListElementUI; return Instantiate(m_elementPrefab, Container); }
protectedvirtualvoidInternalRemove(TempListElementUI element) { // can apply object pool here if (element == null) return; element.Hide(); if (Application.isEditor && !Application.isPlaying) GameObject.DestroyImmediate(element.ElementRectTransform.gameObject); else GameObject.Destroy(element.ElementRectTransform.gameObject); }
#if UNITY_EDITOR
privatevoidOnTransformChildrenChanged() { if (Application.isEditor && !Application.isPlaying) FindPrefabInstances(); }
privatevoidOnPrefabChanged() { // remove pre objects int amount = m_actualUsedComponents.Count; for (int i = 0; i < m_actualUsedComponents.Count; i++) { GameObject.DestroyImmediate(m_actualUsedComponents[i].ElementRectTransform.gameObject); }
if (m_elementPrefab != null) { for (int i = 0; i < amount; i++) { RectTransform rectTransform = (RectTransform)UnityEditor.PrefabUtility.InstantiatePrefab(m_elementPrefab, Container); m_actualUsedComponents[i] = (rectTransform.GetComponent<TempListElementUI>()); } } else { m_actualUsedComponents.Clear(); } }
///<summary> /// Retrieves prefab instances in the transform ///</summary> [ContextMenu("find prefab instances")] privatevoidFindPrefabInstances() { bool hasPrefab = !(m_elementPrefab == null); TempListElementUI elementPrefab = m_elementPrefab.GetComponent<TempListElementUI>(); m_actualUsedComponents.Clear(); List<GameObject> toDeleteObjectList = new List<GameObject>(); foreach (Transform child in Container) { TempListElementUI childElement = child.GetComponent<TempListElementUI>(); if (childElement == null) { toDeleteObjectList.Add(child.gameObject); continue; }
if (hasPrefab) { GameObject detectPrefabGo = UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(child.gameObject); TempListElementUI detectPrefab = (detectPrefabGo == null) ? null : detectPrefabGo.GetComponent<TempListElementUI>(); if (elementPrefab == detectPrefab) { // same source prefab m_actualUsedComponents.Add(childElement); } else { // different source prefab, delete this one toDeleteObjectList.Add(child.gameObject); } } elseif (UnityEditor.PrefabUtility.IsAnyPrefabInstanceRoot(child.gameObject)) { // find the first prefab GameObject prefab = UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(child.gameObject); m_elementPrefab = prefab.GetComponent<TempListElementUI>(); m_actualUsedComponents.Add(childElement); hasPrefab = true; } }
for (int i = 0; i < toDeleteObjectList.Count; i++) { if (Application.isPlaying) GameObject.Destroy(toDeleteObjectList[i]); else GameObject.DestroyImmediate(toDeleteObjectList[i]); } }
[ContextMenu("editor time add")] privatevoidEditorTimeAdd() { if (Application.isPlaying) return; if (m_elementPrefab == null) { Debug.LogError("listview is missing element prefab"); return; } TempListElementUI spawnObject = (TempListElementUI)UnityEditor.PrefabUtility.InstantiatePrefab(m_elementPrefab, Container); m_actualUsedComponents.Add(spawnObject); }
[ContextMenu("editor time clear")] privatevoidEditorTimeClear() { if (Application.isPlaying) return; // remove pre objects TempListElementUI[] preObjects = Container.GetComponentsInChildren<TempListElementUI>(); for (int i = 0; i < preObjects.Length; i++) { GameObject.DestroyImmediate(preObjects[i].ElementRectTransform.gameObject); } m_actualUsedComponents.Clear(); }
[ContextMenu("test print elements")] privatevoidTestPrintListElements() { if (m_actualUsedComponents != null) { StringBuilder printText = new StringBuilder($"temp list view children_{m_actualUsedComponents.Count} :\n"); for (int i = 0; i < m_actualUsedComponents.Count; i++) { printText.AppendLine(m_actualUsedComponents[i].ElementRectTransform.name); } Debug.Log(printText); } }
#endif }
publicclassBoundlessScrollRectController : UIBehaviour { /* too much code, I will just show the draw */
privatevoidDrawContentItem() { IReadOnlyList<TempListElementUI> elementList = ElementList; int dataCount = m_simulatedDataCount; // TODO @Hiko use a general calculation later bool test = m_content.anchorMin != Vector2.up || m_content.anchorMax != Vector2.up || m_content.pivot != Vector2.up; if (test) { m_content.anchorMin = Vector2.up; m_content.anchorMax = Vector2.up; m_content.pivot = Vector2.up; } Vector3 dragContentAnchorPostion = m_content.anchoredPosition; Vector3 contentMove = dragContentAnchorPostion - SomeUtils.GetOffsetLocalPosition(m_content, SomeUtils.UIOffsetType.TopLeft); Vector2 itemSize = m_gridLayoutGroup.CellSize, spacing = m_gridLayoutGroup.Spacing;
// need to know the moving direction, then adjust it to prevent wrong draw float xMove = contentMove.x < 0 ? (-contentMove.x - padding.horizontal) : 0; xMove = Mathf.Clamp(xMove, 0.0f, Mathf.Abs(xMove)); float yMove = contentMove.y > 0 ? (contentMove.y - padding.vertical) : 0; yMove = Mathf.Clamp(yMove, 0.0f, Mathf.Abs(yMove));
// the column index of the top left item int tempColumnIndex = Mathf.FloorToInt((xMove + spacing.x) / (itemSize.x + spacing.x)); if (xMove % (itemSize.x + spacing.x) - itemSize.x > spacing.x) tempColumnIndex = Mathf.Clamp(tempColumnIndex - 1, 0, tempColumnIndex);
// the row index of the top left item int tempRowIndex = Mathf.FloorToInt((yMove + spacing.y) / (itemSize.y + spacing.y)); if (yMove % (itemSize.y + spacing.y) - itemSize.y > spacing.y) tempRowIndex = Mathf.Clamp(tempRowIndex - 1, 0, tempRowIndex);
Vector2Int rowTopLeftItemIndex = new Vector2Int(tempRowIndex, tempColumnIndex);
// x -> element amount on horizontal axis // y -> element amount on vertical axis Vector2Int contentRowColumnSize = new Vector2Int(rowDataCount, columnDataCount);
// deal with content from left to right (simple case) int dataIndex = 0, uiItemIndex = 0; Vector3 rowTopLeftPosition = new Vector3(padding.left, -padding.top, 0.0f), itemTopLeftPosition = Vector3.zero; for (int columnIndex = 0; columnIndex < m_viewItemCountInColumn; columnIndex++) { if (columnIndex + rowTopLeftItemIndex.x == columnDataCount) break;
// apply this on the target shows actual data public interface ISetupable<TData> { voidSetup(TData data); }
///<summary> /// target component should inherit from ISetupable ///</summary> ///<param name="component"></param> ///<param name="data"></param> ///<typeparam name="TData"></typeparam> publicstaticvoid ISetup<TComponent, TData>(this Component component, TData data) where TComponent : Component { // I guess this check should be fine :( if (component is ISetupable<TData> target) { target.Setup(data); return; } UnityEngine.Debug.LogError($"component_{component.name} is not a setupable"); }
publicclassRaycastTarget : Graphic { // Additionally, If you need a Raycast target that is not a rectangle, you can implement bool Raycast(Vector2 sp, Camera eventCamera) method from Graphic. publicoverridevoidSetMaterialDirty() { return; } publicoverridevoidSetVerticesDirty() { return; } /// Probably not necessary since the chain of calls `Rebuild()`->`UpdateGeometry()`->`DoMeshGeneration()`->`OnPopulateMesh()` won't happen; so here really just as a fail-safe. protectedoverridevoidOnPopulateMesh(VertexHelper vh) { vh.Clear(); return; }
#if UNITY_EDITOR
privatevoidOnDrawGizmos() { if (!this.enabled) return RectTransform rectTransform = this.transform as RectTransform; Color wireColor = Color.yellow; if (this.isActiveAndEnabled) wireColor.a *= 0.7f // Padding to be applied to the masking // X = Left, Y = Bottom, Z = Right, W = Top // if you wanna make it bigger, then the all value shouble be negative Vector4 padding = this.raycastPadding * -1.0f; Matrix4x4 localToWorld = rectTransform.localToWorldMatrix Vector3 topLeft = SomeUtils.GetOffsetLocalPosition(rectTransform, SomeUtils.UIOffsetType.TopLeft); Vector3 topRight = SomeUtils.GetOffsetLocalPosition(rectTransform, SomeUtils.UIOffsetType.TopRight); Vector3 bottomLeft = SomeUtils.GetOffsetLocalPosition(rectTransform, SomeUtils.UIOffsetType.BottomLeft); Vector3 bottomRight = SomeUtils.GetOffsetLocalPosition(rectTransform, SomeUtils.UIOffsetType.BottomRight) topLeft = localToWorld.MultiplyPoint(topLeft + (Vector3.left * padding.x) + (Vector3.up * padding.w)); topRight = localToWorld.MultiplyPoint(topRight + (Vector3.right * padding.z) + (Vector3.up * padding.w)); bottomLeft = localToWorld.MultiplyPoint(bottomLeft + (Vector3.left * padding.x) + (Vector3.down * padding.y)); bottomRight = localToWorld.MultiplyPoint(bottomRight + (Vector3.right * padding.z) + (Vector3.down * padding.y)) Color tempColor = Gizmos.color; Gizmos.color = wireColor Gizmos.DrawLine(topLeft, topRight); Gizmos.DrawLine(topLeft, bottomLeft); Gizmos.DrawLine(bottomRight, topRight); Gizmos.DrawLine(bottomRight, bottomLeft); Gizmos.color = tempColor }
git rebase -i HEAD~X # X is the number of commits to go back # Move to the line of your commit, change pick into edit, # then change your commit message: git commit --amend # Finish the rebase with: git rebase --continue
// to find the reference of the target unity object
publicstaticclassFindReferencesInProject { publicstaticboolTryFindReferenceOfUobject(UnityEngine.Object uobject) { bool result = false; string path = AssetDatabase.GetAssetPath(uobject); if (AssetDatabase.IsValidFolder(path)) { // dun do it for a folder :) returnfalse; }
FindObjectReference(uobject);
return result; }
// why the order is like this? [MenuItem("Assets/Find References In Project")] publicstaticvoidFindSelectedObjectReferences() { if (null != Selection.activeObject) { string path = AssetDatabase.GetAssetPath(Selection.activeObject); if (!AssetDatabase.IsValidFolder(path)) { FindObjectReference(Selection.activeObject); } } }
privatestaticvoidFindObjectReference(UnityEngine.Object uobject) { System.Diagnostics.Stopwatch stopWatcher = new System.Diagnostics.Stopwatch(); stopWatcher.Start();
string targetPath = AssetDatabase.GetAssetPath(uobject); List<string> referencesPaths = new List<string>(); Dictionary<string, List<string>> referenceCache = new Dictionary<string, List<string>>(); string[] assetGuids = AssetDatabase.FindAssets("");
for (int i = 0; i < assetGuids.Length; i++) { string assetPath = AssetDatabase.GUIDToAssetPath(assetGuids[i]); string[] dependencies = AssetDatabase.GetDependencies(assetPath, false);
for (int j = 0; j < dependencies.Length; j++) { if (dependencies[j] == targetPath) { referencesPaths.Add(assetPath); } } }
privatevoidDrawContent() { // only use half space of this shit window m_viewportRect = new Rect(0.0f, 0.0f, this.position.width - SCROLLER_WIDTH, this.position.height * 0.8f); m_listRect = new Rect(0.0f, 0.0f, m_viewportRect.width, m_viewportRect.height); int elementCount = m_targetPaths.Length;
if (elementCount < 1) return;
GUI.BeginClip(m_listRect); // to clip the overflow stuff int indexOffset = Mathf.FloorToInt(m_scrollPosition / ELEMENT_HEIGHT); int showCount = Mathf.CeilToInt(m_listRect.height / ELEMENT_HEIGHT); showCount = showCount > elementCount ? elementCount : showCount; float startPosY = (indexOffset * ELEMENT_HEIGHT) - m_scrollPosition;
for (int i = 0; i < showCount; i++) { Rect elementRect = new Rect(0, 0 + startPosY + i * ELEMENT_HEIGHT, m_listRect.width, ELEMENT_HEIGHT); DrawElement(elementRect, indexOffset + i); } GUI.EndClip(); }
privatevoidDrawScroller() { // draw scroller on the right correctly int elementCount = m_targetPaths.Length; float fullElementHeight = elementCount * ELEMENT_HEIGHT;
Rect scrollbarRect = new Rect(m_viewportRect.x + m_viewportRect.width, m_viewportRect.y, SCROLLER_WIDTH, m_viewportRect.height); m_scrollPosition = Mathf.Max(0, GUI.VerticalScrollbar(scrollbarRect, m_scrollPosition, m_listRect.height, 0, Mathf.Max(fullElementHeight, m_listRect.height)));
publicvoidSetData(List<TempDrawData> data) { if (null == m_data) m_data = new List<TempDrawData>(); else m_data.Clear();
m_data.AddRange(data); }
privatevoidDrawTempElement(Rect elementRect, int dataIndex) { Rect iconRect = new Rect(elementRect.x, elementRect.y, ELEMENT_ICON_SIZE, elementRect.height); if (m_data[dataIndex].IconTag == 0) GUI.Label(iconRect, m_infoIconSmall); else GUI.Label(iconRect, m_warningIconSmall);
Rect labelRect = new Rect(elementRect.x + ELEMENT_ICON_SIZE, elementRect.y, elementRect.width - ELEMENT_ICON_SIZE, elementRect.height); GUI.Label(labelRect, $"Tag: {m_data[dataIndex].IconTag} ; Message: {m_data[dataIndex].TempMessage} ;"); }
privatevoidDrawTempRect() { // only use half space of this shit window Rect viewportRect = new Rect(0.0f, 0.0f, this.position.width, this.position.height * 0.5f);
float scrollbarWidth = GUI.skin.verticalScrollbar.fixedWidth; Rect scrollbarRect = new Rect(viewportRect.x + viewportRect.width - scrollbarWidth, viewportRect.y, scrollbarWidth, viewportRect.height); Rect currentRect = new Rect(0.0f, 0.0f, viewportRect.width - scrollbarWidth, viewportRect.height); float viewportHeight = viewportRect.height; int elementCount = m_data.Count;
GUI.BeginClip(currentRect); // to clip the overflow stuff int indexOffset = Mathf.FloorToInt(m_scrollPosition / ELEMENT_HEIGHT); int showCount = Mathf.CeilToInt(currentRect.height / ELEMENT_HEIGHT); showCount = showCount > elementCount ? elementCount : showCount; float startPosY = (indexOffset * ELEMENT_HEIGHT) - m_scrollPosition;
for (int i = 0; i < showCount; i++) { Rect elementRect = new Rect(0, 0 + startPosY + i * ELEMENT_HEIGHT, currentRect.width, ELEMENT_HEIGHT); DrawTempElement(elementRect, indexOffset + i); } GUI.EndClip();
// do stuff for scroller float fullElementHeight = elementCount * ELEMENT_HEIGHT; m_scrollPosition = Mathf.Max(0, GUI.VerticalScrollbar(scrollbarRect, m_scrollPosition, currentRect.height, 0, Mathf.Max(fullElementHeight, currentRect.height)));