diff --git a/Documentation/Information.meta b/Documentation/Information.meta new file mode 100644 index 0000000..0b4d184 --- /dev/null +++ b/Documentation/Information.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b31819f8abae8d6498ef952fa251bb85 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Documentation/Information/ClassViewer.txt b/Documentation/Information/ClassViewer.txt new file mode 100644 index 0000000..8d4a0d4 --- /dev/null +++ b/Documentation/Information/ClassViewer.txt @@ -0,0 +1,70 @@ +Runtime + Core + Architecture + Game -> This is a red word and bold green. Go to TEST https://github.com/AliJimpa/RealMethod/wiki/Assets. + World -> just test text ColortTEst Without link + GameConfig + GameService + ManagerContracts + ServiceContracts + WorldSceneConfig + ProjectSettingAsset -> ProjectSettingAsset Should be in Resource>RealMethod>RealMethodSetting.asset and all RealMethod setting handeled by this asset and this name. + Attributes + ButtonAttribute + ColorFieldAttribute + ConditionalHideAttribute + DropdownAttribute + DropdownFromArrayAttribute + DropdownFromDictionaryAttribute + EnumDescriptionAttribute + ExpandableAttribute + HelpBoxAttribute + HideInInspectorByEnumAttribute + InterfaceValidationDrawer + LayerAttribute + ListToPopupAttribute + MinMaxRangeAttribute + ReadOnlyAttribute + SeparatorAttribute + ShowInInspectorByEnumAttribute + ShowOnlyAttribute + ShowTypeAttribute + TagSelectorAttribute + Definitions + Assets + Identifier + Prefab + Tick + ProjectSetting + ProjectSettingAsset + Library + Extension + Interfaces + SharedScripts + Utilities + Vendor + Pattern + Components + DataAssets + DesignPatterns + Managers + Services + ReadySet + Commands + Components + DefaultsClass + Managers + Presets + Services + Toolkit + Ability + Actor + CSVFile + CurveViewer + Interaction + Inventory + PCG + Pickup + RPG + Tutorial + Upgrade diff --git a/Documentation/Information/ClassViewer.txt.meta b/Documentation/Information/ClassViewer.txt.meta new file mode 100644 index 0000000..9d6bfdd --- /dev/null +++ b/Documentation/Information/ClassViewer.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 79055230a8dc125418831907cfeb785e +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Documentation/Resource.meta b/Documentation/Resource.meta new file mode 100644 index 0000000..16b3086 --- /dev/null +++ b/Documentation/Resource.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7b6dff7ffa0c54f4c93b41811af269d5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Reservoir/Icons/Core/RealMethod.png b/Documentation/Resource/RM_Title.png similarity index 100% rename from Reservoir/Icons/Core/RealMethod.png rename to Documentation/Resource/RM_Title.png diff --git a/Reservoir/Icons/Core/RealMethod.png.meta b/Documentation/Resource/RM_Title.png.meta similarity index 100% rename from Reservoir/Icons/Core/RealMethod.png.meta rename to Documentation/Resource/RM_Title.png.meta diff --git a/Editor/Core/Architecture/GameCompWindow.cs b/Editor/Core/Architecture/Game_Editor.cs similarity index 86% rename from Editor/Core/Architecture/GameCompWindow.cs rename to Editor/Core/Architecture/Game_Editor.cs index 589ee28..6ad9fdd 100644 --- a/Editor/Core/Architecture/GameCompWindow.cs +++ b/Editor/Core/Architecture/Game_Editor.cs @@ -1,10 +1,9 @@ -using PlasticGui.WorkspaceWindow; using UnityEditor; -namespace RealMethod +namespace RealMethod.Editor { [CustomEditor(typeof(Game), true)] - public class GameCompWindow : UnityEditor.Editor + public class Game_Editor : UnityEditor.Editor { private Game BaseComponent; @@ -18,10 +17,6 @@ public override void OnInspectorGUI() EditorGUILayout.Space(); if (BaseComponent != null) { - if (Game.World != null) - { - - } EditorGUILayout.LabelField($"{GetWorld()} | {GetSetvice()} | {GetConfig()}"); EditorGUILayout.Space(0.5f); string[] Data = BaseComponent.GetAllServiceNames(); @@ -32,10 +27,6 @@ public override void OnInspectorGUI() } } - - - - private string GetWorld() { return Game.World != null ? Game.World.GetType().Name : "World Not Valid"; @@ -49,14 +40,6 @@ private string GetConfig() return Game.Config != null ? Game.Config.GetType().Name : "GameConfig Not Valid"; } - - - - - - - - } diff --git a/Editor/Core/Architecture/GameCompWindow.cs.meta b/Editor/Core/Architecture/Game_Editor.cs.meta similarity index 100% rename from Editor/Core/Architecture/GameCompWindow.cs.meta rename to Editor/Core/Architecture/Game_Editor.cs.meta diff --git a/Editor/Core/Architecture/AutoWorldExecutionOrder.cs b/Editor/Core/Architecture/WorldExecutionOrder_Tools.cs similarity index 97% rename from Editor/Core/Architecture/AutoWorldExecutionOrder.cs rename to Editor/Core/Architecture/WorldExecutionOrder_Tools.cs index ab0e17c..f2b0fb7 100644 --- a/Editor/Core/Architecture/AutoWorldExecutionOrder.cs +++ b/Editor/Core/Architecture/WorldExecutionOrder_Tools.cs @@ -7,7 +7,7 @@ namespace RealMethod.Editor { //[InitializeOnLoad] - public static class AutoWorldExecutionOrder + public static class WorldExecutionOrder_Tools { [MenuItem("Tools/RealMethod/Core/ExecutionOrder" , priority = -1001)] static void ApplyExecutionOrder() diff --git a/Editor/Core/Architecture/AutoWorldExecutionOrder.cs.meta b/Editor/Core/Architecture/WorldExecutionOrder_Tools.cs.meta similarity index 100% rename from Editor/Core/Architecture/AutoWorldExecutionOrder.cs.meta rename to Editor/Core/Architecture/WorldExecutionOrder_Tools.cs.meta diff --git a/Editor/Core/Definitions/ConfigAssetValidator.cs b/Editor/Core/Definitions/ConfigAsset_Onload.cs similarity index 96% rename from Editor/Core/Definitions/ConfigAssetValidator.cs rename to Editor/Core/Definitions/ConfigAsset_Onload.cs index bec8bc5..d5f4ee2 100644 --- a/Editor/Core/Definitions/ConfigAssetValidator.cs +++ b/Editor/Core/Definitions/ConfigAsset_Onload.cs @@ -1,4 +1,3 @@ -#if UNITY_EDITOR using UnityEditor; using UnityEngine; using System; @@ -8,9 +7,9 @@ namespace RealMethod.Editor { [InitializeOnLoad] - public static class ConfigAssetValidator + public static class ConfigAsset_Onload { - static ConfigAssetValidator() + static ConfigAsset_Onload() { EditorApplication.delayCall += ValidateAllConfigs; } @@ -84,6 +83,4 @@ private static bool ViolatesPureMethodRules(MethodInfo method) return false; } } -#endif - } \ No newline at end of file diff --git a/Editor/Core/Definitions/ConfigAssetValidator.cs.meta b/Editor/Core/Definitions/ConfigAsset_Onload.cs.meta similarity index 100% rename from Editor/Core/Definitions/ConfigAssetValidator.cs.meta rename to Editor/Core/Definitions/ConfigAsset_Onload.cs.meta diff --git a/Editor/Core/Definitions/DataAssetEditor.cs b/Editor/Core/Definitions/DataAsset_Editor.cs similarity index 92% rename from Editor/Core/Definitions/DataAssetEditor.cs rename to Editor/Core/Definitions/DataAsset_Editor.cs index 2ee63d0..fc68194 100644 --- a/Editor/Core/Definitions/DataAssetEditor.cs +++ b/Editor/Core/Definitions/DataAsset_Editor.cs @@ -1,10 +1,9 @@ -#if UNITY_EDITOR using UnityEditor; using UnityEngine; namespace RealMethod.Editor { - public class DataAssetEditor : UnityEditor.Editor + public class DataAsset_Editor : UnityEditor.Editor { public static void DrawCompleteScriptableObjectEditor(string name, ref T settings, ref bool foldout, ref UnityEditor.Editor editor) where T : ScriptableObject { @@ -23,5 +22,4 @@ public static void DrawSubEditor (Object settings, ref bool foldout, ref UnityEd } } } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/Editor/Core/Definitions/DataAssetEditor.cs.meta b/Editor/Core/Definitions/DataAsset_Editor.cs.meta similarity index 100% rename from Editor/Core/Definitions/DataAssetEditor.cs.meta rename to Editor/Core/Definitions/DataAsset_Editor.cs.meta diff --git a/Editor/Core/Definitions/PrefabCore_Drawer.cs b/Editor/Core/Definitions/PrefabCore_Drawer.cs new file mode 100644 index 0000000..37f4363 --- /dev/null +++ b/Editor/Core/Definitions/PrefabCore_Drawer.cs @@ -0,0 +1,126 @@ +using UnityEditor; +using UnityEngine; +using System; + +namespace RealMethod.Editor +{ + [CustomPropertyDrawer(typeof(PrefabCore), true)] + public class PrefabCore_Drawer : PropertyDrawer + { + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + SerializedProperty prefabGOProp = property.FindPropertyRelative("PrefabAsset"); + + if (prefabGOProp == null) + { + EditorGUI.LabelField(position, label.text, "Cannot serialize generic prefab"); + return; + } + + EditorGUI.BeginProperty(position, label, property); + + // Determine label text + // string displayLabel = prefabGOProp.objectReferenceValue != null ? label.text: $"{label.text} (Select a prefab...)"; + + // Draw the ObjectField for GameObject + UnityEngine.Object newObject = EditorGUI.ObjectField( + position, + label, + prefabGOProp.objectReferenceValue, + typeof(GameObject), + false + ); + + // Only update if changed + if (newObject != prefabGOProp.objectReferenceValue) + { + if (newObject == null) + { + prefabGOProp.objectReferenceValue = null; + } + else if (PrefabUtility.IsPartOfPrefabAsset(newObject)) + { + // Get the concrete PrefabCore instance + PrefabCore targetPrefab = fieldInfo.GetValue(property.serializedObject.targetObject) as PrefabCore; + + if (targetPrefab == null) + { + Debug.LogWarning("Invalid prefab wrapper."); + return; + } + + var targetClass = targetPrefab.GetTargetClass(); + GameObject go = newObject as GameObject; + + // Type-check: the prefab must have the required component + if (targetClass != null && go.GetComponent(targetClass) != null) + { + prefabGOProp.objectReferenceValue = newObject; + } + else + { + Debug.LogWarning($"Selected prefab does not have required component: {targetClass?.Name}"); + } + } + else + { + Debug.LogWarning("Only prefab assets from the project can be assigned."); + } + } + + // --- Set GUI color to blue to indicate change --- + Color prevColor = GUI.color; + GUI.color = Color.cyan; // light blue + EditorGUI.ObjectField(position, label, prefabGOProp.objectReferenceValue, typeof(GameObject), false); + GUI.color = prevColor; + EditorGUI.EndProperty(); + } + + + + + // --- Utility to get the actual object instance from SerializedProperty --- + // private object GetTargetObjectOfProperty(SerializedProperty prop) + // { + // if (prop == null) return null; + + // string path = prop.propertyPath.Replace(".Array.data[", "["); + // object obj = prop.serializedObject.targetObject; + // string[] elements = path.Split('.'); + + // foreach (string element in elements) + // { + // if (element.Contains("[")) + // { + // string elementName = element.Substring(0, element.IndexOf("[")); + // int index = Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", "")); + // obj = GetValue(obj, elementName, index); + // } + // else + // { + // obj = GetValue(obj, element); + // } + // } + // return obj; + // } + // private object GetValue(object source, string name) + // { + // if (source == null) return null; + // var type = source.GetType(); + // var f = type.GetField(name, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + // if (f == null) return null; + // return f.GetValue(source); + // } + // private object GetValue(object source, string name, int index) + // { + // var enumerable = GetValue(source, name) as System.Collections.IEnumerable; + // if (enumerable == null) return null; + // var enm = enumerable.GetEnumerator(); + // for (int i = 0; i <= index; i++) + // { + // if (!enm.MoveNext()) return null; + // } + // return enm.Current; + // } + } +} diff --git a/Editor/Core/Definitions/PrefabDrawer.cs.meta b/Editor/Core/Definitions/PrefabCore_Drawer.cs.meta similarity index 100% rename from Editor/Core/Definitions/PrefabDrawer.cs.meta rename to Editor/Core/Definitions/PrefabCore_Drawer.cs.meta diff --git a/Editor/Core/Definitions/PrefabDrawer.cs b/Editor/Core/Definitions/PrefabDrawer.cs deleted file mode 100644 index 7962de3..0000000 --- a/Editor/Core/Definitions/PrefabDrawer.cs +++ /dev/null @@ -1,117 +0,0 @@ -#if UNITY_EDITOR -using UnityEditor; -using UnityEngine; -using System; - -namespace RealMethod.Editor -{ - [CustomPropertyDrawer(typeof(PrefabCore), true)] - public class PrefabDrawer : PropertyDrawer - { - public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) - { - SerializedProperty prefabGOProp = property.FindPropertyRelative("PrefabAsset"); - - if (prefabGOProp == null) - { - EditorGUI.LabelField(position, label.text, "Invalid Prefab Field"); - return; - } - - EditorGUI.BeginProperty(position, label, property); - - UnityEngine.Object newObject = EditorGUI.ObjectField( - position, - label, - prefabGOProp.objectReferenceValue, - typeof(GameObject), - false - ); - - if (newObject != prefabGOProp.objectReferenceValue) - { - if (newObject == null) - { - prefabGOProp.objectReferenceValue = null; - } - else if (PrefabUtility.IsPartOfPrefabAsset(newObject)) - { - GameObject go = newObject as GameObject; - - // Get the actual object instance for this SerializedProperty - PrefabCore targetPrefab = GetTargetObjectOfProperty(property) as PrefabCore; - - if (targetPrefab == null) - { - EditorGUI.LabelField(position, label.text, "Invalid prefab wrapper."); - return; - } - - var targetClass = targetPrefab.GetTargetClass(); - - if (targetClass != null && go.GetComponent(targetClass) != null) - { - prefabGOProp.objectReferenceValue = newObject; - } - else - { - Debug.LogWarning($"Selected prefab does not have required component: {targetClass?.Name}"); - } - } - else - { - Debug.LogWarning("Only prefab assets from the project can be assigned."); - } - } - - EditorGUI.EndProperty(); - } - - // Utility to get the real object from a SerializedProperty - private object GetTargetObjectOfProperty(SerializedProperty prop) - { - if (prop == null) return null; - - string path = prop.propertyPath.Replace(".Array.data[", "["); - object obj = prop.serializedObject.targetObject; - string[] elements = path.Split('.'); - - foreach (string element in elements) - { - if (element.Contains("[")) - { - string elementName = element.Substring(0, element.IndexOf("[")); - int index = Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", "")); - obj = GetValue(obj, elementName, index); - } - else - { - obj = GetValue(obj, element); - } - } - return obj; - } - - private object GetValue(object source, string name) - { - if (source == null) return null; - var type = source.GetType(); - var f = type.GetField(name, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); - if (f == null) return null; - return f.GetValue(source); - } - - private object GetValue(object source, string name, int index) - { - var enumerable = GetValue(source, name) as System.Collections.IEnumerable; - if (enumerable == null) return null; - var enm = enumerable.GetEnumerator(); - for (int i = 0; i <= index; i++) - { - if (!enm.MoveNext()) return null; - } - return enm.Current; - } - } -} -#endif diff --git a/Editor/Core/ProjectSetting/ProjectSettingWindow.cs b/Editor/Core/ProjectSetting/RealMethod_ProjectSetting.cs similarity index 83% rename from Editor/Core/ProjectSetting/ProjectSettingWindow.cs rename to Editor/Core/ProjectSetting/RealMethod_ProjectSetting.cs index 6f1ebfe..85690a4 100644 --- a/Editor/Core/ProjectSetting/ProjectSettingWindow.cs +++ b/Editor/Core/ProjectSetting/RealMethod_ProjectSetting.cs @@ -7,18 +7,124 @@ namespace RealMethod.Editor { - public static class ProjectSettingWindow + // Interface for call base method in ProjectSettingSection by RealMethodSetting + interface ISectionSetting + { + void FirstSelected(ProjectSettingAsset storage); + void Draw(); + } + + // Abstract base class for a settings section + public abstract class ProjectSettingSection : ISectionSetting + { + protected class ClassType + { + private List TypeList; + private string[] TypeName; + private int selctedIndex = 0; + private int newIndex; + + public ClassType() + { + // Get all available T types **only once** + TypeList = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(assembly => assembly.GetTypes()) + .Where(type => typeof(T).IsAssignableFrom(type) && !type.IsAbstract) + .ToList(); + + TypeName = TypeList.Select(t => t.FullName).ToArray(); + } + + public void Draw(SerializedObject projectSettings, string PropertyName, string DisplayName) + { + selctedIndex = System.Array.IndexOf(TypeName, projectSettings.FindProperty(PropertyName).stringValue); + newIndex = EditorGUILayout.Popup(DisplayName, selctedIndex, TypeName); + if (newIndex >= 0 && newIndex < TypeName.Length) + { + projectSettings.FindProperty(PropertyName).stringValue = TypeName[newIndex]; + } + } + } + private bool isReady = true;// Indicates whether the section is ready to render + private string message = string.Empty;// Error message to display if the section is not ready + private int errorid = 0;// Error ID to identify the type of error + + public ProjectSettingSection() + { + Initialized(); + } + + // Implement ISectionSetting Interface + void ISectionSetting.FirstSelected(ProjectSettingAsset storage) + { + BeginRender(storage); + } + void ISectionSetting.Draw() + { + EditorGUILayout.Space(); + EditorGUILayout.LabelField(GetTitle(), EditorStyles.boldLabel); // Section Title + + if (isReady) + { + // Render the section's content + UpdateRender(); + } + else + { + // Display an error message if the section is not ready + EditorGUILayout.HelpBox(message, MessageType.Error); + + // Provide a "Fix" button to resolve the error + if (GUILayout.Button("Fix")) + { + Fix(errorid); + } + } + } + + // Abstract Method + protected abstract void Initialized(); + protected abstract void BeginRender(ProjectSettingAsset Storage); + protected abstract void UpdateRender(); + protected abstract string GetTitle(); + protected abstract void Fix(int Id); + + // Protected Function + protected void Error(string Message, int Id = 0) + { + if (isReady) + { + isReady = false; + } + message = Message; + errorid = Id; + } + protected void ClearError() + { + message = string.Empty; + errorid = 0; + if (!isReady) + { + isReady = true; + } + UpdateRender(); + } + } + + // Project Setting + public static class RealMethod_ProjectSetting { private const string settingsPath = "Assets/Resources/RealMethod/RealMethodSetting.asset"; private static bool candraw = true;// Flag to determine if the UI can be drawn private static ProjectSettingSection[] sections = new ProjectSettingSection[2] { // Array of sections to be rendered in the settings UI - new InitializerSection(), - new FolderSettingSection() - }; + new InitializerSetting_Section(), + new FolderStructure_Section() + }; - [SettingsProvider]// Create a SettingsProvider for Unity's Project Settings + [SettingsProvider] + // Create a SettingsProvider for Unity's Project Settings public static SettingsProvider CreateSettingsProvider() { var provider = new SettingsProvider("Project/Real Method", SettingsScope.Project) @@ -46,8 +152,9 @@ public static SettingsProvider CreateSettingsProvider() // Initialize each section with the loaded settings foreach (var item in sections) { + ISectionSetting ptovider = item; if (TargetStorage != null) - item.BeginRender(TargetStorage); + ptovider.FirstSelected(TargetStorage); } }, @@ -59,7 +166,8 @@ public static SettingsProvider CreateSettingsProvider() // Render each section foreach (var item in sections) { - item.UpdateRender(); + ISectionSetting ptovider = item; + ptovider.Draw(); // Add a separator line GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(1)); } @@ -79,8 +187,9 @@ public static SettingsProvider CreateSettingsProvider() ProjectSettingAsset TargetStorage = CreateSettingStorage(); foreach (var item in sections) { + ISectionSetting ptovider = item; if (TargetStorage != null) - item.BeginRender(TargetStorage); + ptovider.FirstSelected(TargetStorage); } candraw = true; @@ -108,103 +217,4 @@ private static ProjectSettingAsset CreateSettingStorage() return settings; } } - - - // Abstract base class for a settings section - public abstract class ProjectSettingSection - { - protected class ClassType - { - private List TypeList; - private string[] TypeName; - private int selctedIndex = 0; - private int newIndex; - - public ClassType() - { - // Get all available T types **only once** - TypeList = AppDomain.CurrentDomain.GetAssemblies() - .SelectMany(assembly => assembly.GetTypes()) - .Where(type => typeof(T).IsAssignableFrom(type) && !type.IsAbstract) - .ToList(); - - TypeName = TypeList.Select(t => t.FullName).ToArray(); - } - - public void Draw(SerializedObject projectSettings, string PropertyName, string DisplayName) - { - selctedIndex = System.Array.IndexOf(TypeName, projectSettings.FindProperty(PropertyName).stringValue); - newIndex = EditorGUILayout.Popup(DisplayName, selctedIndex, TypeName); - if (newIndex >= 0 && newIndex < TypeName.Length) - { - projectSettings.FindProperty(PropertyName).stringValue = TypeName[newIndex]; - } - } - } - private bool isReady = true;// Indicates whether the section is ready to render - private string message = string.Empty;// Error message to display if the section is not ready - private int errorid = 0;// Error ID to identify the type of error - - - public ProjectSettingSection() - { - Initialized(); - } - - public void BeginRender(ProjectSettingAsset storage) - { - FirstSelected(storage); - } - public void UpdateRender() - { - EditorGUILayout.Space(); - EditorGUILayout.LabelField(GetTitle(), EditorStyles.boldLabel); // Section Title - - if (isReady) - { - // Render the section's content - Draw(); - } - else - { - // Display an error message if the section is not ready - EditorGUILayout.HelpBox(message, MessageType.Error); - - // Provide a "Fix" button to resolve the error - if (GUILayout.Button("Fix")) - { - Fix(errorid); - } - } - } - - - protected abstract void Initialized(); - protected abstract void FirstSelected(ProjectSettingAsset Storage); - protected abstract void Draw(); - protected abstract string GetTitle(); - protected abstract void Fix(int Id); - - - protected void Error(string Message, int Id = 0) - { - if (isReady) - { - isReady = false; - } - message = Message; - errorid = Id; - } - protected void ClearError() - { - message = string.Empty; - errorid = 0; - if (!isReady) - { - isReady = true; - } - UpdateRender(); - } - - } } \ No newline at end of file diff --git a/Editor/Core/ProjectSetting/ProjectSettingWindow.cs.meta b/Editor/Core/ProjectSetting/RealMethod_ProjectSetting.cs.meta similarity index 100% rename from Editor/Core/ProjectSetting/ProjectSettingWindow.cs.meta rename to Editor/Core/ProjectSetting/RealMethod_ProjectSetting.cs.meta diff --git a/Editor/Core/ProjectSetting/Sections/FolderSettingSection.cs b/Editor/Core/ProjectSetting/Sections/FolderStructure_Section.cs similarity index 75% rename from Editor/Core/ProjectSetting/Sections/FolderSettingSection.cs rename to Editor/Core/ProjectSetting/Sections/FolderStructure_Section.cs index 5396d8c..c62e6ea 100644 --- a/Editor/Core/ProjectSetting/Sections/FolderSettingSection.cs +++ b/Editor/Core/ProjectSetting/Sections/FolderStructure_Section.cs @@ -3,23 +3,31 @@ namespace RealMethod.Editor { - public class FolderSettingSection : ProjectSettingSection + public class FolderStructure_Section : ProjectSettingSection { private ProjectSettingAsset MyStorage; private bool isPanelMaximize = false; // Add a toggle for minimizing the panel - // Implement Abstraction Methods + // Implement ProjectSettingSection Methods protected override void Initialized() { } - - protected override void FirstSelected(ProjectSettingAsset Storage) + protected override void BeginRender(ProjectSettingAsset Storage) { MyStorage = Storage; } - - protected override void Draw() + protected override void UpdateRender() { + string[] structureType = System.Enum.GetNames(typeof(ProjectSettingAsset.FolderStructureType)); + MyStorage.SetStructureType(EditorGUILayout.Popup("StructureType", MyStorage.GetStructureType(), structureType)); + if (MyStorage.GetStructureType() == 1) + { + if (!AssetDatabase.IsValidFolder("Assets/" + Application.productName)) + { + AssetDatabase.CreateFolder("Assets", Application.productName); + } + } + // Add a toggle button for minimizing or expanding the panel EditorGUILayout.BeginHorizontal(); isPanelMaximize = EditorGUILayout.Foldout(isPanelMaximize, "Folder List", true, EditorStyles.foldoutHeader); @@ -27,13 +35,13 @@ protected override void Draw() { foreach (var address in MyStorage.ProjectStructure) { - if (AssetDatabase.IsValidFolder(address.Path)) + if (AssetDatabase.IsValidFolder(address.AssetPath)) { Debug.Log($"Folder exists: {address}"); } else { - string folderpath = address.Path; + string folderpath = address.GetFolderPath(MyStorage); string FolderAddress = string.Join("/", folderpath.Split('/')[..^1]); // Remove the last segment of the path string folderName = System.IO.Path.GetFileName(folderpath); // Get the last segment of the path CreateFolder(FolderAddress, folderName); // Create the folder @@ -49,26 +57,26 @@ protected override void Draw() } // Render the folder list - for (int i = 0; i < MyStorage.ProjectStructure.Length; i++) + for (int i = 0; i < MyStorage.ProjectStructure.Count; i++) { EditorGUILayout.BeginHorizontal(); // Start horizontal layout // Display the folder path as a text field - MyStorage.ProjectStructure[i].Path = EditorGUILayout.TextField($"{i + 1}.{MyStorage.ProjectStructure[i].Identity}", MyStorage.ProjectStructure[i].Path); + MyStorage.SetFolderAddressPath(i, EditorGUILayout.TextField($"{i + 1}.{MyStorage.ProjectStructure[i].AssetType}", MyStorage.ProjectStructure[i].AssetPath)); - string ButtonName = AssetDatabase.IsValidFolder(MyStorage.ProjectStructure[i].Path) ? "Check" : "Create"; + string ButtonName = AssetDatabase.IsValidFolder(MyStorage.ProjectStructure[i].AssetPath) ? "Check" : "Create"; // Add a button next to the text field if (GUILayout.Button(ButtonName, GUILayout.Width(60))) { // Check if the folder exists - if (AssetDatabase.IsValidFolder(MyStorage.ProjectStructure[i].Path)) + if (AssetDatabase.IsValidFolder(MyStorage.ProjectStructure[i].AssetPath)) { Debug.Log($"Folder exists: {MyStorage.ProjectStructure[i]}"); } else { - string folderpath = MyStorage.ProjectStructure[i].Path; + string folderpath = MyStorage.ProjectStructure[i].GetFolderPath(MyStorage); string FolderAddress = string.Join("/", folderpath.Split('/')[..^1]); // Remove the last segment of the path string folderName = System.IO.Path.GetFileName(folderpath); // Get the last segment of the path CreateFolder(FolderAddress, folderName); // Create the folder @@ -83,11 +91,9 @@ protected override string GetTitle() { return "FolderStructure"; } - protected override void Fix(int Id) { } - private void CreateFolder(string parentFolder, string newFolderName) { string folderPath = System.IO.Path.Combine(parentFolder, newFolderName).Replace("\\", "/"); diff --git a/Editor/Core/ProjectSetting/Sections/FolderSettingSection.cs.meta b/Editor/Core/ProjectSetting/Sections/FolderStructure_Section.cs.meta similarity index 100% rename from Editor/Core/ProjectSetting/Sections/FolderSettingSection.cs.meta rename to Editor/Core/ProjectSetting/Sections/FolderStructure_Section.cs.meta diff --git a/Editor/Core/ProjectSetting/Sections/InitializerSection.cs b/Editor/Core/ProjectSetting/Sections/InitializerSetting_Section.cs similarity index 91% rename from Editor/Core/ProjectSetting/Sections/InitializerSection.cs rename to Editor/Core/ProjectSetting/Sections/InitializerSetting_Section.cs index 477e7f7..9fc11b2 100644 --- a/Editor/Core/ProjectSetting/Sections/InitializerSection.cs +++ b/Editor/Core/ProjectSetting/Sections/InitializerSetting_Section.cs @@ -3,7 +3,7 @@ namespace RealMethod.Editor { - public class InitializerSection : ProjectSettingSection + public class InitializerSetting_Section : ProjectSettingSection { private ClassType gameClass; private ClassType gameService; @@ -15,12 +15,12 @@ protected override void Initialized() gameClass = new ClassType(); gameService = new ClassType(); } - protected override void FirstSelected(ProjectSettingAsset Storage) + protected override void BeginRender(ProjectSettingAsset Storage) { SettingAsset = Storage; projectSettings = new SerializedObject(Storage); } - protected override void Draw() + protected override void UpdateRender() { if (projectSettings == null) return; diff --git a/Editor/Core/ProjectSetting/Sections/InitializerSection.cs.meta b/Editor/Core/ProjectSetting/Sections/InitializerSetting_Section.cs.meta similarity index 100% rename from Editor/Core/ProjectSetting/Sections/InitializerSection.cs.meta rename to Editor/Core/ProjectSetting/Sections/InitializerSetting_Section.cs.meta diff --git a/Editor/Core/ProjectSetting/UnityAsset_Postprocessor.cs b/Editor/Core/ProjectSetting/UnityAsset_Postprocessor.cs deleted file mode 100644 index 95642d8..0000000 --- a/Editor/Core/ProjectSetting/UnityAsset_Postprocessor.cs +++ /dev/null @@ -1,123 +0,0 @@ -using UnityEditor; -using UnityEngine; - -namespace RealMethod.Editor -{ - public class UnityAsset_Postprocessor : AssetPostprocessor - { - static AssetHandeler[] AssetList = new AssetHandeler[5] { - new WorldScene_UnityAsset(), - new Table_UnityAsset(), - new PCGResource_UnityAsset(), - new PCGGeneration_UnityAsset(), - new PCGCash_UnityAsset() }; - - // private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) - // { - // if (AssetList == null) - // { - // return; - // } - - // foreach (var asset in AssetList) - // { - // foreach (string assetPath in importedAssets) - // { - // asset.OnAssetImported(assetPath); - // } - // foreach (string assetPath in deletedAssets) - // { - // asset.OnAssetDeleted(assetPath); - // } - // for (int i = 0; i < movedAssets.Length; i++) - // { - // asset.OnAssetMoved(movedAssets[i], movedFromAssetPaths[i]); - // } - // } - // } - - [InitializeOnLoadMethod] - private static void OnDoubleClickScriptableObject() - { - EditorApplication.projectWindowItemOnGUI += (guid, rect) => - { - Event e = Event.current; - if (e.type == EventType.MouseDown && e.clickCount == 2) - { - string assetPath = AssetDatabase.GUIDToAssetPath(guid); - foreach (var asset in AssetList) - { - asset.OnAssetClick(assetPath, e); - } - } - }; - } - } - - - - public abstract class AssetHandeler - { - public AssetHandeler() - { - Initialized(); - } - - protected abstract void Initialized(); - public abstract void OnAssetImported(string AssetPath); - public abstract void OnAssetDeleted(string AssetPath); - public abstract void OnAssetMoved(string AssetPath, string FromPath); - public abstract void OnAssetClick(string AssetPath, Event e); - protected abstract string GetIconPath(); - } - - public abstract class AssetHandeler : AssetHandeler where T : Object - { - protected abstract void DoubleClick(T asset); - - public override void OnAssetImported(string AssetPath) - { - T loadedAsset; - if (TryLoadAsset(AssetPath, out loadedAsset)) - { - if (loadedAsset.GetType() == typeof(J)) - { - // Load your icon from Resources (adjust the path as needed) - Texture2D icon = Resources.Load(GetIconPath()); - if (icon != null) - { - EditorGUIUtility.SetIconForObject(loadedAsset, icon); - } - else - { - Debug.LogWarning("Custom icon not found. Please ensure the path and file are correct."); - } - } - } - else - { - Debug.LogWarning("Cant Load "); - } - } - public override void OnAssetClick(string AssetPath, Event e) - { - var asset = AssetDatabase.LoadAssetAtPath(AssetPath); - - if (asset != null) - { - DoubleClick(asset); - e.Use(); // Consume the event - } - } - - protected bool TryLoadAsset(string assetPath, out K asset) where K : Object - { - asset = AssetDatabase.LoadAssetAtPath(assetPath); - return asset != null; - } - } - - - - -} \ No newline at end of file diff --git a/Editor/Library/SharedScripts/Classes/AssetProcess.cs b/Editor/Library/SharedScripts/Classes/AssetProcess.cs new file mode 100644 index 0000000..85393ee --- /dev/null +++ b/Editor/Library/SharedScripts/Classes/AssetProcess.cs @@ -0,0 +1,67 @@ +using UnityEngine; +using UnityEditor; + + +namespace RealMethod.Editor +{ + public abstract class AssetProcess + { + public AssetProcess() + { + Initialized(); + } + + protected abstract void Initialized(); + public abstract void OnAssetImported(string AssetPath); + public abstract void OnAssetDeleted(string AssetPath); + public abstract void OnAssetMoved(string AssetPath, string FromPath); + public abstract void OnAssetClick(string AssetPath, Event e); + protected abstract string GetIconPath(); + } + public abstract class AssetProcess : AssetProcess where T : Object + { + protected abstract void DoubleClick(T asset); + + public override void OnAssetImported(string AssetPath) + { + T loadedAsset; + if (TryLoadAsset(AssetPath, out loadedAsset)) + { + if (loadedAsset.GetType() == typeof(J)) + { + // Load your icon from Resources (adjust the path as needed) + Texture2D icon = Resources.Load(GetIconPath()); + if (icon != null) + { + EditorGUIUtility.SetIconForObject(loadedAsset, icon); + } + else + { + Debug.LogWarning("Custom icon not found. Please ensure the path and file are correct."); + } + } + } + else + { + Debug.LogWarning("Cant Load "); + } + } + public override void OnAssetClick(string AssetPath, Event e) + { + var asset = AssetDatabase.LoadAssetAtPath(AssetPath); + + if (asset != null) + { + DoubleClick(asset); + e.Use(); // Consume the event + } + } + + protected bool TryLoadAsset(string assetPath, out K asset) where K : Object + { + asset = AssetDatabase.LoadAssetAtPath(assetPath); + return asset != null; + } + } + +} \ No newline at end of file diff --git a/Editor/Library/SharedScripts/Classes/AssetProcess.cs.meta b/Editor/Library/SharedScripts/Classes/AssetProcess.cs.meta new file mode 100644 index 0000000..0dc6766 --- /dev/null +++ b/Editor/Library/SharedScripts/Classes/AssetProcess.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 060b9dbad7dba424b9f55475deb3b39d \ No newline at end of file diff --git a/Editor/Library/Utilities/Core.cs b/Editor/Library/Utilities/Core.cs index e41574f..fab8f91 100644 --- a/Editor/Library/Utilities/Core.cs +++ b/Editor/Library/Utilities/Core.cs @@ -9,6 +9,7 @@ public static class RM_CoreEditor public const string SetttingAssetPath = "Assets/Resources/RealMethod/RealMethodSetting.asset"; public static string ScriptTemplatesPath => GetPackagePath("com.mustard.realmethod") + "/Reservoir/ScriptTemplates"; public static string PrefabTemplatePath => GetPackagePath("com.mustard.realmethod") + "/Reservoir/Prefabs"; + public static string Documentation => GetPackagePath("com.mustard.realmethod") + "/Documentation/Information"; private static string GetPackagePath(string packageName) { diff --git a/Editor/Library/Utilities/Create.cs b/Editor/Library/Utilities/Create.cs index 012366e..54d4b61 100644 --- a/Editor/Library/Utilities/Create.cs +++ b/Editor/Library/Utilities/Create.cs @@ -12,7 +12,7 @@ public static string Script(string templateFileName, string defaultName, bool Us if (UseProject) { ProjectSettingAsset ProjectSetting = AssetDatabase.LoadAssetAtPath(RM_CoreEditor.SetttingAssetPath); - templatePath = Path.Combine(ProjectSetting.FindAddres(ProjectSettingAsset.IdentityAsset.ScriptTemplate).Path, templateFileName); + templatePath = Path.Combine(ProjectSetting[ProjectSettingAsset.AssetFormat.Other], templateFileName); } else { @@ -63,7 +63,7 @@ public static GameObject Prefab(string prefabName, bool UseProject = false) if (UseProject) { ProjectSettingAsset ProjectSetting = AssetDatabase.LoadAssetAtPath(RM_CoreEditor.SetttingAssetPath); - prefabPath = Path.Combine(ProjectSetting.FindAddres(ProjectSettingAsset.IdentityAsset.PrefabTemplate).Path, prefabName); + prefabPath = Path.Combine(ProjectSetting[ProjectSettingAsset.AssetFormat.Prefab], prefabName); } else { diff --git a/Editor/ReadySet/Content/RealMethod_GameObject.cs b/Editor/ReadySet/Content/RealMethod_GameObject.cs new file mode 100644 index 0000000..e7e2e06 --- /dev/null +++ b/Editor/ReadySet/Content/RealMethod_GameObject.cs @@ -0,0 +1,45 @@ +using UnityEditor; +using UnityEngine; + +namespace RealMethod.Editor +{ + class RealMethodGameObject + { + [MenuItem("GameObject/RealMethod/PlayerStarter", false, 10)] + static void CreatePlayerStarter(MenuCommand menuCommand) + { + // Create a new GameObject + GameObject go = new GameObject("PlayerStarter"); + + // Optional: add components + go.AddComponent(); + + // Place it in the scene, parented if needed + GameObjectUtility.SetParentAndAlign(go, menuCommand.context as GameObject); + + // Register the creation in Undo system (so Ctrl+Z works) + Undo.RegisterCreatedObjectUndo(go, "Create " + go.name); + + // Select the new GameObject + Selection.activeObject = go; + } + [MenuItem("GameObject/RealMethod/World", false, 10)] + static void CreateWorld(MenuCommand menuCommand) + { + // Create a new GameObject + GameObject go = new GameObject("World"); + + // Optional: add components + go.AddComponent(); + + // Place it in the scene, parented if needed + GameObjectUtility.SetParentAndAlign(go, menuCommand.context as GameObject); + + // Register the creation in Undo system (so Ctrl+Z works) + Undo.RegisterCreatedObjectUndo(go, "Create " + go.name); + + // Select the new GameObject + Selection.activeObject = go; + } + } +} \ No newline at end of file diff --git a/Editor/ReadySet/Content/RealMethod_GameObject.cs.meta b/Editor/ReadySet/Content/RealMethod_GameObject.cs.meta new file mode 100644 index 0000000..5a7589f --- /dev/null +++ b/Editor/ReadySet/Content/RealMethod_GameObject.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d20b067d280180f4cbcf52a7b71ae786 \ No newline at end of file diff --git a/Editor/ReadySet/Content/RealMethod_Prefab.cs b/Editor/ReadySet/Content/RealMethod_Prefab.cs index 5227ed6..54cfc8b 100644 --- a/Editor/ReadySet/Content/RealMethod_Prefab.cs +++ b/Editor/ReadySet/Content/RealMethod_Prefab.cs @@ -1,7 +1,7 @@ using RealMethod.Editor; using UnityEditor; -namespace RealMethod +namespace RealMethod.Editor { class RealMethodPrefab { diff --git a/Editor/ReadySet/Content/RealMethod_UnityAsset.cs b/Editor/ReadySet/Content/RealMethod_UnityAsset.cs index 48c1cde..98660cb 100644 --- a/Editor/ReadySet/Content/RealMethod_UnityAsset.cs +++ b/Editor/ReadySet/Content/RealMethod_UnityAsset.cs @@ -2,7 +2,7 @@ namespace RealMethod.Editor { - public class Table_UnityAsset : AssetHandeler + public class Table_UnityAsset : AssetProcess { protected override void Initialized() { @@ -26,7 +26,7 @@ protected override void DoubleClick(TableAsset asset) TableViewerWindow.OpenWindow(asset); } } - public class WorldScene_UnityAsset : AssetHandeler + public class WorldScene_UnityAsset : AssetProcess { protected override void Initialized() { @@ -49,7 +49,7 @@ protected override void DoubleClick(WorldSceneConfig asset) asset.OnAssetClick(); } } - public class Game_UnityAsset : AssetHandeler + public class Game_UnityAsset : AssetProcess { protected override void Initialized() { diff --git a/Editor/ReadySet/Content/UnityAsset_Postprocessor.cs b/Editor/ReadySet/Content/UnityAsset_Postprocessor.cs new file mode 100644 index 0000000..bfdbf40 --- /dev/null +++ b/Editor/ReadySet/Content/UnityAsset_Postprocessor.cs @@ -0,0 +1,58 @@ +using UnityEditor; +using UnityEngine; + +namespace RealMethod.Editor +{ + public class UnityAsset_Postprocessor : AssetPostprocessor + { + private static AssetProcess[] AssetList = new AssetProcess[5] + { + new WorldScene_UnityAsset(), + new Table_UnityAsset(), + new PCGResource_UnityAsset(), + new PCGGeneration_UnityAsset(), + new PCGCash_UnityAsset() + }; + + [InitializeOnLoadMethod] + private static void OnDoubleClickScriptableObject() + { + EditorApplication.projectWindowItemOnGUI += (guid, rect) => + { + Event e = Event.current; + if (e.type == EventType.MouseDown && e.clickCount == 2) + { + string assetPath = AssetDatabase.GUIDToAssetPath(guid); + foreach (var asset in AssetList) + { + asset.OnAssetClick(assetPath, e); + } + } + }; + } + + // private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) + // { + // if (AssetList == null) + // { + // return; + // } + + // foreach (var asset in AssetList) + // { + // foreach (string assetPath in importedAssets) + // { + // asset.OnAssetImported(assetPath); + // } + // foreach (string assetPath in deletedAssets) + // { + // asset.OnAssetDeleted(assetPath); + // } + // for (int i = 0; i < movedAssets.Length; i++) + // { + // asset.OnAssetMoved(movedAssets[i], movedFromAssetPaths[i]); + // } + // } + // } + } +} \ No newline at end of file diff --git a/Editor/Core/ProjectSetting/UnityAsset_Postprocessor.cs.meta b/Editor/ReadySet/Content/UnityAsset_Postprocessor.cs.meta similarity index 100% rename from Editor/Core/ProjectSetting/UnityAsset_Postprocessor.cs.meta rename to Editor/ReadySet/Content/UnityAsset_Postprocessor.cs.meta diff --git a/Editor/ReadySet/Tools/AnimatorScriptGenerator.cs b/Editor/ReadySet/Tools/AnimatorScriptGenerator.cs index b5d512f..9267557 100644 --- a/Editor/ReadySet/Tools/AnimatorScriptGenerator.cs +++ b/Editor/ReadySet/Tools/AnimatorScriptGenerator.cs @@ -14,9 +14,9 @@ public class AnimatorScriptGenerator public static void GenerateScriptsFromAnimators() { ProjectSettingAsset TargetStorage; - if (ProjectSettingWindow.GetSettingStorage(out TargetStorage)) + if (RealMethod_ProjectSetting.GetSettingStorage(out TargetStorage)) { - outputFolder = TargetStorage.FindAddres(ProjectSettingAsset.IdentityAsset.AnimatorParam).Path; + outputFolder = TargetStorage[ProjectSettingAsset.AssetFormat.Script] + "/General/AnimatorParam"; } else { diff --git a/Editor/ReadySet/Tools/ClassViewerWindow.cs b/Editor/ReadySet/Tools/ClassViewerWindow.cs new file mode 100644 index 0000000..6a25e38 --- /dev/null +++ b/Editor/ReadySet/Tools/ClassViewerWindow.cs @@ -0,0 +1,264 @@ +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; +using System.IO; + +namespace RealMethod.Editor +{ + [System.Serializable] + public class ClassTreeNode + { + public string name; + public string toturial; // text after -> + public string link; // text afeter https + public List children = new List(); + } + + public class ClassViewerWindow : EditorWindow + { + private List rootNodes = new List(); + private Dictionary foldoutStates = new Dictionary(); + + private TextAsset textFile; + private Vector2 scrollPos; + private string searchQuery = ""; + + [MenuItem("Tools/RealMethod/ClassViewer")] + public static void Open() + { + GetWindow("ClassViewer"); + } + + // Unity Methods + private void OnEnable() + { + string ClassViewPath = "Assets/Realmethod/Documentation/Information/ClassViewer.txt"; // Just for Test + // string ClassViewPath = Path.Combine(RM_CoreEditor.Documentation, "ClassViewer.txt"); + if (!File.Exists(ClassViewPath)) + { + Debug.LogError($"ClassView file not found: {ClassViewPath}"); + Close(); + } + string ClassFile = File.ReadAllText(ClassViewPath); + LoadFromText(ClassFile); + } + private void OnGUI() + { + EditorGUILayout.BeginHorizontal(); + if (GUILayout.Button("Collaps")) + { + foreach (var node in rootNodes) + { + FoldLine(node, false); + } + } + if (GUILayout.Button("Expanded")) + { + foreach (var node in rootNodes) + { + FoldLine(node, true); + } + } + EditorGUILayout.EndHorizontal(); + EditorGUILayout.Space(); + searchQuery = EditorGUILayout.TextField("Search", searchQuery); + + EditorGUILayout.Space(); + + // 🔽 Scroll View + scrollPos = EditorGUILayout.BeginScrollView(scrollPos); + + foreach (var node in rootNodes) + { + DrawNodeWithSearch(node, 0); + } + + EditorGUILayout.EndScrollView(); + // 🔼 Scroll View + } + + private void LoadFromText(string text) + { + rootNodes.Clear(); + foldoutStates.Clear(); + + string[] lines = text.Split('\n'); + Stack stack = new Stack(); + + foreach (string rawLine in lines) + { + if (string.IsNullOrWhiteSpace(rawLine)) + continue; + + int indent = CountIndent(rawLine); + string trimmed = rawLine.Trim(); + + string nodeName = trimmed; + string endText = null; + string linkText = null; + + // 🔹 Parse "-> End Text" + string middleText = null; + int arrowIndex = trimmed.IndexOf("->"); + if (arrowIndex >= 0) + { + nodeName = trimmed.Substring(0, arrowIndex).Trim(); + middleText = trimmed.Substring(arrowIndex + 2).Trim(); + } + + if (middleText != null) + { + int linkindex = middleText.IndexOf("https"); + if (linkindex >= 0) + { + endText = middleText.Substring(0, linkindex).Trim(); + linkText = middleText.Substring(linkindex).Trim(); + } + else + { + endText = middleText; + } + } + + ClassTreeNode node = new ClassTreeNode + { + name = nodeName, + toturial = endText, + link = linkText + }; + + if (indent == 0) + { + rootNodes.Add(node); + stack.Clear(); + stack.Push(node); + } + else + { + while (stack.Count > indent) + stack.Pop(); + + stack.Peek().children.Add(node); + stack.Push(node); + } + } + } + private int CountIndent(string line) + { + int spaces = 0; + foreach (char c in line) + { + if (c == ' ') + spaces++; + else + break; + } + return spaces / 2; // 2 spaces = 1 level + } + + private bool DrawNodeWithSearch(ClassTreeNode node, int indent) + { + // Check if node matches search + bool nodeMatches = string.IsNullOrEmpty(searchQuery) || + node.name.ToLower().Contains(searchQuery.ToLower()) || + node.toturial?.ToLower().Contains(searchQuery.ToLower()) == true; + + // Check if any child matches + bool anyChildMatches = false; + foreach (var child in node.children) + { + if (NodeOrChildrenMatchSearch(child)) + { + anyChildMatches = true; + break; + } + } + + // If nothing matches, skip + if (!nodeMatches && !anyChildMatches) + return false; + + // Draw parent node + if (!foldoutStates.ContainsKey(node)) + foldoutStates[node] = true; + + EditorGUILayout.BeginHorizontal(); + GUILayout.Space(indent * 20); + foldoutStates[node] = + EditorGUILayout.Foldout(foldoutStates[node], node.name, true); + EditorGUILayout.EndHorizontal(); + + // Show endText as link if expanded + if (foldoutStates[node] && !string.IsNullOrEmpty(node.toturial)) + { + EditorGUILayout.BeginHorizontal(); + GUILayout.Space(indent * 20); + EditorGUILayout.BeginVertical("box"); + GUIStyle richLabel = new GUIStyle(EditorStyles.wordWrappedLabel); + richLabel.richText = true; + GUILayout.Label(node.toturial, richLabel); + //GUILayout.Label(node.toturial, new GUIStyle(EditorStyles.wordWrappedLabel)); + if (!string.IsNullOrEmpty(node.link)) + { + if (GUILayout.Button("Link", EditorStyles.linkLabel)) + { + OnEndTextClicked(node); + } + } + EditorGUILayout.EndVertical(); + EditorGUILayout.EndHorizontal(); + } + + + + // Draw children recursively only if expanded + if (foldoutStates[node]) + { + foreach (var child in node.children) + { + DrawNodeWithSearch(child, indent + 1); + } + } + + return true; + } + // Helper: check recursively if a node or any child matches search + private bool NodeOrChildrenMatchSearch(ClassTreeNode node) + { + bool match = string.IsNullOrEmpty(searchQuery) || + node.name.ToLower().Contains(searchQuery.ToLower()) || + node.toturial?.ToLower().Contains(searchQuery.ToLower()) == true; + + if (match) + return true; + + foreach (var child in node.children) + { + if (NodeOrChildrenMatchSearch(child)) + return true; + } + + return false; + } + private void FoldLine(ClassTreeNode node, bool result) + { + foldoutStates[node] = result; + if (foldoutStates[node]) + { + foreach (var child in node.children) + FoldLine(child, result); + } + } + private void OnEndTextClicked(ClassTreeNode node) + { + if (EditorUtility.DisplayDialog("OpenLink", node.link, "Open", "Cancel")) + { + // 3️⃣ OR open URL (if endText is a URL) + Application.OpenURL(node.link); + } + // 2️⃣ OR ping asset + // EditorGUIUtility.PingObject(yourObject); + } + + } +} diff --git a/Editor/ReadySet/Tools/ClassViewerWindow.cs.meta b/Editor/ReadySet/Tools/ClassViewerWindow.cs.meta new file mode 100644 index 0000000..4a30911 --- /dev/null +++ b/Editor/ReadySet/Tools/ClassViewerWindow.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 15438cb6b5774124f90a5a787acca4c8 \ No newline at end of file diff --git a/Editor/Toolkit/PCG/PCGEditorWindow.cs b/Editor/Toolkit/PCG/PCGEditorWindow.cs index a558103..01da03e 100644 --- a/Editor/Toolkit/PCG/PCGEditorWindow.cs +++ b/Editor/Toolkit/PCG/PCGEditorWindow.cs @@ -107,9 +107,9 @@ private void OnEnable() Cash = new EP_ScriptableObject("Cash", this); SelectedData = new EP_List("Data", this); CashAddress = new EP_String("Address", this); - CashAddress.SetValue(ProjectSetting.FindAddres(ProjectSettingAsset.IdentityAsset.PCG).Path + "/PCGCashAsset.asset"); + CashAddress.SetValue(ProjectSetting[ProjectSettingAsset.AssetFormat.ScriptableObject] + "/PCGCashAsset.asset"); PrefabAddress = new EP_String("Address", this); - PrefabAddress.SetValue(ProjectSetting.FindAddres(ProjectSettingAsset.IdentityAsset.Prefab).Path + "/PCG.prefab"); + PrefabAddress.SetValue(ProjectSetting.GetFolderAddressByType(ProjectSettingAsset.AssetFormat.Prefab).AssetPath + "/PCG.prefab"); ExportType = new EP_Enum("ExportType", this); // Subscribe to the selectionChanged event diff --git a/Editor/Toolkit/PCG/PCG_UnityAsset.cs b/Editor/Toolkit/PCG/PCG_UnityAsset.cs index 1a976e3..01de736 100644 --- a/Editor/Toolkit/PCG/PCG_UnityAsset.cs +++ b/Editor/Toolkit/PCG/PCG_UnityAsset.cs @@ -1,6 +1,6 @@ namespace RealMethod.Editor { - public class PCGResource_UnityAsset : AssetHandeler + public class PCGResource_UnityAsset : AssetProcess { protected override void Initialized() { @@ -24,7 +24,7 @@ protected override void DoubleClick(PCGResourceConfig asset) } } - public class PCGGeneration_UnityAsset : AssetHandeler + public class PCGGeneration_UnityAsset : AssetProcess { protected override void Initialized() { @@ -48,7 +48,7 @@ protected override void DoubleClick(PCGGenerationAsset asset) } } - public class PCGCash_UnityAsset : AssetHandeler + public class PCGCash_UnityAsset : AssetProcess { protected override void Initialized() { diff --git a/Editor/Toolkit/TerrainTools/TerrainEditor.cs b/Editor/Toolkit/TerrainTools/TerrainEditor.cs index cf50fcc..be899cd 100644 --- a/Editor/Toolkit/TerrainTools/TerrainEditor.cs +++ b/Editor/Toolkit/TerrainTools/TerrainEditor.cs @@ -94,7 +94,7 @@ public ExportTreeData(Terrain owner, UnityEditor.Editor editor) CashFile = new EP_ScriptableObject("Cash", editor); CashAddress = new EP_String("Address", editor); - CashAddress.SetValue(ProjectSetting.FindAddres(ProjectSettingAsset.IdentityAsset.PCG).Path + "/TerrainCash.asset"); + CashAddress.SetValue(ProjectSetting[ProjectSettingAsset.AssetFormat.ScriptableObject] + "/TerrainCash.asset"); } public void OnRender() diff --git a/README.md b/README.md index abe4bee..718e059 100644 --- a/README.md +++ b/README.md @@ -1,148 +1,31 @@ -# 🌌 RealMethod -*A lightweight Unity architecture for scalable and modular project creation.* ---- - -## ✨ Overview - -**RealMethod** is a Unity package that defines a clean and consistent **project architecture**. -It focuses on separation of responsibility and lifecycle management, allowing developers to build games that are easily extendable, maintainable, and modular. - -RealMethod introduces five main layers: -- **Core** – Defines the base relationships between Game, World, Manager, and Service. -- **Library** – Common utility functions, interfaces, and shared components. -- **Pattern** – Predefined architecture patterns and reusable managers. -- **Toolkit** – Ready-to-use tools, inspectors, and debugging helpers. -- **ReadySet** – Example setups and templates for quick project initialization. - ---- - -## 🧩 Core Architecture - -### **Game** -- The **Game** class is the entry point of the RealMethod framework. -- It is a **singleton** that lives for the entire game lifecycle (until quit). -- Responsible for global operations such as: - - Scene management (`OpenScene`, `ReloadScene`, etc.) - - Time control (`SetGameSpeed`) - - Service construction and destruction - -All **Managers** and **Services** are registered through `Game`, ensuring consistent global access. - ---- - -### **World** -- The **World** class exists **once per scene** and acts as the runtime context for that scene. -- It initializes all **World-scoped Managers** and prepares the scene for gameplay. -- While **Game** persists between scenes, **World** resets when scenes change. -- Required in every scene that uses RealMethod. - ---- - -### **Manager** -- A **Manager** defines a controllable system within a given scope (Game or World). -- Implements the `IManager` interface. -- Managers are initialized **before Unity’s Awake/Start** callbacks. -- Can live in one of two scopes: - - **Game scope:** persists between scenes (registered under Game). - - **World scope:** tied to a specific scene (registered under World). - -Both Game and World automatically call `DontDestroyOnLoad` for their scoped Managers. - ---- - -### **Service** -- A **Service** is a lightweight, non-MonoBehaviour object managed by the Game. -- Created dynamically using `Game.CreateService()`. -- All Managers automatically recognize and can interact with new Services. -- Can be destroyed at any time via `Game.DestroyService()`. -- Ideal for runtime systems like data caching, analytics, or network clients. - ---- - -## ⚙️ Lifecycle Summary - -| Layer | Scope | Lifetime | Created By | Notes | -|-------|--------|-----------|-------------|-------| -| Game | Global | Until Quit | App Start | Singleton | -| World | Scene | Per Scene | Scene Load | Scene Context | -| Manager | Global/Scene | Anytime | Game/World | Registered in Game/Scene scope | -| Service | Runtime | Dynamic | Game | Fully managed objects | - ---- - -## 📦 Package Structure -RealMethod is organized into clearly separated runtime, editor, and resource layers — each designed to keep your project scalable, readable, and modular. -### 🧩 Runtime -Core runtime code that runs inside Unity builds. - -- **Core/** - - `Architecture/` – Core systems such as **Game**, **World**, **Manager**, and **Service**. - - `Attributes/` – Custom attributes used across the framework. - - `Definitions/` – Global enums, tags, and static definitions. - - `ProjectSetting/` – Runtime configuration and project constants. - -- **Library/** - - `Extension/` – Unity extensions and helper methods. - - `Interfaces/` – Common interfaces for modular communication. - - `SharedScripts/` – Shared data structures (Classes, Enums, Structs). - - `Utilities/` – Core utility scripts. - - `Vendor/SerializableDictionary/` – Third-party generic dictionary support. - -- **Pattern/** - - `Components/` – Base components using RealMethod patterns. - - `DataAssets/` – ScriptableObject-based configuration. - - `DesignPatterns/` – Common gameplay and architecture patterns. - - `Managers/` – Runtime manager implementations. - - `Services/` – Service classes managed by the Game/World. - -- **ReadySet/** - - `Commands/` – Command execution framework (Executors, Tasks). - - `Components/` – Common building blocks (Input, Method, Physics, Time, UI, Visual). - - `DefaultsClass/` – Predefined data and logic templates. - - `Managers/` – Ready-to-use manager classes. - - `Presets/` – Resource and pooling presets (PoolAsset, ResourceAsset, Task). - - `Services/` – Prebuilt services and systems. - -- **Toolkit/** - - `Ability/` – Ability system and samples. - - `Actor/` – Actor handling and lifecycle logic. - - `CSVFile/` – CSV parsing and data import tools. - - `CurveViewer/` – Editor curve visualization. - - `Interaction/` – Interaction handling system. - - `Inventory/` – Inventory architecture. - - `PCG/` – Procedural generation tools and resources. - - `Pickup/` – Item pickup logic. - - `RPG/` – RPG systems (Resource, StatSystem). - - `Tutorial/` – Tutorial system and utilities. - - `Upgrade/` – Upgrade and progression tools. - ---- - -### 🛠️ Editor -Unity Editor extensions for all RealMethod layers. - -- **Core/** – Editor scripts for Architecture, Definitions, and Project Settings. -- **Library/** – Shared scripts, vendor tools, and utilities. -- **Pattern/** – Custom editors for data assets and managers. -- **ReadySet/** – Editor tools for content setup and asset generation. -- **Toolkit/** – Editors for gameplay systems (Ability, Inventory, TerrainTools, etc.). - ---- - -### 🧃 Reservoir -Central storage for framework assets. - -- `Icons/` – Organized icons for Core, Pattern, and Toolkit editors. -- `Prefabs/` – Prefab resources for samples and managers. -- `SceneTemplates/` – Example scenes for quick setup. -- `ScriptTemplates/` – Script templates for fast development. - ---- - -### 🧪 Samples~ -Example projects demonstrating **PopupMessage** and **Tutorial** systems. - ---- - -### 🧠 Tests -Unit and integration tests for **General**, **Inspector**, and **ScenesDot** systems. +# RealMethod +**"`RealMethod` is a minimal, Unreal-inspired architecture for structuring Unity games with clear lifetime, ownership, and system boundaries."** +### Links +* [Documentation(Wiki)](https://github.com/AliJimpa/RealMethod/wiki/Home) +* [Video Tutorials]() +### The Pitch +`RealMethod` is a minimal architectural framework for Unity. It provides a small, explicit structure for organizing game lifetime, world context, and systems, inspired by **Unreal Engine** concepts such as `GameInstance` and `GameMode`. + +`RealMethod` is designed to help programmers build and maintain scalable Unity projects by enforcing clear ownership and predictable lifecycles. It favors simplicity and clarity over abstraction and avoids heavy frameworks, dependency injection containers, or hidden execution flows. `RealMethod` focuses on structure, not gameplay, and does not attempt to replace Unity workflows. + +`RealMethod` is particularly suited for games and real-time applications where long-term maintainability matters: indie and studio projects, system-heavy games, tool-driven workflows, and projects that must evolve over time without architectural collapse. +* Clear ownership and responsibility +* Unreal-like mental model for Unity +* Minimal core, flexible edges +* Scales from small games +* experimental for productions +* Team-friendly and testable architecture +### Quick Setup +Install via Unity Package Manager: +1. Open Package Manager +2. Click Add package from Git URL +3. Paste +>https://github.com/AliJimpa/RealMethod.git +4. Follow the Getting Started guide +>https://github.com/AliJimpa/RealMethod/wiki/Quick-Setup +### Gallery +Examples projects using RealMethod: [Death Pulse](https://github.com/wolfpld/tracy](https://cafebazaar.ir/app/com.Mustard.DeathPulse?l=en)), [Crush Conquest](https://cafebazaar.ir/app/com.Mustard.CrushConquest?l=en), [HeardSpoken]() and [Fireball](). + +| | | +|--|--| +|
![Crush Conquest](https://s.cafebazaar.ir/images/icons/com.Mustard.CrushConquest-7bbd0921-42dc-4c79-8676-5a51a79bc6e4_512x512.png?x-img=v1/format,type_webp,lossless_false/resize,h_256,w_256,lossless_false/optimize) |
![Death Pulse](https://s.cafebazaar.ir/images/icons/com.Mustard.DeathPulse-eb40fcf1-095b-4619-97d5-527a7a86dc4f_512x512.png?x-img=v1/format,type_webp,lossless_false/resize,h_256,w_256,lossless_false/optimize) | diff --git a/Reservoir/ScriptTemplates/GameConfigTemplate.txt b/Reservoir/ScriptTemplates/GameConfigTemplate.txt index 7e7ae08..bca67f2 100644 --- a/Reservoir/ScriptTemplates/GameConfigTemplate.txt +++ b/Reservoir/ScriptTemplates/GameConfigTemplate.txt @@ -4,8 +4,9 @@ using RealMethod; [CreateAssetMenu(fileName = "#SCRIPTNAME#", menuName = "#PROJECTNAME#/#SCRIPTNAME#", order = 1)] public class #SCRIPTNAME# : GameConfig { - // Base GameSettingAsset Methods + // GameConfig Methods public override void Initialized(Game Author) { + // Called after load in Game by GameClass } } diff --git a/Reservoir/ScriptTemplates/GameServiceTemplate.txt b/Reservoir/ScriptTemplates/GameServiceTemplate.txt index f2476d2..fced4a8 100644 --- a/Reservoir/ScriptTemplates/GameServiceTemplate.txt +++ b/Reservoir/ScriptTemplates/GameServiceTemplate.txt @@ -3,22 +3,8 @@ using RealMethod; public class #SCRIPTNAME# : GameService { // Base Service Methods - protected override void OnStart(object Author) { } + protected override void OnStart(object Author) {} protected override void OnNewWorld() { } - protected override void OnNewAdditiveWorld(World target) { } protected override void OnEnd(object Author) { } - // GameService Virtual Methods - // public override IEnumerator GetLoadScneCorotine(SceneReference TargetScene) - // { - // return base.GetLoadScneCorotine(); - // } - // public override IEnumerator GetLoadWorldCorotine(WorldSceneConfig WorldScene) - // { - // return base.GetLoadWorldCorotine(); - // } - // public override IEnumerator GetReloadSWCorotine() - // { - // return base.GetReloadSWCorotine(); - // } } diff --git a/Reservoir/ScriptTemplates/GameTemplate.txt b/Reservoir/ScriptTemplates/GameTemplate.txt index de24ed5..9227586 100644 --- a/Reservoir/ScriptTemplates/GameTemplate.txt +++ b/Reservoir/ScriptTemplates/GameTemplate.txt @@ -3,18 +3,27 @@ using RealMethod; public class #SCRIPTNAME# : Game { - // Base Game Methods - protected override void GameInitialized() + // Game Methods + protected override void OnGameInitialized() { + // Called right after Unity finishes loading the engine before any scene or subsystem is loaded. + // You can insure when Called that GameService , GameConfig and all Managers Initilized. } - protected override void GameStarted() + protected override void OnGameStarted() { + // Called after Unity finishes initializing the engine, but before the first scene is loaded. + // It’s guaranteed to execute before any Awake() or Start() in your scene objects. } - protected override void GameWorldSynced(World NewWorld) + protected override void OnWorldChanged(World NewWorld) { + // Called when each World Initialized + // You can insure the Game.world refrence is valid + // Note: if you use world in scen and load scene as additive not OpenScen that world didnt connect to Game and removed by Main World class } - protected override void GameClosed() + protected override void OnGameClosed() { + // Called when application start exit + // You can insure this called befor and object destroy in scene (yes befor) } diff --git a/Reservoir/ScriptTemplates/ManagerTemplate.txt b/Reservoir/ScriptTemplates/ManagerTemplate.txt index c3ad458..66099c1 100644 --- a/Reservoir/ScriptTemplates/ManagerTemplate.txt +++ b/Reservoir/ScriptTemplates/ManagerTemplate.txt @@ -4,18 +4,20 @@ using RealMethod; public class #SCRIPTNAME# : MonoBehaviour, IGameManager { - // Implement IGameManage Methods - MonoBehaviour IGameManager.GetManagerClass() - { - return this; - } + + + + // Implement IGameManage Interface + MonoBehaviour IGameManager.GetManagerClass() => this; void IGameManager.InitiateManager(bool AlwaysLoaded) { - throw new System.NotImplementedException(); + // Called Just Once Befor any Awake called + // Called when World or Game initializing + // AlwaysLoaded means your manager initialize in GameScope(always valid) or World Scope(destroy after change World) } void IGameManager.ResolveService(Service service, bool active) { - throw new System.NotImplementedException(); + // Called When any Service Created or Deleted in Game } } diff --git a/Reservoir/ScriptTemplates/ServiceTemplate.txt b/Reservoir/ScriptTemplates/ServiceTemplate.txt index 2f89517..a71c6c1 100644 --- a/Reservoir/ScriptTemplates/ServiceTemplate.txt +++ b/Reservoir/ScriptTemplates/ServiceTemplate.txt @@ -6,12 +6,17 @@ public class #SCRIPTNAME# : Service // Service Methods protected override void OnStart(object Author) { + // Called right after Construct and stored in Game + // Called befor any Manager Know this Service Created } protected override void OnNewWorld() { + // Called after each World Initialized } protected override void OnEnd(object Author) { + // Called befor Destroyed. + // Called after all Manager know this Service going to Destroyed } } diff --git a/Reservoir/ScriptTemplates/WorldTemplate.txt b/Reservoir/ScriptTemplates/WorldTemplate.txt index 380a432..e81b175 100644 --- a/Reservoir/ScriptTemplates/WorldTemplate.txt +++ b/Reservoir/ScriptTemplates/WorldTemplate.txt @@ -2,13 +2,16 @@ using RealMethod; public class #SCRIPTNAME# : World { - // World Methods - protected override void AwakeWorld() + protected override void WorldBegin() { + // When World Awake + // Called befor awake and befor any Monobehaviort in Scene (by executionorder) + // You can insure your world is connect to game class and all Manager Initilize & Player Selected or Spawn in Scene } - protected override void DestroyWorld() + protected override void WorldEnd() { + // When World Destroy } } diff --git a/Runtime/Core/Architecture/Game.cs b/Runtime/Core/Architecture/Game.cs index 81997b3..1c30658 100644 --- a/Runtime/Core/Architecture/Game.cs +++ b/Runtime/Core/Architecture/Game.cs @@ -4,15 +4,39 @@ using UnityEngine.SceneManagement; using System.Linq; +#if UNITY_EDITOR +using UnityEditor; +#endif + namespace RealMethod { + /// + /// Core game singleton that manages the active , registered s, + /// configuration and high-level game lifecycle (initialization, start, and shutdown). + /// Derive from this class to implement project-specific behavior for the game's lifecycle hooks. + /// public abstract class Game : MonoBehaviour { - // Public Static Variables + /// + /// Singleton instance of the active game. + /// public static Game Instance; - public static GameConfig Config { get; private set; } + /// + /// The currently active . Updated when a world is initiated. + /// public static World World { get; private set; } + /// + /// The core implementation used for scene/world loading and service events. + /// public static GameService Service { get; private set; } + /// + /// Active game configuration instance. + /// + public static GameConfig Config { get; private set; } + /// + /// Convenience property returning the player GameObject from the current . + /// Returns null and logs a warning if no world is set. + /// public static GameObject Player { get @@ -25,72 +49,155 @@ public static GameObject Player return World.GetPlayerObject(); } } - public static bool isPaused => Time.timeScale == 0; + /// + /// Indicates whether the game is currently paused. + /// Returns true when Time.timeScale is set to 0; otherwise, false. + /// + public static bool IsPaused => Time.timeScale == 0; - // Private Variable + + /// + /// Cached array of managers that were instantiated from configured game prefabs. + /// private IGameManager[] Managers; + /// + /// List of runtime-registered instances owned by the game. + /// private List GameServices; - // Public Functions - public T GetManager() where T : class + + + + /// + /// Initializes the game singleton and core systems on subsystem registration. + /// This sets up the , game , + /// configuration, prefabs and managers and registers quit callbacks. + /// + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void InitializeGame() { - foreach (var manager in Managers) + // Load Project Setting + ProjectSettingAsset ProjectSettings = Resources.Load("RealMethod/RealMethodSetting"); + if (ProjectSettings == null) { - if (manager.GetManagerClass() is T Result) - { - return Result; - } + Debug.LogError("ProjectSettingAsset is missing from Resources folder!"); + Quit(); + return; } - return null; - } - public IGameManager GetManager(string ObjectName) - { - foreach (var manger in Managers) + + // Initiate Game Class + var emptyObject = new GameObject("RealGame"); + Type TargetClass = ProjectSettings.GetGameInstanceType(); + if (TargetClass == null) { - if (manger.GetManagerClass().gameObject.name == ObjectName) + Debug.LogWarning("GameInstanceClass that was empty. DefaultGame Created"); + Instance = emptyObject.AddComponent(); + } + if (typeof(Game).IsAssignableFrom(TargetClass)) + { + Instance = (Game)emptyObject.AddComponent(TargetClass); + } + else + { + Debug.LogWarning($"Component of type {TargetClass} is not assignable from Game. DefaultGame Created"); + Instance = emptyObject.AddComponent(); + } + Instance.OnProjectSeettingLoaded(ref ProjectSettings); + + // Create Game Service + Type targetService = ProjectSettings.GetGameServiceType(); + if (targetService == null) + { + Debug.LogWarning($"GetGameServiceClass that was empty. DefaultGameService Created"); + Service = new DefaultGameService(); + } + if (typeof(Service).IsAssignableFrom(targetService)) + { + try { - return manger; + Service = (GameService)Activator.CreateInstance(targetService); + } + catch (Exception ex) + { + Debug.LogError($"Failed to instantiate {targetService}: {ex.Message}. DefaultGameService Created"); + Service = new DefaultGameService(); } } - return null; - } - // Protected Functions - protected T NeedManager() where T : MonoBehaviour - { - T Result = GetManager(); - if (Result == null) + else { - Result = gameObject.AddComponent(); - Debug.LogWarning($"Manager of type {typeof(T).Name} was not found. [A new one has been added]."); + Debug.LogWarning($"Type {targetService} is not assignable to Service. DefaultGameService Created"); + Service = new DefaultGameService(); + } + Instance.GameServices = new List(3); + ((IMethodSync)Service).BindMainWorldAdd(Instance.Notify_OnWorldInitiate); + ((IService)Service).Created(Instance); + // Set Game Config + if (ProjectSettings.GetGameConfig() != null) + { + Config = ProjectSettings.GetGameConfig(); } - return Result; + else + { + Config = ScriptableObject.CreateInstance(); + } + Config.Initialized(Instance); + + // Initiate GamePrefab & Managers + List CashManagers = new List(5); + foreach (var obj in ProjectSettings.GetGamePrefabs()) + { + if (obj != null) + { + GameObject newobj = Instantiate(obj); + foreach (var manager in newobj.GetComponents()) + { + manager.InitiateManager(true); + CashManagers.Add(manager); + } + DontDestroyOnLoad(newobj); + } + } + Instance.Managers = CashManagers.ToArray(); + + // Unload Project Setting + Resources.UnloadAsset(ProjectSettings); + ProjectSettings = null; + + // Move Self GameObject to DontDestroy + DontDestroyOnLoad(Instance.gameObject); + Application.quitting += Instance.Notify_OnGameQuit; } - // Private Functions - private void OnGameWorldReplaced(World NewWorld) + /// + /// Called before any scene is loaded. Invokes on the active instance. + /// + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + private static void RuntimeBeforeSceneLoad() { - World = NewWorld; - foreach (var service in GameServices) + if (Instance != null) { - service.provider.WorldUpdated(); + Instance.OnGameInitialized(); } - GameWorldSynced(World); } - private void OnGameQuit() + /// + /// Called after a scene has finished loading. Invokes on the active instance. + /// + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] + private static void RuntimeAfterSceneLoad() { - Application.quitting -= OnGameQuit; - if (GameServices != null) + if (Instance != null) { - for (int i = 0; i < GameServices.Count; i++) - { - GameServices[i].provider.Deleted(this); - GameServices.RemoveAt(i); - } + Instance.OnGameStarted(); } - Service.provider.Deleted(this); - GameClosed(); } - // Public Static Functions + + + /// + /// Attempts to cast the global to the specified type . + /// Logs an error and returns null if the cast fails. + /// + /// The target type to cast the instance to. + /// The casted instance of type , or null on failure. public static T CastInstance() where T : class { if (Instance is T CastedInstance) @@ -103,6 +210,12 @@ public static T CastInstance() where T : class return null; } } + /// + /// Attempts to cast the current to the specified type . + /// Logs an error and returns null if the cast fails. + /// + /// The target type to cast the world to. + /// The casted world of type , or null on failure. public static T CastWorld() where T : class { if (World is T CastedWorld) @@ -115,6 +228,13 @@ public static T CastWorld() where T : class return null; } } + /// + /// Adds a new service of type to the game if one does not already exist. + /// Newly created service will be bound to managers and notified to the global service system. + /// + /// Service type to add. + /// The object responsible for creating the service (used for provider callbacks). + /// The newly created service instance, or null if a service of the same type already exists. public static T AddService(object author) where T : Service, new() { // Check if a service of this type already exists @@ -126,7 +246,7 @@ public static T CastWorld() where T : class // Create Service T newService = new T(); - newService.provider.Created(author); + ((IService)newService).Created(author); if (Instance.Managers != null) { foreach (var manager in Instance.Managers) @@ -134,10 +254,17 @@ public static T CastWorld() where T : class manager.ResolveService(newService, true); } } - Service.OnServiceCreated?.Invoke(newService); + ((IMethodSync)Service).ServiceCreated(newService); Instance.GameServices.Add(newService); return newService; } + /// + /// Removes the first service instance of type if present. + /// Managers and the global service system will be notified of the removal. + /// + /// Service type to remove. + /// The object requesting the removal (used for provider callbacks). + /// true if a service was found and removed; otherwise false. public static bool RemoveService(object author) where T : Service { var service = Instance.GameServices.FirstOrDefault(s => s.GetType() == typeof(T)); @@ -150,8 +277,8 @@ public static bool RemoveService(object author) where T : Service manager.ResolveService(service, false); } } - Service.OnServiceRemoved?.Invoke(service); - service.provider.Deleted(author); + ((IMethodSync)Service).ServiceRemoved(service); + ((IService)service).Deleted(author); Instance.GameServices.Remove(service); return true; } @@ -159,10 +286,21 @@ public static bool RemoveService(object author) where T : Service Debug.LogWarning($"Service of type {typeof(T).Name} not found to remove."); return false; } + /// + /// Retrieves the first service instance of type if available. + /// + /// Service type to retrieve. + /// The service instance of type , or null if not found. public static T GetService() where T : Service { return Instance.GameServices.OfType().FirstOrDefault(); } + /// + /// Retrieves a service by its concrete . + /// Returns null and logs an error if the provided type is not a . + /// + /// Concrete service type to look up. + /// The matching service instance, or null if not found or invalid type. public static Service GetService(Type type) { if (!typeof(Service).IsAssignableFrom(type)) @@ -173,15 +311,31 @@ public static Service GetService(Type type) return Instance.GameServices.FirstOrDefault(s => s.GetType() == type); } + /// + /// Attempts to find a service of type . + /// + /// Service type to find. + /// Out parameter that receives the service if found. + /// true if the service was found; otherwise false. public static bool TryFindService(out T service) where T : Service { service = Instance.GameServices.OfType().FirstOrDefault(); return service != null; } + /// + /// Returns the type names of all registered game services. + /// + /// Array of service type names. public string[] GetAllServiceNames() { return GameServices.Select(service => service.GetType().Name).ToArray(); } + /// + /// Requests a scene load by build index via the configured . + /// If the requested scene is already active, a warning is logged and null is returned. + /// + /// Build index of the scene to open. + /// A driving the load operation, or null if not started. public static Coroutine OpenScene(int sceneIndex) { if (SceneManager.GetActiveScene().buildIndex != sceneIndex) @@ -194,10 +348,21 @@ public static Coroutine OpenScene(int sceneIndex) return null; } } + /// + /// Requests a scene load using a . + /// + /// Reference describing the scene to load. + /// A driving the load operation, or null if not started. public static Coroutine OpenScene(SceneReference scene) { return OpenScene(scene.ScneName); } + /// + /// Requests a scene load by name via the configured . + /// If the requested scene is already active, a warning is logged and null is returned. + /// + /// Name of the scene to open. + /// A driving the load operation, or null if not started. public static Coroutine OpenScene(string sceneName) { if (SceneManager.GetActiveScene().name != sceneName) @@ -210,6 +375,12 @@ public static Coroutine OpenScene(string sceneName) return null; } } + /// + /// Loads a multi-scene world configuration using the provided . + /// If the persistent scene for the world is already loaded, a warning is logged and null is returned. + /// + /// World scene configuration to load. + /// A driving the world load operation, or null if not started. public static Coroutine OpenWorld(WorldSceneConfig WorldScene) { if (SceneManager.GetActiveScene().buildIndex != SceneManager.GetSceneByPath(WorldScene.Persistent).buildIndex) @@ -222,10 +393,20 @@ public static Coroutine OpenWorld(WorldSceneConfig WorldScene) return null; } } + /// + /// Reloads the currently active scene via the configured . + /// + /// A driving the reload operation. public static Coroutine ReOpenScene() { return Instance.StartCoroutine(Service.GetLoadScneCorotine(SceneManager.GetActiveScene().name)); ; } + /// + /// Finds a manager of type in the current , + /// falling back to the global game managers if not found. + /// + /// Manager type to find. + /// An instance of the manager if found; otherwise null. public static T FindManager() where T : MonoBehaviour { T Result = null; @@ -241,159 +422,167 @@ public static T FindManager() where T : MonoBehaviour } return Instance.GetManager(); } + /// + /// Parents the provided GameObject to the game root instance. + /// + /// The GameObject to hold under the game root. public static void HoldGameObject(GameObject Target) { Target.transform.SetParent(Instance.transform); } - public static bool UnHoldGameObject(string GameObjectName, GameObject Target) + /// + /// Searches for a child GameObject with the given name and reparents it to . + /// + /// Name of the child GameObject to find. + /// New parent GameObject to assign. + /// true if the child was found and reparented; otherwise false. + public static bool TryUnholdGameObject(string GameObjectName, out GameObject result) { Transform[] Childs = Instance.GetComponentsInChildren(); foreach (var item in Childs) { if (item.gameObject.name == GameObjectName) { - item.SetParent(Target.transform); + item.SetParent(null); + result = item.gameObject; return true; } } + result = null; return false; } + /// + /// Pauses or resumes the game by setting . + /// + /// If true the game is paused; otherwise resumed. public static void SetPause(bool paused) { Time.timeScale = paused ? 0 : 1; } + /// + /// Sets the global time scale and optionally adjusts fixed delta time for physics safety. + /// + /// New time scale to apply. + /// If true adjusts to keep physics stable. public static void SetSpeed(float speed, bool physicSafe = true) { Time.timeScale = speed; if (physicSafe) Time.fixedDeltaTime = 0.02f * Time.timeScale; // Keeps physics in sync } + /// + /// Quits the application. In the Unity Editor this stops play mode instead. + /// + public static void Quit() + { +#if UNITY_EDITOR + EditorApplication.isPlaying = false; +#else + Application.Quit(); +#endif + } - // Private Static Functions - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void InitializeGame() + + /// + /// Retrieves a manager of type from this game's instantiated managers. + /// + /// Manager type to retrieve. + /// The manager instance if found; otherwise null. + public T GetManager() where T : class { - Game[] components = FindObjectsByType(FindObjectsSortMode.InstanceID); - if (components.Length > 1) - { - Debug.LogError($"Expected exactly one active 'Game' component in the scene, but found {components.Length}."); - return; - } - // Load Project Setting - ProjectSettingAsset ProjectSettings = Resources.Load("RealMethod/RealMethodSetting"); - if (ProjectSettings != null) + foreach (var manager in Managers) { - // Initiate Game Class - if (components.Length == 0) - { - var emptyObject = new GameObject("RealGame"); - Type TargetClass = ProjectSettings.GetGameInstanceClass(); - if (TargetClass != null) - { - Instance = (Game)emptyObject.AddComponent(TargetClass); - if (Instance == null) - { - Debug.LogError($"Component of type {TargetClass} is not assignable from Game."); - Instance = emptyObject.AddComponent(); - } - } - else - { - Instance = emptyObject.AddComponent(); - } - } - else - { - Instance = components[0]; - } - // Create Game Service - Type targetService = ProjectSettings.GetGameServiceClass(); - if (targetService != null) - { - if (typeof(Service).IsAssignableFrom(targetService)) - { - try - { - Service = (GameService)Activator.CreateInstance(targetService); - } - catch (Exception ex) - { - Debug.LogError($"Failed to instantiate {targetService}: {ex.Message}"); - Service = new DefaultGameService(); - } - } - else - { - Debug.LogError($"Type {targetService} is not assignable to Service."); - Service = new DefaultGameService(); - } - } - else - { - Service = new DefaultGameService(); - } - Instance.GameServices = new List(3); - Service.OnWorldUpdate += Instance.OnGameWorldReplaced; - Service.provider.Created(Instance); - // Set Game Config - if (ProjectSettings.GetGameConfig() != null) - { - Config = ProjectSettings.GetGameConfig(); - } - else - { - Config = ScriptableObject.CreateInstance(); - } - Config.Initialized(Instance); - // Initiate GamePrefab & Managers - List CashManagers = new List(5); - foreach (var obj in ProjectSettings.GetGamePrefabs()) + if (manager.GetManagerClass() is T Result) { - if (obj != null) - { - GameObject newobj = Instantiate(obj); - foreach (var manager in newobj.GetComponents()) - { - manager.InitiateManager(true); - CashManagers.Add(manager); - } - DontDestroyOnLoad(newobj); - } + return Result; } - Instance.Managers = CashManagers.ToArray(); - // Unload Project Setting - Resources.UnloadAsset(ProjectSettings); - // Move Self GameObject to DontDestroy - DontDestroyOnLoad(Instance.gameObject); - Application.quitting += Instance.OnGameQuit; } - else + return null; + } + /// + /// Retrieves an by the name of its GameObject. + /// + /// Name of the manager GameObject to find. + /// The matching , or null if none match. + public IGameManager GetManager(string ObjectName) + { + foreach (var manger in Managers) { - Debug.LogError("ProjectSettingAsset is missing from Resources folder!"); + if (manger.GetManagerClass().gameObject.name == ObjectName) + { + return manger; + } } + return null; } - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] - private static void RuntimeBeforeSceneLoad() + + + /// + /// Called after GameInstance Created befor GameService , GameConfig & Managers Initilized + /// if you use custom PtojectSetttingAsset class you can use instances after loaded in game + /// this refrence unloaded befor OnGameInitialized Called. + /// + /// ProjectSettingAsset refrence + protected virtual void OnProjectSeettingLoaded(ref ProjectSettingAsset setting) { - if (Instance != null) + // Nothing todo + } + + + /// + /// Callback invoked when a new is created or assigned. + /// Updates the static reference and notifies all services. + /// + /// The newly initiated world instance. + private void Notify_OnWorldInitiate(World NewWorld) + { + World = NewWorld; + foreach (var service in GameServices) { - Instance.GameInitialized(); + ((IService)service).WorldUpdated(); } + OnWorldChanged(World); } - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] - private static void RuntimeAfterSceneLoad() + /// + /// Handles application quit events: unbinds world callbacks, deletes services and invokes . + /// + private void Notify_OnGameQuit() { - if (Instance != null) + Application.quitting -= Notify_OnGameQuit; + ((IMethodSync)Service).UnbindMainWorldAdd(); + if (GameServices != null) { - Instance.GameStarted(); + for (int i = 0; i < GameServices.Count; i++) + { + ((IService)GameServices[i]).Deleted(this); + GameServices.RemoveAt(i); + } } + ((IService)Service).Deleted(this); + OnGameClosed(); } - // Abstract Methods - protected abstract void GameInitialized(); - protected abstract void GameStarted(); - protected abstract void GameWorldSynced(World NewWorld); - protected abstract void GameClosed(); + + /// + /// Called once when the game framework has finished initial initialization. + /// Implement this to perform game-specific initialization logic. + /// + protected abstract void OnGameInitialized(); + /// + /// Called after the first scene has been loaded and the game has started. + /// Implement this to perform logic that should run once the first scene is active. + /// + protected abstract void OnGameStarted(); + /// + /// Called when the current reference changes. + /// Implement to react to world switches. + /// + /// The new active world. + protected abstract void OnWorldChanged(World NewWorld); + /// + /// Called when the game is closing. Implement to perform cleanup logic. + /// + protected abstract void OnGameClosed(); } } \ No newline at end of file diff --git a/Runtime/Core/Architecture/GameConfig.cs b/Runtime/Core/Architecture/GameConfig.cs index 109059d..98c9ad6 100644 --- a/Runtime/Core/Architecture/GameConfig.cs +++ b/Runtime/Core/Architecture/GameConfig.cs @@ -3,14 +3,28 @@ namespace RealMethod { + /// + /// Base configuration asset for game-wide settings and initialization logic. + /// public abstract class GameConfig : ConfigAsset { [Header("Game")] [SerializeField] private float fadeTime = 0; + /// + /// Gets the duration (in seconds) used for screen fade transitions. + /// public float FadeTime => fadeTime; + /// + /// Called when the game is initialized, allowing the configuration + /// to apply settings or perform setup logic. + /// + /// + /// The Game instance responsible for initialization. + /// public abstract void Initialized(Game Author); } + } \ No newline at end of file diff --git a/Runtime/Core/Architecture/GameManager.cs b/Runtime/Core/Architecture/GameManager.cs deleted file mode 100644 index bf5c6e7..0000000 --- a/Runtime/Core/Architecture/GameManager.cs +++ /dev/null @@ -1,43 +0,0 @@ -using UnityEngine; - -namespace RealMethod -{ - public interface IGameManager - { - MonoBehaviour GetManagerClass(); - void InitiateManager(bool AlwaysLoaded); - void ResolveService(Service service, bool active); - } - - public interface IService - { - void Created(object author); - void WorldUpdated(); - void Deleted(object author); - } - - public abstract class Service : IService - { - public IService provider => this; - - // Implement IService Interface - void IService.Created(object author) - { - OnStart(author); - } - void IService.WorldUpdated() - { - OnNewWorld(); - } - void IService.Deleted(object author) - { - OnEnd(author); - } - - // Abstract Method - protected abstract void OnStart(object Author); - protected abstract void OnNewWorld(); - protected abstract void OnEnd(object Author); - } - -} \ No newline at end of file diff --git a/Runtime/Core/Architecture/GameService.cs b/Runtime/Core/Architecture/GameService.cs index 69717e5..9f2f93d 100644 --- a/Runtime/Core/Architecture/GameService.cs +++ b/Runtime/Core/Architecture/GameService.cs @@ -8,43 +8,165 @@ namespace RealMethod { - public interface IWorldSync + /// + /// Defines synchronization hooks used internally to coordinate + /// worlds and services during runtime initialization. + /// + public interface IMethodSync { + /// + /// Introduces a newly created world to the system. + /// + /// The world instance being introduced. + /// + /// True if the world is treated as the main world; + /// false if it is considered a side/additive world. + /// bool IntroduceWorld(World world); + /// + /// Binds a callback invoked when the main world is added. + /// + void BindMainWorldAdd(Action func); + /// + /// Unbinds the main world added callback. + /// + void UnbindMainWorldAdd(); + /// + /// Binds a callback invoked when a side (additive) world is added. + /// + void BindSideWorldAdd(Action func); + /// + /// Unbinds the side world added callback. + /// + void UnbindSideWorldAdd(); + /// + /// Notifies the system that a service has been created. + /// + void ServiceCreated(Service service); + /// + /// Notifies the system that a service has been removed. + /// + void ServiceRemoved(Service service); + /// + /// Binds a callback invoked when services are added or removed. + /// + /// + /// Callback receiving the service instance and its active state. + /// + void BindServicesUpdated(Action func); + /// + /// Unbinds the service update callback. + /// + void UnbindServicesUpdated(); } - public abstract class GameService : Service, IWorldSync + + /// + /// Base class for any game services in the framework. + /// Provides core functionality for: + /// + /// World synchronization (main and additive worlds) + /// Service lifecycle notifications (created/removed) + /// Scene and world loading with progress reporting + /// + /// Inherits from and implements + /// to integrate with the game's internal world and service management system. + /// + public abstract class GameService : Service, IMethodSync { - public IWorldSync Worldprovider => this; - // Game Structure - public Action OnWorldUpdate; - public Action OnAdditiveWorld; - public Action OnServiceCreated; - public Action OnServiceRemoved; - // Load Scene - public Action OnSceneLoading; - public Action OnSceneLoadingProcess; + /// + /// Event invoked when a scene or world starts or finishes loading. + /// The boolean parameter is true when loading starts and false when loading ends. + /// + public event Action OnSceneLoading; + /// + /// Event invoked during scene or world loading to report progress. + /// The float parameter represents the loading progress from 0 (start) to 1 (complete). + /// + public event Action OnSceneLoadingProcess; + /// + /// Indicates whether a scene or world load operation is currently in progress. + /// public bool IsLoading { get; protected set; } + // Game Structure + private Action MainWorldEvent; + private Action SideWorldEvent; + private Action ServiceEvents; + + - // Implement IWorldSync Interface - // Any World in Awake time call this method - bool IWorldSync.IntroduceWorld(World world) + // Implement IMethodSync Interface + bool IMethodSync.IntroduceWorld(World world) { if (Game.World == null) { - OnWorldUpdate?.Invoke(world); + MainWorldEvent?.Invoke(world); return true; } else { - OnNewAdditiveWorld(world); - OnAdditiveWorld?.Invoke(world); + SideWorldEvent?.Invoke(world); return false; } } + void IMethodSync.BindMainWorldAdd(Action func) + { + if (MainWorldEvent != null) + { + Debug.LogWarning("BindMainWorldAdd is already binded this interface is internal didnt use in another script or your game"); + return; + } + MainWorldEvent = func; + } + void IMethodSync.UnbindMainWorldAdd() + { + MainWorldEvent = null; + } + void IMethodSync.BindSideWorldAdd(Action func) + { + if (SideWorldEvent != null) + { + Debug.LogWarning("BindSideWorldAdd is already binded this interface is internal didnt use in another script or your game"); + return; + } + SideWorldEvent = func; + } + void IMethodSync.UnbindSideWorldAdd() + { + SideWorldEvent = null; + } + void IMethodSync.ServiceCreated(Service service) + { + ServiceEvents.Invoke(service, true); + } + void IMethodSync.ServiceRemoved(Service service) + { + ServiceEvents.Invoke(service, false); + } + void IMethodSync.BindServicesUpdated(Action func) + { + if (ServiceEvents != null) + { + Debug.LogWarning("BindServicesUpdated is already binded this interface is internal didnt use in another script or your game"); + return; + } + ServiceEvents = func; + } + void IMethodSync.UnbindServicesUpdated() + { + ServiceEvents = null; + } + - // public Methods + /// + /// Starts loading a scene by name using a coroutine. + /// + /// The name of the scene to load. + /// + /// An IEnumerator coroutine for loading the scene, + /// or null if a load operation is already in progress. + /// public virtual IEnumerator GetLoadScneCorotine(string sceneName) { if (IsLoading == true) @@ -54,6 +176,14 @@ public virtual IEnumerator GetLoadScneCorotine(string sceneName) } return LoadSceneAsync(sceneName); } + /// + /// Starts loading a scene by build index using a coroutine. + /// + /// The build index of the scene to load. + /// + /// An IEnumerator coroutine for loading the scene, + /// or null if a load operation is already in progress. + /// public virtual IEnumerator GetLoadScneCorotine(int sceneIndex) { if (IsLoading == true) @@ -63,6 +193,14 @@ public virtual IEnumerator GetLoadScneCorotine(int sceneIndex) } return LoadSceneAsync(string.Empty, sceneIndex); } + /// + /// Starts loading a world configuration using a coroutine. + /// + /// The world scene configuration to load. + /// + /// An IEnumerator coroutine for loading the world, + /// or null if a load operation is already in progress. + /// public virtual IEnumerator GetLoadWorldCorotine(WorldSceneConfig WorldScene) { if (IsLoading == true) @@ -73,8 +211,6 @@ public virtual IEnumerator GetLoadWorldCorotine(WorldSceneConfig WorldScene) return LoadWorldAsync(WorldScene); } - // Abstract Methods - protected abstract void OnNewAdditiveWorld(World target); // Corotine private IEnumerator LoadSceneAsync(string scene, int scneIndex = -1) @@ -149,7 +285,7 @@ private IEnumerator LoadWorldAsync(WorldSceneConfig WS) } while (!Load_opertation.isDone) { - OnSceneLoadingProcess?.Invoke(RM_Math.Map.RemapClamped(Load_opertation.progress, 0, 1, 0, 1 / WS.Count + 1)); + OnSceneLoadingProcess?.Invoke(Clamped(Load_opertation.progress, 0, 1, 0, 1 / WS.Count + 1)); OnSceneLoading?.Invoke(false); IsLoading = false; yield return null; @@ -168,7 +304,7 @@ private IEnumerator LoadWorldAsync(WorldSceneConfig WS) } while (!Additive_Load_opertation.isDone) { - OnSceneLoadingProcess?.Invoke(RM_Math.Map.RemapClamped(Load_opertation.progress, 0, 1, 0, 1 / WS.Count + 1 - (i + 1))); + OnSceneLoadingProcess?.Invoke(Clamped(Load_opertation.progress, 0, 1, 0, 1 / WS.Count + 1 - (i + 1))); yield return null; } } @@ -186,9 +322,30 @@ private IEnumerator LoadWorldAsync(WorldSceneConfig WS) } + // Private Functions + private float Clamped(float value, float inMin, float inMax, float outMin, float outMax) + { + // Prevent divide by zero + if (Mathf.Approximately(inMax, inMin)) + { + Debug.LogWarning("Input range is zero. Returning outMin."); + return outMin; + } + + // Normalize the input value to 0–1 within the input range + float t = (value - inMin) / (inMax - inMin); + + // Scale and offset to target range + float mappedValue = t * (outMax - outMin) + outMin; + + // Clamp result to the output range + return Mathf.Clamp(mappedValue, Mathf.Min(outMin, outMax), Mathf.Max(outMin, outMax)); + } + + + #if UNITY_EDITOR - // Runs when entering Play Mode in Editor - [InitializeOnEnterPlayMode] + [InitializeOnEnterPlayMode] // Runs when entering Play Mode in Editor private static void EditorPlayModeInit() { var assets = Resources.FindObjectsOfTypeAll(); @@ -202,5 +359,6 @@ private static void EditorPlayModeInit() } #endif + } } \ No newline at end of file diff --git a/Runtime/Core/Architecture/ManagerContracts.cs b/Runtime/Core/Architecture/ManagerContracts.cs new file mode 100644 index 0000000..fec74db --- /dev/null +++ b/Runtime/Core/Architecture/ManagerContracts.cs @@ -0,0 +1,38 @@ +using UnityEngine; + +namespace RealMethod +{ + /// + /// Defines the core behavior required for all game manager classes. + /// + public interface IGameManager + { + /// + /// Returns the MonoBehaviour instance that implements this manager. + /// + /// + /// The MonoBehaviour associated with this game manager. + /// + MonoBehaviour GetManagerClass(); + + /// + /// Initializes the manager and prepares it for use in the game lifecycle. + /// + /// + /// If true, the manager should persist and remain loaded at all times. + /// + void InitiateManager(bool AlwaysLoaded); + + /// + /// Enables or disables a service handled by this manager. + /// + /// + /// The service to be resolved or managed. + /// + /// + /// Indicates whether the service should be activated (true) or deactivated (false). + /// + void ResolveService(Service service, bool active); + } + +} \ No newline at end of file diff --git a/Runtime/Core/Architecture/ManagerContracts.cs.meta b/Runtime/Core/Architecture/ManagerContracts.cs.meta new file mode 100644 index 0000000..66608a9 --- /dev/null +++ b/Runtime/Core/Architecture/ManagerContracts.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d4832fe965ec2e242a7b04504a965182 \ No newline at end of file diff --git a/Runtime/Core/Architecture/ServiceContracts.cs b/Runtime/Core/Architecture/ServiceContracts.cs new file mode 100644 index 0000000..ed1a575 --- /dev/null +++ b/Runtime/Core/Architecture/ServiceContracts.cs @@ -0,0 +1,64 @@ + +namespace RealMethod +{ + /// + /// Defines the contract for a service that can respond to lifecycle events. + /// + public interface IService + { + /// + /// Called when the service is created. + /// + /// The object responsible for creating the service. + void Created(object author); + /// + /// Called when the world or environment updates. + /// + void WorldUpdated(); + /// + /// Called when the service is deleted or destroyed. + /// + /// The object responsible for deleting the service. + void Deleted(object author); + } + + /// + /// Base abstract class implementing . + /// Provides a framework for derived services to handle lifecycle events. + /// + public abstract class Service : IService + { + // Implement IService Interface + void IService.Created(object author) + { + OnStart(author); + } + void IService.WorldUpdated() + { + OnNewWorld(); + } + void IService.Deleted(object author) + { + OnEnd(author); + } + + + /// + /// Called when the service starts. Must be implemented by derived classes. + /// + /// The object responsible for creating the service. + protected abstract void OnStart(object Author); + /// + /// Called when a new world or environment is initialized. + /// Must be implemented by derived classes. + /// + protected abstract void OnNewWorld(); + /// + /// Called when the service ends or is deleted. + /// Must be implemented by derived classes. + /// + /// The object responsible for deleting the service. + protected abstract void OnEnd(object Author); + } + +} \ No newline at end of file diff --git a/Runtime/Core/Architecture/GameManager.cs.meta b/Runtime/Core/Architecture/ServiceContracts.cs.meta similarity index 100% rename from Runtime/Core/Architecture/GameManager.cs.meta rename to Runtime/Core/Architecture/ServiceContracts.cs.meta diff --git a/Runtime/Core/Architecture/World.cs b/Runtime/Core/Architecture/World.cs index a447056..fe81608 100644 --- a/Runtime/Core/Architecture/World.cs +++ b/Runtime/Core/Architecture/World.cs @@ -3,34 +3,100 @@ namespace RealMethod { + /// + /// Base class representing a locatioon for spawn player in scene + /// some virtual function in world calls can find all of object that has this class and with these spawing to location + /// + public abstract class PlayerStarter : MonoBehaviour + { + [Header("Setting")] + [SerializeField] + private string posName = "None"; + public string PosName => posName; + [SerializeField] + private float height = 2f; + [SerializeField] + private float radius = 0.5f; + + private bool hasPlayer = false; + + // Unity Method + private void Awake() + { + Destroy(gameObject); + } + + /// + /// Attempts to mark this spawn point as occupied by a player. + /// + /// + /// True if the spawn point was successfully marked as occupied; false if it is already occupied. + /// + public bool TryStartHere() + { + if (hasPlayer) + return false; + + hasPlayer = true; + return true; + } + /// + /// Check is player Spawn on this location. + /// + /// + /// True is already occupied. + /// + public bool CanStartHere() + { + return hasPlayer; + } + + +#if UNITY_EDITOR + protected virtual void OnDrawGizmos() + { + RM_Draw.gizmos.Capsule(transform.position, Color.cyan, height, radius); + RM_Draw.gizmos.Arrow(transform.position, transform.forward, Color.red); + RM_Draw.gizmos.Text(PosName, transform.position + (transform.up * (height / 2)) + (Vector3.up * 0.1f), Color.black); + } +#endif + } + + /// + /// Base class representing a game world / scene context. + /// Manages the player GameObject, locates and initializes child game managers, + /// and forwards service updates to managers. Concrete worlds should implement + /// and to perform + /// world-specific initialization and cleanup. + /// public abstract class World : MonoBehaviour { - [Header("Player")] + [Header("Setting")] [SerializeField] - private bool IsPlayerInScene = true; - [SerializeField, ConditionalHide("IsPlayerInScene", true, false), TagSelector] - private string PlayerTag = "Player"; - [SerializeField, ConditionalHide("IsPlayerInScene", true, true)] - private GameObject DefualtPlayer; - [SerializeField, ConditionalHide("IsPlayerInScene", true, true)] - private Transform SpawnPoint; - - // Private Variable + private Prefab DefualtPlayer; + private IGameManager[] Managers; private GameObject PlayerObject; - // Base Method + + /// + /// Unity callback invoked when the script instance is being loaded. + /// Connects the world to the game service, binds service callbacks, + /// initializes child managers and locates or creates the player object, + /// then calls for world-specific initialization. + /// private void Awake() { //Connect to Game Service - if (Game.Service.Worldprovider.IntroduceWorld(this)) + IMethodSync SyncProvider = Game.Service; + if (SyncProvider.IntroduceWorld(this)) { - Game.Service.OnAdditiveWorld += AdditiveWorld; - Game.Service.OnServiceCreated += NotifyServiceCreated; - Game.Service.OnServiceRemoved += NotifyServiceRemoved; + SyncProvider.BindSideWorldAdd(Notify_OnAdditiveWorldInitiate); + SyncProvider.BindServicesUpdated(Notify_OnServicesUpdated); } else { + enabled = false; return; } @@ -45,20 +111,40 @@ private void Awake() Managers = CashManagers.ToArray(); //Find Player or Create newone - if (!InitiatePlayer(ref PlayerObject)) + var scneplayer = GetPlayerInScene(); + if (scneplayer == null) { - return; + var starters = FindObjectsByType(FindObjectsSortMode.InstanceID); + Transform SpawnPoint = SelectSpawnPoint(starters); + PlayerObject = SpawnPlayer(DefualtPlayer, SpawnPoint); + } + else + { + PlayerObject = scneplayer; } - AwakeWorld(); + WorldBegin(); } + + + /// + /// Unity callback invoked when the object is being destroyed. + /// Unbinds previously bound service callbacks to avoid dangling references. + /// private void OnDestroy() { - Game.Service.OnAdditiveWorld -= AdditiveWorld; - Game.Service.OnServiceCreated -= NotifyServiceCreated; - Game.Service.OnServiceRemoved -= NotifyServiceRemoved; + IMethodSync SyncProvider = Game.Service; + SyncProvider.UnbindSideWorldAdd(); + SyncProvider.UnbindServicesUpdated(); + WorldEnd(); } - // Public Metthods + + + /// + /// Returns a manager instance of the requested type if one exists on this world. + /// + /// The manager type to search for. + /// The manager instance cast to , or null if not found. public T GetManager() where T : class { foreach (var manager in Managers) @@ -70,6 +156,11 @@ public T GetManager() where T : class } return null; } + /// + /// Returns a manager whose associated GameObject has the specified name. + /// + /// The name of the manager's GameObject to find. + /// The matching , or null if none match. public IGameManager GetManager(string ObjectName) { foreach (var manger in Managers) @@ -81,18 +172,44 @@ public IGameManager GetManager(string ObjectName) } return null; } + + /// + /// Returns the primary player for this world. + /// + /// Player index when supporting multiple players (currently unused). + /// The player GameObject reference, or null if none is present. public GameObject GetPlayerObject(byte index = 0) { return PlayerObject; } + /// + /// Gets a component of type from the player GameObject. + /// + /// Component type to retrieve (must derive from ). + /// Player index when supporting multiple players (currently unused). + /// The component instance if found; otherwise null. public T GetPlayerComponent(byte index = 0) where T : MonoBehaviour { return PlayerObject.GetComponent(); } + /// + /// Gets a component or class of type from the player GameObject. + /// This is a generic helper that uses Unity's GetComponent and returns the result + /// as a reference type. + /// + /// The class type to retrieve. + /// Player index when supporting multiple players (currently unused). + /// The requested class instance, or null if not found. public T GetPlayerClass(byte index = 0) where T : class { return PlayerObject.GetComponent(); } + /// + /// Retrieves all components of type attached to the player GameObject. + /// + /// Component type to retrieve. + /// Player index when supporting multiple players (currently unused). + /// Array of components if found; otherwise null and a warning is logged. public T[] GetPlayerComponents(byte index = 0) where T : MonoBehaviour { T[] components = PlayerObject.GetComponents(); @@ -103,6 +220,12 @@ public T[] GetPlayerComponents(byte index = 0) where T : MonoBehaviour Debug.LogWarning($"No components of type {typeof(T).Name} found on {PlayerObject.name} or its children."); return null; } + /// + /// Retrieves all components of type from the player GameObject and its children. + /// + /// Component type to retrieve. + /// Player index when supporting multiple players (currently unused). + /// Array of components if found; otherwise null and a warning is logged. public T[] GetPlayerComponentsInChilderen(byte index = 0) where T : MonoBehaviour { T[] components = PlayerObject.GetComponentsInChildren(); @@ -113,74 +236,150 @@ public T[] GetPlayerComponentsInChilderen(byte index = 0) where T : MonoBehav Debug.LogWarning($"No components of type {typeof(T).Name} found on {PlayerObject.name} or its children."); return null; } + /// + /// Retrieves the first component of type found on the player GameObject or its children. + /// + /// Component type to retrieve. + /// Player index when supporting multiple players (currently unused). + /// The component instance if found; otherwise null. public T GetPlayerComponentsInChildren(byte index = 0) where T : MonoBehaviour { return PlayerObject.GetComponentInChildren(); } - // Virtual Methods - protected virtual bool InitiatePlayer(ref GameObject player) + + + /// + /// Spawns the player character at the specified spawn point. + /// + /// + /// The player prefab to instantiate. If invalid, a fallback empty Player GameObject is created. + /// + /// + /// The transform representing the position and rotation where the player should spawn. + /// + /// + /// The spawned player GameObject. + /// + protected virtual GameObject SpawnPlayer(Prefab playerPrefab, Transform spawnPoint) { - if (IsPlayerInScene) + if (playerPrefab.IsValid()) { - player = GameObject.FindGameObjectWithTag(PlayerTag); - if (!player) - { - Debug.LogError("PlayerGameObject Cant Find in Scene"); - return false; - } - return true; + Transform player = Instantiate(playerPrefab.GetSoftClassTarget(), spawnPoint.position, spawnPoint.rotation); + player.SendMessage("OnSpawn", this, SendMessageOptions.DontRequireReceiver); + return player.gameObject; } else { - if (DefualtPlayer != null) + GameObject player = new GameObject("Player"); + player.tag = "Player"; + player.transform.SetPositionAndRotation(spawnPoint.position, spawnPoint.rotation); + return player; + } + } + /// + /// Searches the current scene for an existing player GameObject. + /// + /// + /// The first GameObject found with the "Player" tag, or null if none exists. + /// + protected virtual GameObject GetPlayerInScene() + { + return GameObject.FindGameObjectWithTag("Player"); + } + /// + /// Selects a spawn point from the available PlayerStarter components. + /// + /// + /// An array of PlayerStarter objects that define possible spawn locations. + /// + /// + /// The transform of the selected spawn point. If none are available, + /// the current object's transform at the world origin is used. + /// + protected virtual Transform SelectSpawnPoint(PlayerStarter[] starters) + { + if (starters.Length > 0) + { + int index = Random.Range(0, starters.Length); + if (!starters[index].CanStartHere()) { - if (SpawnPoint) + return starters[index].transform; + } + foreach (var start in starters) + { + if (!start.CanStartHere()) { - player = Instantiate(DefualtPlayer, SpawnPoint.position, SpawnPoint.rotation); + if (start.TryStartHere()) + { + return start.transform; + } + else + { + continue; + } } else { - player = Instantiate(DefualtPlayer, transform.position, Quaternion.identity); + continue; } - return true; - } - else - { - player = new GameObject("Player"); - player.tag = "Player"; - return true; } - } + + transform.position = Vector3.zero; + return transform; } - protected virtual void AdditiveWorld(World TargetWorld) + /// + /// Called when an additive world GameObject is added to this world. + /// Default behaviour is to destroy the provided object; override to implement custom handling. + /// + /// The additive world GameObject that was added. + protected virtual void OnAdditiveWorldAdded(GameObject WorldObject) { - Debug.LogWarning($"This World Class ({TargetWorld}) Deleted"); - Destroy(TargetWorld); + Destroy(WorldObject); } - // Private Methods - private void NotifyServiceCreated(Service NewService) + + + /// + /// Internal callback invoked by the service when an additive world is initiated. + /// Extracts the world GameObject, destroys the component instance, + /// and forwards the object to . + /// + /// The additive instance that was initiated. + private void Notify_OnAdditiveWorldInitiate(World TargetWorld) { - if (Managers != null) - { - foreach (var manager in Managers) - { - manager.ResolveService(NewService, true); - } - } + GameObject worldObject = TargetWorld.gameObject; + Destroy(TargetWorld); + OnAdditiveWorldAdded(worldObject); } - private void NotifyServiceRemoved(Service NewService) + /// + /// Internal callback invoked when services are updated. Forwards the service update + /// to all managers so they can resolve or react to the change. + /// + /// The service that changed. + /// The stage or phase of the service update. + private void Notify_OnServicesUpdated(Service NewService, bool stage) { if (Managers != null) { foreach (var manager in Managers) { - manager.ResolveService(NewService, false); + manager.ResolveService(NewService, stage); } } } - // Abstract Methods - protected abstract void AwakeWorld(); - protected abstract void DestroyWorld(); + + + /// + /// Called after the world is initialized in the context of this scene. + /// If this world is the main world, this runs normally. + /// If this world is loaded additively, it runs in additive context. + /// Use this to set up scene-specific systems, not global main-world logic. + /// + protected abstract void WorldBegin(); + /// + /// Called when the world is being destroyed to perform cleanup of world-specific state. + /// Implementations should release resources and unregister any world-specific hooks here. + /// + protected abstract void WorldEnd(); } } \ No newline at end of file diff --git a/Runtime/Core/Architecture/WorldSceneConfig.cs b/Runtime/Core/Architecture/WorldSceneConfig.cs index dda47d1..e8babb3 100644 --- a/Runtime/Core/Architecture/WorldSceneConfig.cs +++ b/Runtime/Core/Architecture/WorldSceneConfig.cs @@ -7,34 +7,58 @@ namespace RealMethod { + /// + /// ScriptableObject for configuring a world scene setup in Unity. + /// Allows specifying a persistent scene and multiple additive layers. + /// [CreateAssetMenu(fileName = "WorldScene", menuName = "Scene/WorldScene", order = 1)] public class WorldSceneConfig : ConfigAsset { [Header("Scenes")] + /// + /// The persistent scene that is always loaded and acts as the base scene. + /// [SerializeField] private SceneReference persistent; + /// + /// Public getter for the persistent scene. + /// public SceneReference Persistent => persistent; + /// + /// Array of additive layer scenes that can be loaded alongside the persistent scene. + /// [SerializeField] private SceneReference[] Layers; + /// + /// Gets the number of additive layer scenes. + /// public int Count => Layers.Length; + /// + /// Indexer to access additive layer scenes by index. + /// + /// Index of the layer scene. + /// The at the specified index. + public SceneReference this[int index] => Layers[index]; + + - public SceneReference this[int index] - { - get => Layers[index]; - } #if UNITY_EDITOR + /// + /// Opens the persistent scene and all additive layer scenes in the editor. + /// Only available in the Unity Editor. + /// public void OnAssetClick() { + // Open the persistent base scene in Single mode EditorSceneManager.OpenScene(Persistent, OpenSceneMode.Single); + + // Open all layer scenes additively foreach (var item in Layers) { EditorSceneManager.OpenScene(item, OpenSceneMode.Additive); } } #endif - - - } } \ No newline at end of file diff --git a/Runtime/Core/Definitions/Assets.cs b/Runtime/Core/Definitions/Assets.cs index fc40859..c28237c 100644 --- a/Runtime/Core/Definitions/Assets.cs +++ b/Runtime/Core/Definitions/Assets.cs @@ -44,6 +44,7 @@ public bool IsProjectAsset() #endif } + #if UNITY_EDITOR public virtual void OnEditorPlay() { @@ -57,7 +58,7 @@ public virtual void OnEditorPlay() public abstract class DataAsset : PrimitiveAsset { } - // TemplateAsset: is a PrimitiveAsset that you can't create new at runtime + // TemplateAsset: is a PrimitiveAsset that you can't create new at runtime & Should Use With Clone public abstract class TemplateAsset : PrimitiveAsset { protected virtual void OnEnable() @@ -72,6 +73,13 @@ protected virtual void OnEnable() } } } + +#if UNITY_EDITOR + public override void OnEditorPlay() + { + + } +#endif } // FileAsset: is a PrimitiveAsset that you can't clone at runtime public abstract class FileAsset : PrimitiveAsset @@ -104,6 +112,13 @@ protected virtual void OnEnable() return; } } + +#if UNITY_EDITOR + public override void OnEditorPlay() + { + + } +#endif } // ConfigAsset: is a UniqueAsset that you can't decelar modifier variable or method , all of things should be readonly public abstract class ConfigAsset : UniqueAsset diff --git a/Runtime/Core/Definitions/Prefab.cs b/Runtime/Core/Definitions/Prefab.cs index c031ef8..be83a65 100644 --- a/Runtime/Core/Definitions/Prefab.cs +++ b/Runtime/Core/Definitions/Prefab.cs @@ -34,18 +34,18 @@ public bool HasInterface() [System.Serializable] public class PrefabCore : PrefabCore where T : Component { - // Prefab Methods + // PrefabCore Methods public override bool IsValid() { return asset != null && asset.GetComponent() != null; } + public override System.Type GetTargetClass() => typeof(T); // <--- implemented + // Public Method public T GetSoftClassTarget() { return asset.GetComponent(); } - - public override System.Type GetTargetClass() => typeof(T); // <--- implemented } diff --git a/Runtime/Core/Architecture/SceneReference.cs b/Runtime/Core/Definitions/SceneReference.cs similarity index 100% rename from Runtime/Core/Architecture/SceneReference.cs rename to Runtime/Core/Definitions/SceneReference.cs diff --git a/Runtime/Core/Architecture/SceneReference.cs.meta b/Runtime/Core/Definitions/SceneReference.cs.meta similarity index 100% rename from Runtime/Core/Architecture/SceneReference.cs.meta rename to Runtime/Core/Definitions/SceneReference.cs.meta diff --git a/Runtime/Core/ProjectSetting/ProjectSettingAsset.cs b/Runtime/Core/ProjectSetting/ProjectSettingAsset.cs index cea5c51..3b65e43 100644 --- a/Runtime/Core/ProjectSetting/ProjectSettingAsset.cs +++ b/Runtime/Core/ProjectSetting/ProjectSettingAsset.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using UnityEngine; namespace RealMethod @@ -9,12 +10,18 @@ public class ProjectSettingAsset : ScriptableObject [Serializable] public struct FolderAddress { - public IdentityAsset Identity; - public string Path; - public string FolderName => System.IO.Path.GetFileName(Path); + public AssetFormat AssetType; + public string AssetPath; + public string FolderName => System.IO.Path.GetFileName(AssetPath); + public string GetFolderPath(ProjectSettingAsset settingAsset) + { + string RootPath = settingAsset.GetStructureType() == 0 ? "Assets" : "Assets/" + Application.productName; + return $"{RootPath}/{AssetPath}"; + } + } [Serializable] - public enum IdentityAsset + public enum AssetFormat { Scene = 0, Script = 1, @@ -29,21 +36,19 @@ public enum IdentityAsset Audio = 10, Particle = 11, Animationclip = 12, - Miscellaneous = 13, - User = 14, - Resources = 15, - ThirdpartyPack = 16, - PCG = 17, - ScriptTemplate = 18, - PrefabTemplate = 19, - AnimatorParam, + Other = 13 + } + [Serializable] + public enum FolderStructureType + { + Assets = 0, + ProjectName = 1, } - [Header("Initializer")] - [SerializeField] + [SerializeField, ReadOnly] private string GameClass = "RealMethod.DefultGame"; - [SerializeField] + [SerializeField, ReadOnly] private string GameService = "RealMethod.DefaultGameService"; [SerializeField] private GameConfig GameConfig; @@ -54,34 +59,37 @@ public enum IdentityAsset [SerializeField] private GameObject GamePrefab_3; [Header("FolderStructure")] - public FolderAddress[] ProjectStructure = new FolderAddress[21] + [SerializeField, ReadOnly] + private FolderStructureType structureType; + [SerializeField, ReadOnly] + private FolderAddress[] projectStructure = new FolderAddress[14] { - new FolderAddress { Identity = 0, Path = "Assets/1_Scenes"}, - new FolderAddress { Identity = (IdentityAsset)1, Path = "Assets/2_Scripts" }, - new FolderAddress { Identity = (IdentityAsset)2, Path = "Assets/3_Prefabs"}, - new FolderAddress { Identity = (IdentityAsset)3, Path = "Assets/4_Data" }, - new FolderAddress { Identity = (IdentityAsset)4, Path = "Assets/5_Mesh"}, - new FolderAddress { Identity = (IdentityAsset)5, Path = "Assets/5_Sprite"}, - new FolderAddress { Identity = (IdentityAsset)6, Path = "Assets/7_Misc/Textures"}, - new FolderAddress { Identity = (IdentityAsset)7, Path = "Assets/7_Misc/Videos"}, - new FolderAddress { Identity = (IdentityAsset)8, Path = "Assets/7_Misc/Materials"}, - new FolderAddress { Identity = (IdentityAsset)9, Path = "Assets/6_Shader"}, - new FolderAddress { Identity = (IdentityAsset)10, Path = "Assets/8_Sound&Music"}, - new FolderAddress { Identity = (IdentityAsset)11, Path = "Assets/9_VFX"}, - new FolderAddress { Identity = (IdentityAsset)12, Path = "Assets/10_Animation"}, - new FolderAddress { Identity = (IdentityAsset)13, Path = "Assets/7_Misc"}, - new FolderAddress { Identity = (IdentityAsset)14, Path = "Assets/Developer"}, - new FolderAddress { Identity = (IdentityAsset)15, Path = "Assets/Resources"}, - new FolderAddress { Identity = (IdentityAsset)16, Path = "Assets/~Thirdparty"}, - new FolderAddress { Identity = (IdentityAsset)17, Path = "Assets/4_Data/PCG"}, - new FolderAddress { Identity = (IdentityAsset)18, Path = "Assets/7_Misc/Templates/Scripts"}, - new FolderAddress { Identity = (IdentityAsset)19, Path = "Assets/7_Misc/Templates/Prefabs"}, - new FolderAddress { Identity = (IdentityAsset)20, Path = "Assets/2_Scripts/_Game/Utility"} + new FolderAddress { AssetType = 0, AssetPath = "1_Scenes"}, + new FolderAddress { AssetType = (AssetFormat)1, AssetPath = "2_Scripts" }, + new FolderAddress { AssetType = (AssetFormat)2, AssetPath = "3_Prefabs"}, + new FolderAddress { AssetType = (AssetFormat)3, AssetPath = "4_Data" }, + new FolderAddress { AssetType = (AssetFormat)4, AssetPath = "5_Mesh"}, + new FolderAddress { AssetType = (AssetFormat)5, AssetPath = "5_Sprite"}, + new FolderAddress { AssetType = (AssetFormat)6, AssetPath = "7_Misc/Textures"}, + new FolderAddress { AssetType = (AssetFormat)7, AssetPath = "7_Misc/Videos"}, + new FolderAddress { AssetType = (AssetFormat)8, AssetPath = "7_Misc/Materials"}, + new FolderAddress { AssetType = (AssetFormat)9, AssetPath = "6_Shader"}, + new FolderAddress { AssetType = (AssetFormat)10, AssetPath = "8_Sound&Music"}, + new FolderAddress { AssetType = (AssetFormat)11, AssetPath = "9_VFX"}, + new FolderAddress { AssetType = (AssetFormat)12, AssetPath = "10_Animation"}, + new FolderAddress { AssetType = (AssetFormat)13, AssetPath = "7_Misc"} }; + public IReadOnlyList ProjectStructure => projectStructure; + // Access values + public string this[AssetFormat type] + { + get => GetFolderAddressByType(type).GetFolderPath(this); + } + // Public Functions - public Type GetGameInstanceClass() + public Type GetGameInstanceType() { foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { @@ -91,7 +99,7 @@ public Type GetGameInstanceClass() } return null; } - public void SetGameInstanceClass(Type type) + public void SetGameInstanceType(Type type) { // Store fully qualified name of the type if (type != null) @@ -102,9 +110,8 @@ public void SetGameInstanceClass(Type type) { Debug.LogError("Type is null. Cannot set GameInstanceClass."); } - } - public Type GetGameServiceClass() + public Type GetGameServiceType() { foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { @@ -114,7 +121,7 @@ public Type GetGameServiceClass() } return null; } - public void SetGameServiceClass(Type type) + public void SetGameServiceType(Type type) { // Store fully qualified name of the type if (type != null) @@ -139,11 +146,21 @@ public GameObject[] GetGamePrefabs() GamePrefab_3, }; } - public FolderAddress FindAddres(IdentityAsset identity) + public int GetStructureType() + { + return (int)structureType; + } + public void SetStructureType(int type) + { + structureType = (FolderStructureType)type; + } + public FolderAddress GetFolderAddressByIndex(int index) => projectStructure[index]; + public void SetFolderAddressPath(int index, string value) => projectStructure[index].AssetPath = value; + public FolderAddress GetFolderAddressByType(AssetFormat identity) { - foreach (var PS in ProjectStructure) + foreach (var PS in projectStructure) { - if (PS.Identity == identity) + if (PS.AssetType == identity) { return PS; } diff --git a/Runtime/Library/SharedScripts/Structs/TransformData.cs b/Runtime/Library/SharedScripts/Structs/TransformData.cs index f541146..8fdc6a9 100644 --- a/Runtime/Library/SharedScripts/Structs/TransformData.cs +++ b/Runtime/Library/SharedScripts/Structs/TransformData.cs @@ -14,6 +14,12 @@ public TransformData(Vector3 pos, Quaternion rot, Vector3 scl) rotation = rot; scale = scl; } + public TransformData(Transform comp) + { + position = comp.position; + rotation = comp.rotation; + scale = comp.localScale; + } public static TransformData FromTransform(Transform t) { @@ -241,7 +247,7 @@ public static TransformData Scale(TransformData a, TransformData b, float t, Qua Vector3.Scale(a.scale, new Vector3(q.x, q.y, q.z)) ); } - + } } diff --git a/Runtime/Library/Utilities/Draw.cs b/Runtime/Library/Utilities/Draw.cs index e717172..eb47d0d 100644 --- a/Runtime/Library/Utilities/Draw.cs +++ b/Runtime/Library/Utilities/Draw.cs @@ -1,3 +1,4 @@ +using UnityEditor; using UnityEngine; namespace RealMethod @@ -6,7 +7,7 @@ public static class RM_Draw { public class gizmos { - public static void DrawCurve(AnimationCurve curve) + public static void Curve(AnimationCurve curve) { for (float t = 0; t < curve.keys[curve.length - 1].time; t += 0.1f) { @@ -14,7 +15,7 @@ public static void DrawCurve(AnimationCurve curve) } } // Draws a directional arrow using Gizmos (no color) - public static void DrawArrow(Vector3 position, Vector3 direction, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) + public static void Arrow(Vector3 position, Vector3 direction, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Gizmos.DrawRay(position, direction); @@ -24,7 +25,7 @@ public static void DrawArrow(Vector3 position, Vector3 direction, float arrowHea Gizmos.DrawRay(position + direction, left * arrowHeadLength); } // Draws a directional arrow using Gizmos (with color) - public static void DrawArrow(Vector3 position, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) + public static void Arrow(Vector3 position, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Gizmos.color = color; Gizmos.DrawRay(position, direction); @@ -34,12 +35,65 @@ public static void DrawArrow(Vector3 position, Vector3 direction, Color color, f Gizmos.DrawRay(position + direction, right * arrowHeadLength); Gizmos.DrawRay(position + direction, left * arrowHeadLength); } + // Draws a Capsule using Gizmos (no color) + public static void Capsule(Vector3 position, float height = 2f, float radius = 0.5f) + { + // Draw top sphere + Vector3 top = position + Vector3.up * (height / 2 - radius); + Gizmos.DrawWireSphere(top, radius); + + // Draw bottom sphere + Vector3 bottom = position + Vector3.down * (height / 2 - radius); + Gizmos.DrawWireSphere(bottom, radius); + + // Draw cylinder (approximation) + Gizmos.DrawLine(top + Vector3.forward * radius, bottom + Vector3.forward * radius); + Gizmos.DrawLine(top - Vector3.forward * radius, bottom - Vector3.forward * radius); + Gizmos.DrawLine(top + Vector3.right * radius, bottom + Vector3.right * radius); + Gizmos.DrawLine(top - Vector3.right * radius, bottom - Vector3.right * radius); + } + // Draws a Capsule using Gizmos (with color) + public static void Capsule(Vector3 position, Color color, float height = 2f, float radius = 0.5f) + { + // Save previous Gizmos color + Color prevColor = Gizmos.color; + Gizmos.color = color; + + // Draw top sphere + Vector3 top = position + Vector3.up * (height / 2 - radius); + Gizmos.DrawWireSphere(top, radius); + + // Draw bottom sphere + Vector3 bottom = position + Vector3.down * (height / 2 - radius); + Gizmos.DrawWireSphere(bottom, radius); + + // Draw cylinder (approximation) + Gizmos.DrawLine(top + Vector3.forward * radius, bottom + Vector3.forward * radius); + Gizmos.DrawLine(top - Vector3.forward * radius, bottom - Vector3.forward * radius); + Gizmos.DrawLine(top + Vector3.right * radius, bottom + Vector3.right * radius); + Gizmos.DrawLine(top - Vector3.right * radius, bottom - Vector3.right * radius); + + // Restore previous color + Gizmos.color = prevColor; + } + // Draw a Text using Gizmos (With Colort) + public static void Text(string text, Vector3 position, Color color, FontStyle font = FontStyle.Bold, TextAnchor ancher = TextAnchor.MiddleCenter) + { + // Set color + GUIStyle style = new GUIStyle(); + style.normal.textColor = color; + style.alignment = ancher; + style.fontStyle = font; + + // Draw the label above the GameObject + Handles.Label(position, text, style); + } } public class debug { // Draws a directional arrow using Debug.DrawRay (no color) - public static void DrawArrow(Vector3 position, Vector3 direction, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) + public static void Arrow(Vector3 position, Vector3 direction, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Debug.DrawRay(position, direction); @@ -49,7 +103,7 @@ public static void DrawArrow(Vector3 position, Vector3 direction, float arrowHea Debug.DrawRay(position + direction, left * arrowHeadLength); } // Draws a directional arrow using Debug.DrawRay (with color) - public static void DrawArrow(Vector3 position, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) + public static void Arrow(Vector3 position, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Debug.DrawRay(position, direction, color); diff --git a/Runtime/ReadySet/Components/Method/PlayerStarterComponent.cs b/Runtime/ReadySet/Components/Method/PlayerStarterComponent.cs new file mode 100644 index 0000000..afddb9e --- /dev/null +++ b/Runtime/ReadySet/Components/Method/PlayerStarterComponent.cs @@ -0,0 +1,10 @@ +using UnityEngine; + +namespace RealMethod +{ + [AddComponentMenu("RealMethod/Method/PlayerStarter")] + public sealed class PlayerStarterComponent : PlayerStarter + { + + } +} \ No newline at end of file diff --git a/Runtime/ReadySet/Components/Method/PlayerStarterComponent.cs.meta b/Runtime/ReadySet/Components/Method/PlayerStarterComponent.cs.meta new file mode 100644 index 0000000..95d243c --- /dev/null +++ b/Runtime/ReadySet/Components/Method/PlayerStarterComponent.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: bd7492c4fc6b670479f8f302f507dd22 \ No newline at end of file diff --git a/Runtime/ReadySet/DefaultsClass/DefaultGame.cs b/Runtime/ReadySet/DefaultsClass/DefaultGame.cs index 764b121..fa1f575 100644 --- a/Runtime/ReadySet/DefaultsClass/DefaultGame.cs +++ b/Runtime/ReadySet/DefaultsClass/DefaultGame.cs @@ -6,19 +6,19 @@ namespace RealMethod [AddComponentMenu("RealMethod/Essential/DefultGame")] public sealed class DefultGame : Game { - protected override void GameInitialized() + protected override void OnGameInitialized() { Debug.Log("DefultGame Initialized"); } - protected override void GameStarted() + protected override void OnGameStarted() { Debug.Log("DefultGame Started"); } - protected override void GameWorldSynced(World NewWorld) + protected override void OnWorldChanged(World NewWorld) { - Debug.Log($"DefultGame Synced to {NewWorld}"); + Debug.Log($"DefultGame.World Change to {NewWorld}"); } - protected override void GameClosed() + protected override void OnGameClosed() { Debug.Log("DefultGame Closed"); } diff --git a/Runtime/ReadySet/DefaultsClass/DefaultGameService.cs b/Runtime/ReadySet/DefaultsClass/DefaultGameService.cs index 79b3c9f..fb93638 100644 --- a/Runtime/ReadySet/DefaultsClass/DefaultGameService.cs +++ b/Runtime/ReadySet/DefaultsClass/DefaultGameService.cs @@ -6,18 +6,14 @@ public sealed class DefaultGameService : GameService { protected override void OnStart(object Author) { - Debug.Log("DefaultService Started"); + Debug.Log("DefaultGameService Started"); } protected override void OnNewWorld() { - } - protected override void OnNewAdditiveWorld(World target) - { - } protected override void OnEnd(object Author) { - Debug.Log("DefaultService Ended"); + Debug.Log("DefaultGameService Ended"); } } } \ No newline at end of file diff --git a/Runtime/ReadySet/DefaultsClass/DefaultWorld.cs b/Runtime/ReadySet/DefaultsClass/DefaultWorld.cs index 1712602..68cac71 100644 --- a/Runtime/ReadySet/DefaultsClass/DefaultWorld.cs +++ b/Runtime/ReadySet/DefaultsClass/DefaultWorld.cs @@ -5,13 +5,13 @@ namespace RealMethod [AddComponentMenu("RealMethod/Essential/DefaultWorld")] public sealed class DefaultWorld : World { - protected override void AwakeWorld() + protected override void WorldBegin() { - Debug.Log("DefaultWorld Awaked"); + Debug.Log("DefaultWorld Begin"); } - protected override void DestroyWorld() + protected override void WorldEnd() { - Debug.Log("DefaultWorld Destroyed"); + Debug.Log("DefaultWorld End"); } } } \ No newline at end of file