When I searching some articles about MVC and MVVM, I see this talk. Basiclly they does same stuff as my current project, but we still have some differences on our implementation. I’d like to share it and take some notes :D
Here is the talk abour UGUI implementation. video. ppt. temp.
Main note
Architecture in < Lost Survivor >
Artist works directly in Unity (Creating prefabs, setup aniamtions; well that’s what I my current project does, so I guess they did the same)
Dev works on GameLogic and UILogic
Dev/Art work decoupled
Dev/Art work parallel (This happens a lot in iteration)
This is the pattern they use for their project. The one thing that I cant understand is that the View will receive events from MessageBus. IDK why they did like this. For me, I prefer to let view only receive direct events from VM, bind to many stuff to MessageBus will make it slow.
I’m sure this pattern is kind of useful. I use this a lot when I did the first version of some UI pages. But later the Controller become a giant class. And also I think the View and Controller in this pattern is not very reusable.
+------------------------------+ | Model Character | +----------------------------------------------- | | int AmountXp; | | | int AttributeTypeA; | | | int AttributeTypeB; | | | | | | | | +------------------------------+ +------------------------------+ +----------------------|-------+ | Character View | data binding <-> | View Model | -------------------------------- notifications <- -------------------------------- | Text XP; | command -> |int AmountXP; | | Text Attribute; ----------------------+int WeaponATK; | | Button ReAllocate; | |int WeaponLevel; | +------------------------------+ | int WeaponATK; | | | | Model Weapon | | | | | -------------------------------- +------------------------------+ +----------------------|-------+ | int ATK; | | | int Level; | +---------------- | +------------------------------+
ViewModel serves the View
One ViewModel per View
Based on Data Binding
After a few iterations, I find this pattern is also a good solutions. And I feel that we can even use CompositePattern in ViewModel to create big ViewModel for some big views(resuable).
In my current project, for the UI-View, we create some basic view that can receive similar types of data. And the good thing from this pattern is that the VM can send notificaiton to View to update, then I dun need to have lots of update method from Controller to update different data (cuz some data update need aniamtion, I cant just update all).
Problems
Previously it says that separating UIPrefabs creation and UILogic code may solve “Not my problem attitude”. But actually not. In production state, everyone is busy, and we are iterating View-Scripts and VM-Scripts together. Sometimes it is diffcult to tell that is code issues or setup issues. Unless we have solid View-Scripts.
Summary
This talk has lots of similiar content as the DivisionUI talk. And I also realise that the game development has lots of unknow shit. It’s very difficult to have some resuable components that always works. Even the thougts are similar, the implementation can be different. A solution can be that having some solid, simple, basic and resuable compoents.
publicvoidSetVideo(string path) { m_player.source = VideoSource.Url; // I guess this process may not be finished now. m_player.url = path; m_player.Prepare(); }
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.
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 }