From 22fe6f8037a840582bf946cd687392235e514a67 Mon Sep 17 00:00:00 2001 From: Alijimpa Date: Mon, 22 Dec 2025 14:07:39 +0330 Subject: [PATCH 01/15] Cleanup Naming for Scyncing between Game & World --- Runtime/Core/Architecture/Game.cs | 200 +++++++++--------- Runtime/Core/Architecture/GameManager.cs | 2 +- Runtime/Core/Architecture/GameService.cs | 74 ++++++- Runtime/Core/Architecture/World.cs | 36 ++-- Runtime/ReadySet/DefaultsClass/DefaultGame.cs | 4 +- 5 files changed, 184 insertions(+), 132 deletions(-) diff --git a/Runtime/Core/Architecture/Game.cs b/Runtime/Core/Architecture/Game.cs index 81997b3..5487ab0 100644 --- a/Runtime/Core/Architecture/Game.cs +++ b/Runtime/Core/Architecture/Game.cs @@ -4,6 +4,10 @@ using UnityEngine.SceneManagement; using System.Linq; +#if UNITY_EDITOR +using UnityEditor; +#endif + namespace RealMethod { public abstract class Game : MonoBehaviour @@ -67,28 +71,29 @@ protected T NeedManager() where T : MonoBehaviour return Result; } // Private Functions - private void OnGameWorldReplaced(World NewWorld) + private void Notify_OnWorldInitiate(World NewWorld) { World = NewWorld; foreach (var service in GameServices) { - service.provider.WorldUpdated(); + service.Provider.WorldUpdated(); } - GameWorldSynced(World); + OnWorldChanged(World); } - private void OnGameQuit() + private void Notify_OnGameQuit() { - Application.quitting -= OnGameQuit; + ((IMethodSync)Service).UnbindMainWorldAdd(); + Application.quitting -= Notify_OnGameQuit; if (GameServices != null) { for (int i = 0; i < GameServices.Count; i++) { - GameServices[i].provider.Deleted(this); + GameServices[i].Provider.Deleted(this); GameServices.RemoveAt(i); } } - Service.provider.Deleted(this); - GameClosed(); + Service.Provider.Deleted(this); + OnGameClosed(); } // Public Static Functions public static T CastInstance() where T : class @@ -126,7 +131,7 @@ public static T CastWorld() where T : class // Create Service T newService = new T(); - newService.provider.Created(author); + newService.Provider.Created(author); if (Instance.Managers != null) { foreach (var manager in Instance.Managers) @@ -134,7 +139,7 @@ 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; } @@ -150,8 +155,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); + service.Provider.Deleted(author); Instance.GameServices.Remove(service); return true; } @@ -268,109 +273,108 @@ public static void SetSpeed(float speed, bool physicSafe = true) if (physicSafe) Time.fixedDeltaTime = 0.02f * Time.timeScale; // Keeps physics in sync } + public static void Quit() + { +#if UNITY_EDITOR + EditorApplication.isPlaying = false; +#else + Application.Quit(); +#endif + } // Private Static Functions [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void InitializeGame() { - Game[] components = FindObjectsByType(FindObjectsSortMode.InstanceID); - if (components.Length > 1) + // Load Project Setting + ProjectSettingAsset ProjectSettings = Resources.Load("RealMethod/RealMethodSetting"); + if (ProjectSettings == null) { - Debug.LogError($"Expected exactly one active 'Game' component in the scene, but found {components.Length}."); + Debug.LogError("ProjectSettingAsset is missing from Resources folder!"); + Quit(); return; } - // Load Project Setting - ProjectSettingAsset ProjectSettings = Resources.Load("RealMethod/RealMethodSetting"); - if (ProjectSettings != null) + + // Initiate Game Class + var emptyObject = new GameObject("RealGame"); + Type TargetClass = ProjectSettings.GetGameInstanceClass(); + if (TargetClass == null) { - // 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 + 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(); + } + + // Create Game Service + Type targetService = ProjectSettings.GetGameServiceClass(); + if (targetService == null) + { + Debug.LogWarning($"GetGameServiceClass that was empty. DefaultGameService Created"); + Service = new DefaultGameService(); + } + if (typeof(Service).IsAssignableFrom(targetService)) + { + try { - Instance = components[0]; + Service = (GameService)Activator.CreateInstance(targetService); } - // 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 + catch (Exception ex) { + Debug.LogError($"Failed to instantiate {targetService}: {ex.Message}. DefaultGameService Created"); 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()) + } + else + { + 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); + 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 (obj != null) { - if (obj != null) + GameObject newobj = Instantiate(obj); + foreach (var manager in newobj.GetComponents()) { - GameObject newobj = Instantiate(obj); - foreach (var manager in newobj.GetComponents()) - { - manager.InitiateManager(true); - CashManagers.Add(manager); - } - DontDestroyOnLoad(newobj); + manager.InitiateManager(true); + CashManagers.Add(manager); } + DontDestroyOnLoad(newobj); } - Instance.Managers = CashManagers.ToArray(); - // Unload Project Setting - Resources.UnloadAsset(ProjectSettings); - // Move Self GameObject to DontDestroy - DontDestroyOnLoad(Instance.gameObject); - Application.quitting += Instance.OnGameQuit; - } - else - { - Debug.LogError("ProjectSettingAsset is missing from Resources folder!"); } + Instance.Managers = CashManagers.ToArray(); + + // Unload Project Setting + Resources.UnloadAsset(ProjectSettings); + + // Move Self GameObject to DontDestroy + DontDestroyOnLoad(Instance.gameObject); + Application.quitting += Instance.Notify_OnGameQuit; } [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void RuntimeBeforeSceneLoad() @@ -392,8 +396,8 @@ private static void RuntimeAfterSceneLoad() // Abstract Methods protected abstract void GameInitialized(); protected abstract void GameStarted(); - protected abstract void GameWorldSynced(World NewWorld); - protected abstract void GameClosed(); + protected abstract void OnWorldChanged(World NewWorld); + protected abstract void OnGameClosed(); } } \ No newline at end of file diff --git a/Runtime/Core/Architecture/GameManager.cs b/Runtime/Core/Architecture/GameManager.cs index bf5c6e7..3b6290b 100644 --- a/Runtime/Core/Architecture/GameManager.cs +++ b/Runtime/Core/Architecture/GameManager.cs @@ -18,7 +18,7 @@ public interface IService public abstract class Service : IService { - public IService provider => this; + public IService Provider => this; // Implement IService Interface void IService.Created(object author) diff --git a/Runtime/Core/Architecture/GameService.cs b/Runtime/Core/Architecture/GameService.cs index 69717e5..b3afc46 100644 --- a/Runtime/Core/Architecture/GameService.cs +++ b/Runtime/Core/Architecture/GameService.cs @@ -8,19 +8,25 @@ namespace RealMethod { - public interface IWorldSync + public interface IMethodSync { bool IntroduceWorld(World world); + void BindMainWorldAdd(Action func); + void UnbindMainWorldAdd(); + void BindSideWorldAdd(Action func); + void UnbindSideWorldAdd(); + void ServiceCreated(Service service); + void ServiceRemoved(Service service); + void BindServicesUpdated(Action func); + void UnbindServicesUpdated(); } - public abstract class GameService : Service, IWorldSync + public abstract class GameService : Service, IMethodSync { - public IWorldSync Worldprovider => this; // Game Structure - public Action OnWorldUpdate; - public Action OnAdditiveWorld; - public Action OnServiceCreated; - public Action OnServiceRemoved; + private Action MainWorldEvent; + private Action SideWorldEvent; + private Action ServiceEvents; // Load Scene public Action OnSceneLoading; public Action OnSceneLoadingProcess; @@ -29,20 +35,66 @@ public abstract class GameService : Service, IWorldSync // Implement IWorldSync Interface // Any World in Awake time call this method - bool IWorldSync.IntroduceWorld(World world) + 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 public virtual IEnumerator GetLoadScneCorotine(string sceneName) diff --git a/Runtime/Core/Architecture/World.cs b/Runtime/Core/Architecture/World.cs index a447056..972f5f3 100644 --- a/Runtime/Core/Architecture/World.cs +++ b/Runtime/Core/Architecture/World.cs @@ -23,14 +23,15 @@ public abstract class World : MonoBehaviour 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; } @@ -54,9 +55,9 @@ private void Awake() } private void OnDestroy() { - Game.Service.OnAdditiveWorld -= AdditiveWorld; - Game.Service.OnServiceCreated -= NotifyServiceCreated; - Game.Service.OnServiceRemoved -= NotifyServiceRemoved; + IMethodSync SyncProvider = Game.Service; + SyncProvider.UnbindSideWorldAdd(); + SyncProvider.UnbindServicesUpdated(); } // Public Metthods public T GetManager() where T : class @@ -153,29 +154,24 @@ protected virtual bool InitiatePlayer(ref GameObject player) } } - protected virtual void AdditiveWorld(World TargetWorld) + protected virtual void OnAdditiveWorldAdded(GameObject WorldObject) { - Debug.LogWarning($"This World Class ({TargetWorld}) Deleted"); - Destroy(TargetWorld); + Destroy(WorldObject); } // Private Methods - private void NotifyServiceCreated(Service NewService) + 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) + private void Notify_OnServicesUpdated(Service NewService, bool stage) { if (Managers != null) { foreach (var manager in Managers) { - manager.ResolveService(NewService, false); + manager.ResolveService(NewService, stage); } } } diff --git a/Runtime/ReadySet/DefaultsClass/DefaultGame.cs b/Runtime/ReadySet/DefaultsClass/DefaultGame.cs index 764b121..57c2deb 100644 --- a/Runtime/ReadySet/DefaultsClass/DefaultGame.cs +++ b/Runtime/ReadySet/DefaultsClass/DefaultGame.cs @@ -14,11 +14,11 @@ protected override void GameStarted() { Debug.Log("DefultGame Started"); } - protected override void GameWorldSynced(World NewWorld) + protected override void OnWorldChanged(World NewWorld) { Debug.Log($"DefultGame Synced to {NewWorld}"); } - protected override void GameClosed() + protected override void OnGameClosed() { Debug.Log("DefultGame Closed"); } From cd2b47ea5722ac41e8cd66acebf2be18c214d868 Mon Sep 17 00:00:00 2001 From: Alijimpa Date: Mon, 22 Dec 2025 18:56:47 +0330 Subject: [PATCH 02/15] Refactor Core Section & Add Comment --- Editor/Core/Architecture/GameCompWindow.cs | 17 - Runtime/Core/Architecture/Game.cs | 450 ++++++++++++------ Runtime/Core/Architecture/GameConfig.cs | 14 + Runtime/Core/Architecture/GameManager.cs | 43 -- Runtime/Core/Architecture/GameService.cs | 108 ++++- Runtime/Core/Architecture/ManagerContracts.cs | 38 ++ .../Architecture/ManagerContracts.cs.meta | 2 + Runtime/Core/Architecture/ServiceContracts.cs | 64 +++ ...nager.cs.meta => ServiceContracts.cs.meta} | 0 Runtime/Core/Architecture/World.cs | 224 +++++++-- Runtime/Core/Architecture/WorldSceneConfig.cs | 38 +- Runtime/Core/Definitions/Assets.cs | 17 +- .../SceneReference.cs | 0 .../SceneReference.cs.meta | 0 .../SharedScripts/Structs/TransformData.cs | 8 +- .../DefaultsClass/DefaultGameService.cs | 4 - .../ReadySet/DefaultsClass/DefaultWorld.cs | 8 +- 17 files changed, 760 insertions(+), 275 deletions(-) delete mode 100644 Runtime/Core/Architecture/GameManager.cs create mode 100644 Runtime/Core/Architecture/ManagerContracts.cs create mode 100644 Runtime/Core/Architecture/ManagerContracts.cs.meta create mode 100644 Runtime/Core/Architecture/ServiceContracts.cs rename Runtime/Core/Architecture/{GameManager.cs.meta => ServiceContracts.cs.meta} (100%) rename Runtime/Core/{Architecture => Definitions}/SceneReference.cs (100%) rename Runtime/Core/{Architecture => Definitions}/SceneReference.cs.meta (100%) diff --git a/Editor/Core/Architecture/GameCompWindow.cs b/Editor/Core/Architecture/GameCompWindow.cs index 589ee28..13d6815 100644 --- a/Editor/Core/Architecture/GameCompWindow.cs +++ b/Editor/Core/Architecture/GameCompWindow.cs @@ -1,4 +1,3 @@ -using PlasticGui.WorkspaceWindow; using UnityEditor; namespace RealMethod @@ -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/Runtime/Core/Architecture/Game.cs b/Runtime/Core/Architecture/Game.cs index 5487ab0..9a710ef 100644 --- a/Runtime/Core/Architecture/Game.cs +++ b/Runtime/Core/Architecture/Game.cs @@ -10,13 +10,33 @@ 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 @@ -29,73 +49,154 @@ 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.GetGameInstanceClass(); + 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(); + } + + // Create Game Service + Type targetService = ProjectSettings.GetGameServiceClass(); + 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 Notify_OnWorldInitiate(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.GameInitialized(); } - OnWorldChanged(World); } - private void Notify_OnGameQuit() + /// + /// Called after a scene has finished loading. Invokes on the active instance. + /// + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] + private static void RuntimeAfterSceneLoad() { - ((IMethodSync)Service).UnbindMainWorldAdd(); - Application.quitting -= Notify_OnGameQuit; - if (GameServices != null) + if (Instance != null) { - for (int i = 0; i < GameServices.Count; i++) - { - GameServices[i].Provider.Deleted(this); - GameServices.RemoveAt(i); - } + Instance.GameStarted(); } - Service.Provider.Deleted(this); - OnGameClosed(); } - // 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) @@ -108,6 +209,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) @@ -120,6 +227,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 @@ -131,7 +245,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) @@ -143,6 +257,13 @@ public static T CastWorld() where T : class 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)); @@ -156,7 +277,7 @@ public static bool RemoveService(object author) where T : Service } } ((IMethodSync)Service).ServiceRemoved(service); - service.Provider.Deleted(author); + ((IService)service).Deleted(author); Instance.GameServices.Remove(service); return true; } @@ -164,10 +285,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)) @@ -178,15 +310,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) @@ -199,10 +347,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) @@ -215,6 +374,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) @@ -227,10 +392,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; @@ -246,10 +421,20 @@ 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); } + /// + /// 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 UnHoldGameObject(string GameObjectName, GameObject Target) { Transform[] Childs = Instance.GetComponentsInChildren(); @@ -263,16 +448,28 @@ public static bool UnHoldGameObject(string GameObjectName, GameObject Target) } 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 @@ -282,121 +479,94 @@ public static void Quit() #endif } - // Private Static Functions - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void InitializeGame() - { - // Load Project Setting - ProjectSettingAsset ProjectSettings = Resources.Load("RealMethod/RealMethodSetting"); - if (ProjectSettings == null) - { - Debug.LogError("ProjectSettingAsset is missing from Resources folder!"); - Quit(); - return; - } - // Initiate Game Class - var emptyObject = new GameObject("RealGame"); - Type TargetClass = ProjectSettings.GetGameInstanceClass(); - if (TargetClass == null) - { - 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(); - } - - // Create Game Service - Type targetService = ProjectSettings.GetGameServiceClass(); - if (targetService == null) - { - Debug.LogWarning($"GetGameServiceClass that was empty. DefaultGameService Created"); - Service = new DefaultGameService(); - } - if (typeof(Service).IsAssignableFrom(targetService)) + /// + /// 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 + { + foreach (var manager in Managers) { - try - { - Service = (GameService)Activator.CreateInstance(targetService); - } - catch (Exception ex) + if (manager.GetManagerClass() is T Result) { - Debug.LogError($"Failed to instantiate {targetService}: {ex.Message}. DefaultGameService Created"); - Service = new DefaultGameService(); + return Result; } } - else - { - 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); - 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()) + 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) { - if (obj != null) + if (manger.GetManagerClass().gameObject.name == ObjectName) { - GameObject newobj = Instantiate(obj); - foreach (var manager in newobj.GetComponents()) - { - manager.InitiateManager(true); - CashManagers.Add(manager); - } - DontDestroyOnLoad(newobj); + return manger; } } - Instance.Managers = CashManagers.ToArray(); + return null; + } - // Unload Project Setting - Resources.UnloadAsset(ProjectSettings); - // Move Self GameObject to DontDestroy - DontDestroyOnLoad(Instance.gameObject); - Application.quitting += Instance.Notify_OnGameQuit; - } - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] - private static void RuntimeBeforeSceneLoad() + /// + /// 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) { - if (Instance != null) + 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) + ((IMethodSync)Service).UnbindMainWorldAdd(); + Application.quitting -= Notify_OnGameQuit; + 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 + + /// + /// Called once when the game framework has finished initial initialization. + /// Implement this to perform game-specific initialization logic. + /// protected abstract void GameInitialized(); + /// + /// 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 GameStarted(); + /// + /// 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(); } 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 3b6290b..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 b3afc46..d7974e6 100644 --- a/Runtime/Core/Architecture/GameService.cs +++ b/Runtime/Core/Architecture/GameService.cs @@ -8,33 +8,94 @@ namespace RealMethod { + /// + /// 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(); } + + /// + /// 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 { + /// + /// 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; - // Load Scene - public Action OnSceneLoading; - public Action OnSceneLoadingProcess; - public bool IsLoading { get; protected set; } - // Implement IWorldSync Interface - // Any World in Awake time call this method + // Implement IMethodSync Interface bool IMethodSync.IntroduceWorld(World world) { if (Game.World == null) @@ -96,7 +157,15 @@ 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) @@ -106,6 +175,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) @@ -115,6 +192,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) @@ -125,8 +210,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) @@ -238,9 +321,11 @@ private IEnumerator LoadWorldAsync(WorldSceneConfig WS) } + + + #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(); @@ -254,5 +339,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 972f5f3..6c6ed51 100644 --- a/Runtime/Core/Architecture/World.cs +++ b/Runtime/Core/Architecture/World.cs @@ -3,23 +3,39 @@ namespace RealMethod { + public abstract class PlayerStarter : MonoBehaviour + { + [Header("Starter")] + [SerializeField] + private string posName = "None"; + public string PosName => posName; + } + + /// + /// 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")] [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 @@ -46,20 +62,38 @@ 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() { 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) @@ -71,6 +105,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) @@ -82,18 +121,43 @@ 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(); @@ -104,6 +168,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(); @@ -114,57 +184,106 @@ 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) - { - if (SpawnPoint) - { - player = Instantiate(DefualtPlayer, SpawnPoint.position, SpawnPoint.rotation); - } - else - { - player = Instantiate(DefualtPlayer, transform.position, Quaternion.identity); - } - return true; - } - else - { - player = new GameObject("Player"); - player.tag = "Player"; - return true; - } - + GameObject player = new GameObject("Player"); + player.tag = "Player"; + 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) + { + return starters[0].transform; + } + else + { + transform.position = Vector3.zero; + return transform; } } + /// + /// 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) { Destroy(WorldObject); } - // Private Methods + + + /// + /// 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) { GameObject worldObject = TargetWorld.gameObject; Destroy(TargetWorld); OnAdditiveWorldAdded(worldObject); } + /// + /// 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) @@ -175,8 +294,19 @@ private void Notify_OnServicesUpdated(Service NewService, bool 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/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/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/ReadySet/DefaultsClass/DefaultGameService.cs b/Runtime/ReadySet/DefaultsClass/DefaultGameService.cs index 79b3c9f..2731fa7 100644 --- a/Runtime/ReadySet/DefaultsClass/DefaultGameService.cs +++ b/Runtime/ReadySet/DefaultsClass/DefaultGameService.cs @@ -10,10 +10,6 @@ protected override void OnStart(object Author) } protected override void OnNewWorld() { - } - protected override void OnNewAdditiveWorld(World target) - { - } protected override void OnEnd(object Author) { 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 From 0adb4fe1f59f941f86f8425789cd0e7344662575 Mon Sep 17 00:00:00 2001 From: Alijimpa Date: Tue, 23 Dec 2025 15:15:05 +0330 Subject: [PATCH 03/15] Refactor Prefab Define --- Editor/Core/Definitions/PrefabDrawer.cs | 26 ++++++++++++++++--------- Runtime/Core/Definitions/Prefab.cs | 6 +++--- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/Editor/Core/Definitions/PrefabDrawer.cs b/Editor/Core/Definitions/PrefabDrawer.cs index 7962de3..c5a9840 100644 --- a/Editor/Core/Definitions/PrefabDrawer.cs +++ b/Editor/Core/Definitions/PrefabDrawer.cs @@ -1,4 +1,3 @@ -#if UNITY_EDITOR using UnityEditor; using UnityEngine; using System; @@ -14,12 +13,16 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten if (prefabGOProp == null) { - EditorGUI.LabelField(position, label.text, "Invalid Prefab Field"); + 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, @@ -28,6 +31,7 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten false ); + // Only update if changed if (newObject != prefabGOProp.objectReferenceValue) { if (newObject == null) @@ -36,19 +40,19 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten } else if (PrefabUtility.IsPartOfPrefabAsset(newObject)) { - GameObject go = newObject as GameObject; - - // Get the actual object instance for this SerializedProperty - PrefabCore targetPrefab = GetTargetObjectOfProperty(property) as PrefabCore; + // Get the concrete PrefabCore instance + PrefabCore targetPrefab = fieldInfo.GetValue(property.serializedObject.targetObject) as PrefabCore; if (targetPrefab == null) { - EditorGUI.LabelField(position, label.text, "Invalid prefab wrapper."); + 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; @@ -64,10 +68,15 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten } } + // --- 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 real object from a SerializedProperty + // --- Utility to get the actual object instance from SerializedProperty --- private object GetTargetObjectOfProperty(SerializedProperty prop) { if (prop == null) return null; @@ -114,4 +123,3 @@ private object GetValue(object source, string name, int index) } } } -#endif 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 } From 9bdfe4539c8629008e7b5a78cbc19025962a040f Mon Sep 17 00:00:00 2001 From: Alijimpa Date: Tue, 23 Dec 2025 15:15:21 +0330 Subject: [PATCH 04/15] Implement Player Starter into project --- .../Core/Definitions/ConfigAssetValidator.cs | 3 - Editor/Core/Definitions/DataAssetEditor.cs | 4 +- .../ReadySet/Content/RealMethod_GameObject.cs | 27 ++++++++ .../Content/RealMethod_GameObject.cs.meta | 2 + Editor/ReadySet/Content/RealMethod_Prefab.cs | 2 +- Runtime/Core/Architecture/Game.cs | 8 ++- Runtime/Core/Architecture/GameService.cs | 24 ++++++- Runtime/Core/Architecture/World.cs | 7 +- Runtime/Library/Utilities/Draw.cs | 64 +++++++++++++++++-- .../Method/PlayerStarterComponent.cs | 31 +++++++++ .../Method/PlayerStarterComponent.cs.meta | 2 + 11 files changed, 155 insertions(+), 19 deletions(-) create mode 100644 Editor/ReadySet/Content/RealMethod_GameObject.cs create mode 100644 Editor/ReadySet/Content/RealMethod_GameObject.cs.meta create mode 100644 Runtime/ReadySet/Components/Method/PlayerStarterComponent.cs create mode 100644 Runtime/ReadySet/Components/Method/PlayerStarterComponent.cs.meta diff --git a/Editor/Core/Definitions/ConfigAssetValidator.cs b/Editor/Core/Definitions/ConfigAssetValidator.cs index bec8bc5..4d69de6 100644 --- a/Editor/Core/Definitions/ConfigAssetValidator.cs +++ b/Editor/Core/Definitions/ConfigAssetValidator.cs @@ -1,4 +1,3 @@ -#if UNITY_EDITOR using UnityEditor; using UnityEngine; using System; @@ -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/DataAssetEditor.cs b/Editor/Core/Definitions/DataAssetEditor.cs index 2ee63d0..ece664f 100644 --- a/Editor/Core/Definitions/DataAssetEditor.cs +++ b/Editor/Core/Definitions/DataAssetEditor.cs @@ -1,4 +1,3 @@ -#if UNITY_EDITOR using UnityEditor; using UnityEngine; @@ -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/ReadySet/Content/RealMethod_GameObject.cs b/Editor/ReadySet/Content/RealMethod_GameObject.cs new file mode 100644 index 0000000..720c92a --- /dev/null +++ b/Editor/ReadySet/Content/RealMethod_GameObject.cs @@ -0,0 +1,27 @@ +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; + } + } +} \ 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/Runtime/Core/Architecture/Game.cs b/Runtime/Core/Architecture/Game.cs index 9a710ef..ceff0d3 100644 --- a/Runtime/Core/Architecture/Game.cs +++ b/Runtime/Core/Architecture/Game.cs @@ -435,17 +435,19 @@ public static void HoldGameObject(GameObject Target) /// 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 UnHoldGameObject(string GameObjectName, GameObject Target) + 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; } /// @@ -533,8 +535,8 @@ private void Notify_OnWorldInitiate(World NewWorld) /// private void Notify_OnGameQuit() { - ((IMethodSync)Service).UnbindMainWorldAdd(); Application.quitting -= Notify_OnGameQuit; + ((IMethodSync)Service).UnbindMainWorldAdd(); if (GameServices != null) { for (int i = 0; i < GameServices.Count; i++) diff --git a/Runtime/Core/Architecture/GameService.cs b/Runtime/Core/Architecture/GameService.cs index d7974e6..9f2f93d 100644 --- a/Runtime/Core/Architecture/GameService.cs +++ b/Runtime/Core/Architecture/GameService.cs @@ -95,6 +95,7 @@ public abstract class GameService : Service, IMethodSync private Action ServiceEvents; + // Implement IMethodSync Interface bool IMethodSync.IntroduceWorld(World world) { @@ -284,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; @@ -303,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; } } @@ -321,6 +322,25 @@ 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)); + } diff --git a/Runtime/Core/Architecture/World.cs b/Runtime/Core/Architecture/World.cs index 6c6ed51..562e42d 100644 --- a/Runtime/Core/Architecture/World.cs +++ b/Runtime/Core/Architecture/World.cs @@ -5,7 +5,7 @@ namespace RealMethod { public abstract class PlayerStarter : MonoBehaviour { - [Header("Starter")] + [Header("Setting")] [SerializeField] private string posName = "None"; public string PosName => posName; @@ -76,6 +76,7 @@ private void Awake() WorldBegin(); } + /// /// Unity callback invoked when the object is being destroyed. /// Unbinds previously bound service callbacks to avoid dangling references. @@ -220,6 +221,7 @@ protected virtual GameObject SpawnPlayer(Prefab playerPrefab, Transform spawnPoi { GameObject player = new GameObject("Player"); player.tag = "Player"; + player.transform.SetPositionAndRotation(spawnPoint.position, spawnPoint.rotation); return player; } } @@ -247,7 +249,8 @@ protected virtual Transform SelectSpawnPoint(PlayerStarter[] starters) { if (starters.Length > 0) { - return starters[0].transform; + int index = Random.Range(0, starters.Length); + return starters[index].transform; } else { 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..a938873 --- /dev/null +++ b/Runtime/ReadySet/Components/Method/PlayerStarterComponent.cs @@ -0,0 +1,31 @@ +using UnityEngine; + +namespace RealMethod +{ + [AddComponentMenu("RealMethod/Method/PlayerStarter")] + public class PlayerStarterComponent : PlayerStarter + { + [Header("Debug")] + [SerializeField] + private float height = 2f; + [SerializeField] + private float radius = 0.5f; + + + // Unity Methods + private void Awake() + { + Destroy(gameObject); + } + + +#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 + } +} \ 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 From 977f0b9e24dc9f403f4a472ae8a5111c51fd59df Mon Sep 17 00:00:00 2001 From: Alijimpa Date: Tue, 23 Dec 2025 15:15:55 +0330 Subject: [PATCH 05/15] Change Header in World --- Runtime/Core/Architecture/World.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Core/Architecture/World.cs b/Runtime/Core/Architecture/World.cs index 562e42d..1e3bbb8 100644 --- a/Runtime/Core/Architecture/World.cs +++ b/Runtime/Core/Architecture/World.cs @@ -20,7 +20,7 @@ public abstract class PlayerStarter : MonoBehaviour /// public abstract class World : MonoBehaviour { - [Header("Player")] + [Header("Setting")] [SerializeField] private Prefab DefualtPlayer; From d725126f9abe7d0fd1305c003d317933b8da2e0d Mon Sep 17 00:00:00 2001 From: Alijimpa Date: Tue, 23 Dec 2025 15:41:06 +0330 Subject: [PATCH 06/15] Refine all Script Template for Core --- Reservoir/ScriptTemplates/GameConfigTemplate.txt | 3 ++- .../ScriptTemplates/GameServiceTemplate.txt | 16 +--------------- Reservoir/ScriptTemplates/GameTemplate.txt | 15 ++++++++++++--- Reservoir/ScriptTemplates/ManagerTemplate.txt | 16 +++++++++------- Reservoir/ScriptTemplates/ServiceTemplate.txt | 5 +++++ Reservoir/ScriptTemplates/WorldTemplate.txt | 9 ++++++--- 6 files changed, 35 insertions(+), 29 deletions(-) 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..16d7ecc 100644 --- a/Reservoir/ScriptTemplates/GameTemplate.txt +++ b/Reservoir/ScriptTemplates/GameTemplate.txt @@ -3,18 +3,27 @@ using RealMethod; public class #SCRIPTNAME# : Game { - // Base Game Methods + // Game Methods protected override void GameInitialized() { + // 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() { + // 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 } } From 0b6a49604daf2e78bac75be6e8b7c898042115b7 Mon Sep 17 00:00:00 2001 From: Alijimpa Date: Thu, 25 Dec 2025 20:38:18 +0330 Subject: [PATCH 07/15] Iimplement ClassViewer editor for guid --- Documentation/Information.meta | 8 + Documentation/Information/ClassViewer.txt | 69 +++++ .../Information/ClassViewer.txt.meta | 7 + Documentation/Resource.meta | 8 + Editor/Library/Utilities/Core.cs | 1 + Editor/ReadySet/Tools/ClassViewerWindow.cs | 264 ++++++++++++++++++ .../ReadySet/Tools/ClassViewerWindow.cs.meta | 2 + 7 files changed, 359 insertions(+) create mode 100644 Documentation/Information.meta create mode 100644 Documentation/Information/ClassViewer.txt create mode 100644 Documentation/Information/ClassViewer.txt.meta create mode 100644 Documentation/Resource.meta create mode 100644 Editor/ReadySet/Tools/ClassViewerWindow.cs create mode 100644 Editor/ReadySet/Tools/ClassViewerWindow.cs.meta 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..bc5ba57 --- /dev/null +++ b/Documentation/Information/ClassViewer.txt @@ -0,0 +1,69 @@ +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 + 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/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/ReadySet/Tools/ClassViewerWindow.cs b/Editor/ReadySet/Tools/ClassViewerWindow.cs new file mode 100644 index 0000000..b925957 --- /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 From 34647925564e1e9b0ab2d0a098356579ef94162c Mon Sep 17 00:00:00 2001 From: Alijimpa Date: Thu, 25 Dec 2025 20:38:48 +0330 Subject: [PATCH 08/15] Implement Select player Radom befor selection by order --- Runtime/Core/Architecture/World.cs | 86 +++++++++++++++++-- .../Method/PlayerStarterComponent.cs | 23 +---- Runtime/ReadySet/DefaultsClass/DefaultGame.cs | 2 +- .../DefaultsClass/DefaultGameService.cs | 4 +- 4 files changed, 82 insertions(+), 33 deletions(-) diff --git a/Runtime/Core/Architecture/World.cs b/Runtime/Core/Architecture/World.cs index 1e3bbb8..fe81608 100644 --- a/Runtime/Core/Architecture/World.cs +++ b/Runtime/Core/Architecture/World.cs @@ -3,12 +3,63 @@ 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 } /// @@ -24,12 +75,10 @@ public abstract class World : MonoBehaviour [SerializeField] private Prefab DefualtPlayer; - private IGameManager[] Managers; private GameObject PlayerObject; - /// /// Unity callback invoked when the script instance is being loaded. /// Connects the world to the game service, binds service callbacks, @@ -77,6 +126,7 @@ private void Awake() WorldBegin(); } + /// /// Unity callback invoked when the object is being destroyed. /// Unbinds previously bound service callbacks to avoid dangling references. @@ -122,6 +172,7 @@ public IGameManager GetManager(string ObjectName) } return null; } + /// /// Returns the primary player for this world. /// @@ -250,13 +301,32 @@ protected virtual Transform SelectSpawnPoint(PlayerStarter[] starters) if (starters.Length > 0) { int index = Random.Range(0, starters.Length); - return starters[index].transform; - } - else - { - transform.position = Vector3.zero; - return transform; + if (!starters[index].CanStartHere()) + { + return starters[index].transform; + } + foreach (var start in starters) + { + if (!start.CanStartHere()) + { + if (start.TryStartHere()) + { + return start.transform; + } + else + { + continue; + } + } + else + { + continue; + } + } } + + transform.position = Vector3.zero; + return transform; } /// /// Called when an additive world GameObject is added to this world. diff --git a/Runtime/ReadySet/Components/Method/PlayerStarterComponent.cs b/Runtime/ReadySet/Components/Method/PlayerStarterComponent.cs index a938873..afddb9e 100644 --- a/Runtime/ReadySet/Components/Method/PlayerStarterComponent.cs +++ b/Runtime/ReadySet/Components/Method/PlayerStarterComponent.cs @@ -3,29 +3,8 @@ namespace RealMethod { [AddComponentMenu("RealMethod/Method/PlayerStarter")] - public class PlayerStarterComponent : PlayerStarter + public sealed class PlayerStarterComponent : PlayerStarter { - [Header("Debug")] - [SerializeField] - private float height = 2f; - [SerializeField] - private float radius = 0.5f; - - // Unity Methods - private void Awake() - { - Destroy(gameObject); - } - - -#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 } } \ No newline at end of file diff --git a/Runtime/ReadySet/DefaultsClass/DefaultGame.cs b/Runtime/ReadySet/DefaultsClass/DefaultGame.cs index 57c2deb..c4a9be7 100644 --- a/Runtime/ReadySet/DefaultsClass/DefaultGame.cs +++ b/Runtime/ReadySet/DefaultsClass/DefaultGame.cs @@ -16,7 +16,7 @@ protected override void GameStarted() } protected override void OnWorldChanged(World NewWorld) { - Debug.Log($"DefultGame Synced to {NewWorld}"); + Debug.Log($"DefultGame.World Change to {NewWorld}"); } protected override void OnGameClosed() { diff --git a/Runtime/ReadySet/DefaultsClass/DefaultGameService.cs b/Runtime/ReadySet/DefaultsClass/DefaultGameService.cs index 2731fa7..fb93638 100644 --- a/Runtime/ReadySet/DefaultsClass/DefaultGameService.cs +++ b/Runtime/ReadySet/DefaultsClass/DefaultGameService.cs @@ -6,14 +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 OnEnd(object Author) { - Debug.Log("DefaultService Ended"); + Debug.Log("DefaultGameService Ended"); } } } \ No newline at end of file From 5b283d9ea3d426cb6bbbed4c4087f561a6b31167 Mon Sep 17 00:00:00 2001 From: Alijimpa Date: Sat, 27 Dec 2025 13:30:35 +0330 Subject: [PATCH 09/15] refine Class Structure --- Runtime/Core/Architecture/Game.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Core/Architecture/Game.cs b/Runtime/Core/Architecture/Game.cs index ceff0d3..8c22902 100644 --- a/Runtime/Core/Architecture/Game.cs +++ b/Runtime/Core/Architecture/Game.cs @@ -87,7 +87,7 @@ private static void InitializeGame() // Initiate Game Class var emptyObject = new GameObject("RealGame"); - Type TargetClass = ProjectSettings.GetGameInstanceClass(); + Type TargetClass = ProjectSettings.GetGameInstanceType(); if (TargetClass == null) { Debug.LogWarning("GameInstanceClass that was empty. DefaultGame Created"); From 5544bcff0d46a1bbbb17436237044dea026b5e27 Mon Sep 17 00:00:00 2001 From: Alijimpa Date: Sat, 27 Dec 2025 16:14:31 +0330 Subject: [PATCH 10/15] Implement Base forlder structure for Ptoject Setting --- .../{GameCompWindow.cs => Game_Editor.cs} | 2 +- ...CompWindow.cs.meta => Game_Editor.cs.meta} | 0 ...nOrder.cs => WorldExecutionOrder_Tools.cs} | 2 +- ...meta => WorldExecutionOrder_Tools.cs.meta} | 0 ...tValidator.cs => ConfigAsset_Validator.cs} | 4 +- ....cs.meta => ConfigAsset_Validator.cs.meta} | 0 ...DataAssetEditor.cs => DataAsset_Editor.cs} | 2 +- ...ditor.cs.meta => DataAsset_Editor.cs.meta} | 0 .../{PrefabDrawer.cs => PrefabCore_Drawer.cs} | 85 +++---- ...awer.cs.meta => PrefabCore_Drawer.cs.meta} | 0 ...Window.cs => RealMethod_ProjectSetting.cs} | 218 +++++++++--------- ...meta => RealMethod_ProjectSetting.cs.meta} | 0 .../Sections/FolderSettingSection.cs | 34 +-- .../Sections/InitializerSection.cs | 4 +- Editor/Library/Utilities/Create.cs | 4 +- .../ReadySet/Content/RealMethod_GameObject.cs | 18 ++ .../ReadySet/Tools/AnimatorScriptGenerator.cs | 4 +- Editor/Toolkit/PCG/PCGEditorWindow.cs | 4 +- Editor/Toolkit/TerrainTools/TerrainEditor.cs | 2 +- Runtime/Core/Architecture/Game.cs | 2 +- .../ProjectSetting/ProjectSettingAsset.cs | 108 +++++---- 21 files changed, 272 insertions(+), 221 deletions(-) rename Editor/Core/Architecture/{GameCompWindow.cs => Game_Editor.cs} (95%) rename Editor/Core/Architecture/{GameCompWindow.cs.meta => Game_Editor.cs.meta} (100%) rename Editor/Core/Architecture/{AutoWorldExecutionOrder.cs => WorldExecutionOrder_Tools.cs} (97%) rename Editor/Core/Architecture/{AutoWorldExecutionOrder.cs.meta => WorldExecutionOrder_Tools.cs.meta} (100%) rename Editor/Core/Definitions/{ConfigAssetValidator.cs => ConfigAsset_Validator.cs} (97%) rename Editor/Core/Definitions/{ConfigAssetValidator.cs.meta => ConfigAsset_Validator.cs.meta} (100%) rename Editor/Core/Definitions/{DataAssetEditor.cs => DataAsset_Editor.cs} (94%) rename Editor/Core/Definitions/{DataAssetEditor.cs.meta => DataAsset_Editor.cs.meta} (100%) rename Editor/Core/Definitions/{PrefabDrawer.cs => PrefabCore_Drawer.cs} (61%) rename Editor/Core/Definitions/{PrefabDrawer.cs.meta => PrefabCore_Drawer.cs.meta} (100%) rename Editor/Core/ProjectSetting/{ProjectSettingWindow.cs => RealMethod_ProjectSetting.cs} (85%) rename Editor/Core/ProjectSetting/{ProjectSettingWindow.cs.meta => RealMethod_ProjectSetting.cs.meta} (100%) diff --git a/Editor/Core/Architecture/GameCompWindow.cs b/Editor/Core/Architecture/Game_Editor.cs similarity index 95% rename from Editor/Core/Architecture/GameCompWindow.cs rename to Editor/Core/Architecture/Game_Editor.cs index 13d6815..08c9380 100644 --- a/Editor/Core/Architecture/GameCompWindow.cs +++ b/Editor/Core/Architecture/Game_Editor.cs @@ -3,7 +3,7 @@ namespace RealMethod { [CustomEditor(typeof(Game), true)] - public class GameCompWindow : UnityEditor.Editor + public class Game_Editor : UnityEditor.Editor { private Game BaseComponent; 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_Validator.cs similarity index 97% rename from Editor/Core/Definitions/ConfigAssetValidator.cs rename to Editor/Core/Definitions/ConfigAsset_Validator.cs index 4d69de6..65cf625 100644 --- a/Editor/Core/Definitions/ConfigAssetValidator.cs +++ b/Editor/Core/Definitions/ConfigAsset_Validator.cs @@ -7,9 +7,9 @@ namespace RealMethod.Editor { [InitializeOnLoad] - public static class ConfigAssetValidator + public static class ConfigAsset_Validator { - static ConfigAssetValidator() + static ConfigAsset_Validator() { EditorApplication.delayCall += ValidateAllConfigs; } diff --git a/Editor/Core/Definitions/ConfigAssetValidator.cs.meta b/Editor/Core/Definitions/ConfigAsset_Validator.cs.meta similarity index 100% rename from Editor/Core/Definitions/ConfigAssetValidator.cs.meta rename to Editor/Core/Definitions/ConfigAsset_Validator.cs.meta diff --git a/Editor/Core/Definitions/DataAssetEditor.cs b/Editor/Core/Definitions/DataAsset_Editor.cs similarity index 94% rename from Editor/Core/Definitions/DataAssetEditor.cs rename to Editor/Core/Definitions/DataAsset_Editor.cs index ece664f..fc68194 100644 --- a/Editor/Core/Definitions/DataAssetEditor.cs +++ b/Editor/Core/Definitions/DataAsset_Editor.cs @@ -3,7 +3,7 @@ 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 { 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/PrefabDrawer.cs b/Editor/Core/Definitions/PrefabCore_Drawer.cs similarity index 61% rename from Editor/Core/Definitions/PrefabDrawer.cs rename to Editor/Core/Definitions/PrefabCore_Drawer.cs index c5a9840..37f4363 100644 --- a/Editor/Core/Definitions/PrefabDrawer.cs +++ b/Editor/Core/Definitions/PrefabCore_Drawer.cs @@ -5,7 +5,7 @@ namespace RealMethod.Editor { [CustomPropertyDrawer(typeof(PrefabCore), true)] - public class PrefabDrawer : PropertyDrawer + public class PrefabCore_Drawer : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { @@ -76,50 +76,51 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten 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); - } + // --- Utility to get the actual object instance from SerializedProperty --- + // private object GetTargetObjectOfProperty(SerializedProperty prop) + // { + // if (prop == null) return null; - 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; - } + // 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/ProjectSetting/ProjectSettingWindow.cs b/Editor/Core/ProjectSetting/RealMethod_ProjectSetting.cs similarity index 85% rename from Editor/Core/ProjectSetting/ProjectSettingWindow.cs rename to Editor/Core/ProjectSetting/RealMethod_ProjectSetting.cs index 6f1ebfe..f085417 100644 --- a/Editor/Core/ProjectSetting/ProjectSettingWindow.cs +++ b/Editor/Core/ProjectSetting/RealMethod_ProjectSetting.cs @@ -7,7 +7,110 @@ namespace RealMethod.Editor { - public static class ProjectSettingWindow + 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 @@ -15,10 +118,11 @@ public static class ProjectSettingWindow // Array of sections to be rendered in the settings UI new InitializerSection(), new FolderSettingSection() - }; + }; - [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 +150,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 +164,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 +185,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 +215,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/FolderSettingSection.cs index 5396d8c..b9a6298 100644 --- a/Editor/Core/ProjectSetting/Sections/FolderSettingSection.cs +++ b/Editor/Core/ProjectSetting/Sections/FolderSettingSection.cs @@ -8,18 +8,26 @@ public class FolderSettingSection : 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/InitializerSection.cs b/Editor/Core/ProjectSetting/Sections/InitializerSection.cs index 477e7f7..b975d3a 100644 --- a/Editor/Core/ProjectSetting/Sections/InitializerSection.cs +++ b/Editor/Core/ProjectSetting/Sections/InitializerSection.cs @@ -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/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 index 720c92a..e7e2e06 100644 --- a/Editor/ReadySet/Content/RealMethod_GameObject.cs +++ b/Editor/ReadySet/Content/RealMethod_GameObject.cs @@ -20,6 +20,24 @@ static void CreatePlayerStarter(MenuCommand menuCommand) // 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; } 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/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/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/Runtime/Core/Architecture/Game.cs b/Runtime/Core/Architecture/Game.cs index 8c22902..e5f7389 100644 --- a/Runtime/Core/Architecture/Game.cs +++ b/Runtime/Core/Architecture/Game.cs @@ -104,7 +104,7 @@ private static void InitializeGame() } // Create Game Service - Type targetService = ProjectSettings.GetGameServiceClass(); + Type targetService = ProjectSettings.GetGameServiceType(); if (targetService == null) { Debug.LogWarning($"GetGameServiceClass that was empty. DefaultGameService Created"); diff --git a/Runtime/Core/ProjectSetting/ProjectSettingAsset.cs b/Runtime/Core/ProjectSetting/ProjectSettingAsset.cs index cea5c51..3156a2e 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 @@ -6,15 +7,22 @@ namespace RealMethod // Real Method Setting Storage public class ProjectSettingAsset : ScriptableObject { + private string ProjectName; [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 +37,20 @@ 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 +61,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 +101,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 +112,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 +123,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,18 +148,27 @@ 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; } } return default(FolderAddress); } - } From 80535b0b3ff779bfcb64e25df30be6618ad1e9ad Mon Sep 17 00:00:00 2001 From: Alijimpa Date: Sat, 27 Dec 2025 17:05:07 +0330 Subject: [PATCH 11/15] Refine Naming and folder sorting for Core --- Documentation/Information/ClassViewer.txt | 1 + Editor/Core/Architecture/Game_Editor.cs | 2 +- ...set_Validator.cs => ConfigAsset_Onload.cs} | 4 +- ...tor.cs.meta => ConfigAsset_Onload.cs.meta} | 0 .../RealMethod_ProjectSetting.cs | 6 +- ...gSection.cs => FolderStructure_Section.cs} | 2 +- ...s.meta => FolderStructure_Section.cs.meta} | 0 ...ction.cs => InitializerSetting_Section.cs} | 2 +- ...eta => InitializerSetting_Section.cs.meta} | 0 .../UnityAsset_Postprocessor.cs | 123 ------------------ .../SharedScripts/Classes/AssetProcess.cs | 67 ++++++++++ .../Classes/AssetProcess.cs.meta | 2 + .../ReadySet/Content/RealMethod_UnityAsset.cs | 6 +- .../Content/UnityAsset_Postprocessor.cs | 58 +++++++++ .../Content}/UnityAsset_Postprocessor.cs.meta | 0 Editor/Toolkit/PCG/PCG_UnityAsset.cs | 6 +- Reservoir/ScriptTemplates/GameTemplate.txt | 4 +- Runtime/Core/Architecture/Game.cs | 25 +++- .../ProjectSetting/ProjectSettingAsset.cs | 5 +- Runtime/ReadySet/DefaultsClass/DefaultGame.cs | 4 +- 20 files changed, 168 insertions(+), 149 deletions(-) rename Editor/Core/Definitions/{ConfigAsset_Validator.cs => ConfigAsset_Onload.cs} (97%) rename Editor/Core/Definitions/{ConfigAsset_Validator.cs.meta => ConfigAsset_Onload.cs.meta} (100%) rename Editor/Core/ProjectSetting/Sections/{FolderSettingSection.cs => FolderStructure_Section.cs} (98%) rename Editor/Core/ProjectSetting/Sections/{FolderSettingSection.cs.meta => FolderStructure_Section.cs.meta} (100%) rename Editor/Core/ProjectSetting/Sections/{InitializerSection.cs => InitializerSetting_Section.cs} (96%) rename Editor/Core/ProjectSetting/Sections/{InitializerSection.cs.meta => InitializerSetting_Section.cs.meta} (100%) delete mode 100644 Editor/Core/ProjectSetting/UnityAsset_Postprocessor.cs create mode 100644 Editor/Library/SharedScripts/Classes/AssetProcess.cs create mode 100644 Editor/Library/SharedScripts/Classes/AssetProcess.cs.meta create mode 100644 Editor/ReadySet/Content/UnityAsset_Postprocessor.cs rename Editor/{Core/ProjectSetting => ReadySet/Content}/UnityAsset_Postprocessor.cs.meta (100%) diff --git a/Documentation/Information/ClassViewer.txt b/Documentation/Information/ClassViewer.txt index bc5ba57..8d4a0d4 100644 --- a/Documentation/Information/ClassViewer.txt +++ b/Documentation/Information/ClassViewer.txt @@ -8,6 +8,7 @@ Runtime 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 diff --git a/Editor/Core/Architecture/Game_Editor.cs b/Editor/Core/Architecture/Game_Editor.cs index 08c9380..6ad9fdd 100644 --- a/Editor/Core/Architecture/Game_Editor.cs +++ b/Editor/Core/Architecture/Game_Editor.cs @@ -1,6 +1,6 @@ using UnityEditor; -namespace RealMethod +namespace RealMethod.Editor { [CustomEditor(typeof(Game), true)] public class Game_Editor : UnityEditor.Editor diff --git a/Editor/Core/Definitions/ConfigAsset_Validator.cs b/Editor/Core/Definitions/ConfigAsset_Onload.cs similarity index 97% rename from Editor/Core/Definitions/ConfigAsset_Validator.cs rename to Editor/Core/Definitions/ConfigAsset_Onload.cs index 65cf625..d5f4ee2 100644 --- a/Editor/Core/Definitions/ConfigAsset_Validator.cs +++ b/Editor/Core/Definitions/ConfigAsset_Onload.cs @@ -7,9 +7,9 @@ namespace RealMethod.Editor { [InitializeOnLoad] - public static class ConfigAsset_Validator + public static class ConfigAsset_Onload { - static ConfigAsset_Validator() + static ConfigAsset_Onload() { EditorApplication.delayCall += ValidateAllConfigs; } diff --git a/Editor/Core/Definitions/ConfigAsset_Validator.cs.meta b/Editor/Core/Definitions/ConfigAsset_Onload.cs.meta similarity index 100% rename from Editor/Core/Definitions/ConfigAsset_Validator.cs.meta rename to Editor/Core/Definitions/ConfigAsset_Onload.cs.meta diff --git a/Editor/Core/ProjectSetting/RealMethod_ProjectSetting.cs b/Editor/Core/ProjectSetting/RealMethod_ProjectSetting.cs index f085417..85690a4 100644 --- a/Editor/Core/ProjectSetting/RealMethod_ProjectSetting.cs +++ b/Editor/Core/ProjectSetting/RealMethod_ProjectSetting.cs @@ -7,11 +7,13 @@ namespace RealMethod.Editor { + // 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 { @@ -116,8 +118,8 @@ public static class RealMethod_ProjectSetting 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() }; diff --git a/Editor/Core/ProjectSetting/Sections/FolderSettingSection.cs b/Editor/Core/ProjectSetting/Sections/FolderStructure_Section.cs similarity index 98% rename from Editor/Core/ProjectSetting/Sections/FolderSettingSection.cs rename to Editor/Core/ProjectSetting/Sections/FolderStructure_Section.cs index b9a6298..c62e6ea 100644 --- a/Editor/Core/ProjectSetting/Sections/FolderSettingSection.cs +++ b/Editor/Core/ProjectSetting/Sections/FolderStructure_Section.cs @@ -3,7 +3,7 @@ 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 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 96% rename from Editor/Core/ProjectSetting/Sections/InitializerSection.cs rename to Editor/Core/ProjectSetting/Sections/InitializerSetting_Section.cs index b975d3a..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; 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/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/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/Reservoir/ScriptTemplates/GameTemplate.txt b/Reservoir/ScriptTemplates/GameTemplate.txt index 16d7ecc..9227586 100644 --- a/Reservoir/ScriptTemplates/GameTemplate.txt +++ b/Reservoir/ScriptTemplates/GameTemplate.txt @@ -4,12 +4,12 @@ public class #SCRIPTNAME# : Game { // Game Methods - protected override void GameInitialized() + 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. diff --git a/Runtime/Core/Architecture/Game.cs b/Runtime/Core/Architecture/Game.cs index e5f7389..1c30658 100644 --- a/Runtime/Core/Architecture/Game.cs +++ b/Runtime/Core/Architecture/Game.cs @@ -102,6 +102,7 @@ private static void InitializeGame() 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(); @@ -168,25 +169,25 @@ private static void InitializeGame() Application.quitting += Instance.Notify_OnGameQuit; } /// - /// Called before any scene is loaded. Invokes on the active instance. + /// Called before any scene is loaded. Invokes on the active instance. /// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void RuntimeBeforeSceneLoad() { if (Instance != null) { - Instance.GameInitialized(); + Instance.OnGameInitialized(); } } /// - /// Called after a scene has finished loading. Invokes on the active instance. + /// Called after a scene has finished loading. Invokes on the active instance. /// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] private static void RuntimeAfterSceneLoad() { if (Instance != null) { - Instance.GameStarted(); + Instance.OnGameStarted(); } } @@ -516,6 +517,18 @@ public IGameManager GetManager(string ObjectName) } + /// + /// 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) + { + // Nothing todo + } + + /// /// Callback invoked when a new is created or assigned. /// Updates the static reference and notifies all services. @@ -554,12 +567,12 @@ private void Notify_OnGameQuit() /// Called once when the game framework has finished initial initialization. /// Implement this to perform game-specific initialization logic. /// - protected abstract void GameInitialized(); + 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 GameStarted(); + protected abstract void OnGameStarted(); /// /// Called when the current reference changes. /// Implement to react to world switches. diff --git a/Runtime/Core/ProjectSetting/ProjectSettingAsset.cs b/Runtime/Core/ProjectSetting/ProjectSettingAsset.cs index 3156a2e..3b65e43 100644 --- a/Runtime/Core/ProjectSetting/ProjectSettingAsset.cs +++ b/Runtime/Core/ProjectSetting/ProjectSettingAsset.cs @@ -7,7 +7,6 @@ namespace RealMethod // Real Method Setting Storage public class ProjectSettingAsset : ScriptableObject { - private string ProjectName; [Serializable] public struct FolderAddress { @@ -16,7 +15,7 @@ public struct FolderAddress public string FolderName => System.IO.Path.GetFileName(AssetPath); public string GetFolderPath(ProjectSettingAsset settingAsset) { - string RootPath = settingAsset.GetStructureType() == 0 ? "Assets" : "Assets/"+Application.productName; + string RootPath = settingAsset.GetStructureType() == 0 ? "Assets" : "Assets/" + Application.productName; return $"{RootPath}/{AssetPath}"; } @@ -46,7 +45,6 @@ public enum FolderStructureType ProjectName = 1, } - [Header("Initializer")] [SerializeField, ReadOnly] private string GameClass = "RealMethod.DefultGame"; @@ -169,6 +167,7 @@ public FolderAddress GetFolderAddressByType(AssetFormat identity) } return default(FolderAddress); } + } diff --git a/Runtime/ReadySet/DefaultsClass/DefaultGame.cs b/Runtime/ReadySet/DefaultsClass/DefaultGame.cs index c4a9be7..fa1f575 100644 --- a/Runtime/ReadySet/DefaultsClass/DefaultGame.cs +++ b/Runtime/ReadySet/DefaultsClass/DefaultGame.cs @@ -6,11 +6,11 @@ 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"); } From 1a67ff77da17f6f8b14a2502d5ec9865327a09ef Mon Sep 17 00:00:00 2001 From: Alijimpa Date: Sat, 27 Dec 2025 17:12:59 +0330 Subject: [PATCH 12/15] Fix Class Viewer for Show Nunlink Address --- Editor/ReadySet/Tools/ClassViewerWindow.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Editor/ReadySet/Tools/ClassViewerWindow.cs b/Editor/ReadySet/Tools/ClassViewerWindow.cs index b925957..6a25e38 100644 --- a/Editor/ReadySet/Tools/ClassViewerWindow.cs +++ b/Editor/ReadySet/Tools/ClassViewerWindow.cs @@ -33,7 +33,7 @@ public static void Open() private void OnEnable() { string ClassViewPath = "Assets/Realmethod/Documentation/Information/ClassViewer.txt"; // Just for Test - //string ClassViewPath = Path.Combine(RM_CoreEditor.Documentation, "ClassViewer.txt"); + // string ClassViewPath = Path.Combine(RM_CoreEditor.Documentation, "ClassViewer.txt"); if (!File.Exists(ClassViewPath)) { Debug.LogError($"ClassView file not found: {ClassViewPath}"); @@ -114,10 +114,10 @@ private void LoadFromText(string text) endText = middleText.Substring(0, linkindex).Trim(); linkText = middleText.Substring(linkindex).Trim(); } - } - else - { - endText = middleText; + else + { + endText = middleText; + } } ClassTreeNode node = new ClassTreeNode From fbabe9bc62f7785d3f0138adad88d79ff0f81bb3 Mon Sep 17 00:00:00 2001 From: Alijimpa Date: Sun, 28 Dec 2025 12:10:05 +0330 Subject: [PATCH 13/15] Wrok on ReadMe --- Documentation/README.md | 26 +++ .../README.md.meta | 0 .../Resource/RM_Title.png | Bin .../Resource/RM_Title.png.meta | 0 README.md | 148 ------------------ 5 files changed, 26 insertions(+), 148 deletions(-) create mode 100644 Documentation/README.md rename README.md.meta => Documentation/README.md.meta (100%) rename Reservoir/Icons/Core/RealMethod.png => Documentation/Resource/RM_Title.png (100%) rename Reservoir/Icons/Core/RealMethod.png.meta => Documentation/Resource/RM_Title.png.meta (100%) delete mode 100644 README.md diff --git a/Documentation/README.md b/Documentation/README.md new file mode 100644 index 0000000..1b90d48 --- /dev/null +++ b/Documentation/README.md @@ -0,0 +1,26 @@ +# 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 + diff --git a/README.md.meta b/Documentation/README.md.meta similarity index 100% rename from README.md.meta rename to Documentation/README.md.meta 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/README.md b/README.md deleted file mode 100644 index abe4bee..0000000 --- a/README.md +++ /dev/null @@ -1,148 +0,0 @@ -# 🌌 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. From eb4d2e95429ef407acd1c6a0b027be8bbbb7f2c9 Mon Sep 17 00:00:00 2001 From: Alijimpa Date: Sun, 28 Dec 2025 12:15:25 +0330 Subject: [PATCH 14/15] Miss Readme Place --- Documentation/README.md => README.md | 0 Documentation/README.md.meta => README.md.meta | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename Documentation/README.md => README.md (100%) rename Documentation/README.md.meta => README.md.meta (100%) diff --git a/Documentation/README.md b/README.md similarity index 100% rename from Documentation/README.md rename to README.md diff --git a/Documentation/README.md.meta b/README.md.meta similarity index 100% rename from Documentation/README.md.meta rename to README.md.meta From dab4f11f246033b85d8cc6cd62bd8f8565e2c3e4 Mon Sep 17 00:00:00 2001 From: Ali Badpa Date: Sun, 28 Dec 2025 12:29:59 +0330 Subject: [PATCH 15/15] Update README.md add gallry --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 1b90d48..718e059 100644 --- a/README.md +++ b/README.md @@ -23,4 +23,9 @@ Install via Unity Package Manager: >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) |