Use basic widgets, and use them to build functionalities.
Window
2D and 3D (anchoring functionalities, pixels or percentage; prepare some options for different auto scaling and apply to different UI widgets/graphic and situations).
Vertical Stack (stack the (children)widgets)
Layering
Text
JIT font generaion (wtf, keep memory down)
Fich text formatting lib to build customize text(icon, color …).
Auto clip (no idea how to do it)
Image
Use color/gradient more. (looks good with vector graphic)
Texture and loose image(ikd this, no border image?) into sprite sheet.
Stack container
For sorting elements.
Spacing and padding (how to do it with stack)
Invert for quick right to left language fixes (wtf)
Scroll Box
Fixed size(viewport size) with a virtual inner space(content).
7 Graphics (nodes)
Text with effects.
Images(basic) with more shapes, flat or curved.
Lines(strait, curved list?, 3d)
Points, single point or large squares(for effects?)
Shapes
Sector (why? for their art style?)
Custom graphics
Simply Ui workflow
Complex and powerful (lots of resuable compounds, and threading stuff)
Documentation and examples(I guess examples are better and save time)
For coder
Simple code; Less code, less bugs
Simple Tools; Do not overthink. Simple base, complex behaviour.
For UI artist/designer
Communicate to avoid merge conflicts, get ahead of it (cuz yaml merge wont help u all the time).
// get state object EditingState state = GUIUtility.GetStateObject(typeof(EditingState), GUIUtility.GetControlID(FocusType.Passive)) as EditingState;
using (new EditorGUILayout.HorizontalScope(style)) { Rect editButtonRect = new Rect(position.x, position.y, 16f, 16f); if (GUI.Button(editButtonRect, "E")) { // well since it's a class we dun need to set it back :> state.IsEditing = !state.IsEditing; }
if (GUILayout.Button("X", GUILayout.Width(18))) { state.IsEditing = !state.IsEditing; } if (state.IsEditing) { // show text field tagStr = EditorGUILayout.TextField(tagStr); } else { // show lable EditorGUILayout.LabelField(tagStr); } } } }
Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.
[Serializable] publicstruct LocalizationStringParmPack { [CustomValueDrawer("DrawPreviewText")] public LocalizedString TextLoc; public LocalizationParamsManager.ParamValue[] ParmsArray; publicoverridestringToString() { returnthis.GetRawEnglishString(); }
#if UNITY_EDITOR
static GUIStyle m_previewStyle; static GUIStyle PreviewStyle { get { if (m_previewStyle == null) { m_previewStyle = new GUIStyle(EditorStyles.textArea); m_previewStyle.wordWrap = true; } return m_previewStyle; } }
// use hacky stuff to get selected new lockey staticstring s_wtfKey = string.Empty; staticbool s_hasChanged = false; LocalizedString DrawPreviewText(LocalizedString value, GUIContent label) { using (new EditorGUILayout.VerticalScope(UnityEditor.EditorStyles.helpBox)) { // draw lockey using (new EditorGUILayout.HorizontalScope()) { if (label != null) { EditorGUILayout.LabelField(label, GUILayout.Width(EditorGUIUtility.labelWidth)); }
// weird stuff happin in dropdown if (EditorGUILayout.DropdownButton(new GUIContent(value.mTerm), FocusType.Keyboard)) { var terms = LocalizationManager.GetTermsList(); var selector = new GenericSelector<string>("Select Term", terms); selector.EnableSingleClickToSelect(); selector.SelectionConfirmed += s => { if (s.Any()) { s_wtfKey = s.First(); // WEIRD STUFF HERE /* if I do LocalizedString next = new LocalizedString(s.First()); and return 'next' and the end of method, it just doesnt work!!! cuz the GenericSelector is actually a editor window, and */ s_hasChanged = true; } }; selector.ShowInPopup(); }
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"); }