From the experience of studying various starter whales, I can assume that at the moment developers using Unity, in general, very rarely use any generally accepted programming patterns. For myself, I see the reason for this in the short life of the game as a product, plus the specificity of Unity itself. One, two - and in production, so to speak.
If we talk about how to organize the user interface, for myself, I decided that the optimal work model would be something similar to MVVM, plus the Team template.
In the scene we create a user interface, then a manager object, which will be responsible for integrating the interface with logic. Let's call it, for example, GUIImpl . The class must be MonoBehaviour successor in order for us to work with it in the editor. In the example below, I use NGUI for the interface itself.
The next step is to create a class description of the graphic element, for example, a panel. I make this class intrinsic to GUIImpl , but it's not for GUIImpl .
[System.Serializable] public class MenuPanel : GUIMenuBase { public GameObject SettingsButton; public GameObject CancelButton; public GameObject ExitButton; public GameObject ResetButton; public override void Init() { associateButton(NewGameButton, new NewGameCommand(this)); associateButton(SettingsButton, new SettingsCommand(this)); associateButton(CancelButton, new CancelCommand(this)); associateButton(ExitButton, new ExitCommand(this)); } public override void Show() { base.Show(); } public override void Hide() { base.Hide(); } }
As you can see, the panel class is a descendant of GUIMenuBase . This class is specific to your user interface. I will give my basic functionality.
[System.Serializable] public abstract class GUIMenuBase : IGUIMenu { public GameObject Instance; protected Dictionary<GameObject, ICommand> associations = new Dictionary<GameObject, ICommand>(); protected List<GameObject> activeButtons; public GUIMenuBase() { } public abstract void Init(); public GameObject GetGOInstance() { return Instance; } public virtual void Show() { GUILogic.ShowMenu(this); } public virtual void Hide() { GUILogic.HideMenu(this); } public virtual bool CanShow(GameObject guiItem) { return true; } public virtual bool IsShown(GameObject guiItem) { return true; } public virtual bool IsAvalibale(GameObject guiItem) { return true; } public virtual bool CanButtonClick(GameObject button) { if (!associations.ContainsKey(button)) return false; return associations[button].CanExecute(button); } public virtual void ButtonClick(GameObject button) { if (!associations.ContainsKey(button)) return; if (associations[button].CanExecute(button)) associations[button].Execute(button); } public virtual void OnSourceChanged() { foreach(var item in associations) { UIButton buttonScript = item.Key.GetComponent<UIButton>(); if (buttonScript != null) { buttonScript.isEnabled = item.Value.CanExecute(item.Key); buttonScript.enabled = true; } } } protected virtual void adjustPanelButtons() { } protected virtual void associateButton(GameObject guiItem, ICommand command) { associate(guiItem, command); UIEventListener.Get(guiItem).onClick += ButtonClick; } protected virtual void associate(GameObject guiItem, ICommand command) { associations.Add(guiItem, command); } protected virtual void removeAssociation(GameObject guiItem) { if (associations.ContainsKey(guiItem)) associations.Remove(guiItem); } }
Iguimenu
public interface IGUIMenu { void Show(); void Hide(); bool CanShow(GameObject guiItem); bool IsShown(GameObject guiItem); bool IsAvalibale(GameObject guiItem); bool CanButtonClick(GameObject button); void ButtonClick(GameObject button); void OnSourceChanged(); }
IGUIMenu contains methods for working with various elements. I brought the interface to work only with buttons. Next, we implement the action by clicking on the buttons.
public class NewGameCommand : CommandBase { public NewGameCommand(GUIMenuBase aPanel) : base(aPanel) {} public override void Execute(GameObject button) { if (!CanExecute (button)) return; DatabaseManagerFactory.GetDefaultDatabaseManager().Reset(); Application.LoadLevel(Application.loadedLevel); } } public abstract class CommandBase : ICommand { protected GUIMenuBase panel; public CommandBase(GUIMenuBase aPanel) { panel = aPanel; } public virtual bool CanExecute(GameObject button) { return true; } public virtual void Execute(GameObject button) { } } public interface ICommand { void Execute (GameObject context); bool CanExecute (GameObject context); }
Basically, that's all. It remains only to implement the class GUILogic , which contains the logic of the panels. Then open the editor, find our object to control the user interface, and assign the elements of the various panels to the corresponding fields. Thus, for each panel should be its own class, which contains all the necessary fields. Please note the public GameObject Instance; field public GameObject Instance; class GUIMenuBase . Instance is an object of the panel itself. For most panel operations, we need its root element.