Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Assets World Manager - Generic world management system

Discussion in 'Works In Progress - Archive' started by AdamGoodrich, Jul 20, 2017.

  1. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    Hey everyone,

    After recently launching CTS, and seeing how quickly and well the weather management system was accepted, and then having some awesome conversations in the Gaia 2 beta, I made the first cut of a new and generic free and open source environmental management system.

    In the image below we have lighting, skies, clouds, terrain, terrain shading, weather, and sound, and they all need to be synchronized properly to create the immersion you want. I do this all the time and its just painful!

    Grab 20170718154552 w1822h1084 x185y65z125r143.jpg

    The Problem:

    I want make a game, and none of the assets I want to use are integrated. I want a common way to create and manage my scenes with things such as time of day, terrain, lighting and weather all working together.

    The Solution:

    World Manager (WAPI) - A generic world and environment command and control system for Unity that enables environmental assets to play nicely together.

    It does this by providing a simple but powerful interface that allows you to control the components that render your environment a generic and coordinated way.

    The intention is to bridge the gap between assets and make it easier to develop games by plugging them together using a common API. You will still configure your assets individually to your own taste, but coordination and control is generic and centralised.

    Assets can choose which parts of the API they implement and regardless of how much they support, you will get value from this system because of the way it coordinates their behaviour.

    Additionally, WAPI introduces a global set of shader variables, so that any shader that implements WAPI will automatically be updated when settings change without requiring the overhead associated with the subscription mechanism.

    This API will be kept stable and professionaly maintained, and will also be available as a free unity asset (soon).

    Basic Usage Pattern:

    o generate events, and get and set values use the following generic format e.g. WorldManager.Instance.Api()

    To receive events when values are changed implement your own listener via the IWorldApiChangeHandler interface and then connect it up e.g.

    Code (CSharp):
    1. public class WorldController : MonoBehaviour, IWorldApiChangeHandler
    2. {
    3.     .. your stuff..
    4.  
    5.     void Start()
    6.     {
    7.         ConnectToWorldAPI();
    8.     }
    9.  
    10.     //Let world manager API know you want to handle events
    11.     void ConnectToWorldAPI()
    12.     {
    13.         WorldManager.Instance.AddListener(this);
    14.     }
    15.  
    16.     //Let world manager API know that you are no longer interested
    17.     void DisconnectFromWorldAPI()
    18.     {
    19.         WorldManager.Instance.RemoveListener(this);
    20.     }
    21.  
    22.     //If this object has been added as a listener then it will be called whenever an event is fired,
    23.     //use the changeArgs.HasChanged method to filter for the events you are interested in
    24.     public void OnWorldChanged(WorldChangeArgs changeArgs)
    25.     {
    26.         if (changeArgs.HasChanged(WorldConstants.WorldChangeEvents.GameTimeChanged))
    27.         {
    28.             //Grab game time
    29.             m_timeNow = (float)changeArgs.manager.GetTimeDecimal();
    30.  
    31.             //Do whatever logic you want
    32.             m_timeNow += 0.25f;
    33.  
    34.             //Set it back into world manager -> NOTE you would never do THIS SPECIFIC THING
    35.             //as this will cause another OnWorldChanged event to be generated, which would in turn
    36.             //cause this to be executed again in one nasty loop
    37.             WorldManager.Instance.SetDecimalTime(m_timeNow);
    38.         }
    39.     }
    40. }
    Take a look at WorldController.cs for a simple example of how to use the API. It both listens to things and also implements a simple user interface that controls it in the editor and at runtime.

    To use as global variables in shaders the general naming standard is _WAPI_[PropertyName], however in many instances the data has been stored in vectors for efficient transfer to the GPU, so take a peek at the code to get the naming. At the very least however all shader variables are prefixed with _WAPI_.

    API Categories:
    • IsActive
    • Time
    • Player Location, Sea Level, Latitude & Longitude, Scene Center & Size
    • Temperature and humidity
    • Wind
    • Fog
    • Rain
    • Hail
    • Snow
    • Thunder
    • Clouds
    • Moon
    • Season
    • Sound Levels
    • Extensions
    • Serilialisation
    Serialisation System

    The serialisation system allows you to manage the process of saving and loading WAPI data. The choice of when to do this and how to store the serialised data are up to you.

    Extension System

    The extension system allows you to create your own extension sub classes and add them to WAPI to be managed. You could add additional game state information as well handlers for Update and LateUpdate and have them called on your extensions when World Manager updates. Just derive your class from WorldManagerExtension, and add it to the extensions list.

    I have made the source available here for comment. The WorldManager.cs file has the bulk of the API work : https://github.com/adamgoodrich/WorldManager

    And when it's been through some peer review I will maintain and publish it as a free asset on the store.

    In the next few days I will also extend on the demo, and show it working with some more sophisticated assets.

    Love to get your feedback.
     
    Last edited: Aug 22, 2017
  2. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,492
    The small prints says: does not support infinite world
     
  3. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    I don't understand your point nor do I agree with your comment. The majority of the API's have nothing to do with world size.

    If you would like to be positive and contribute ideas and suggestions then go for it - this is a free service to the community in an attempt to solve a tedious problem, and community feedback will determine how or even if this is further developed.
     
    Last edited: Jul 20, 2017
  4. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,492
    Sorry for the cheap jokes lol, it was a reference to some stance with gaia, since I support/seek infinite terrain system. Wasn't as funny as I think it was so I apologize.
     
    Tony-Lovell and AdamGoodrich like this.
  5. derkoi

    derkoi

    Joined:
    Jul 3, 2012
    Posts:
    2,255
    Looks interesting however I've rolled my own now which I'll be using.
     
  6. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    Love to get your feedback on this if you would consider having a quick look. Thoughts and feedback much appreciated.
     
  7. derkoi

    derkoi

    Joined:
    Jul 3, 2012
    Posts:
    2,255
    Sure. I could probably use it alongside mine & plug some of my system in to it, but at a later date. (It deals with real world weather from forecasts, dependent on location of the game world in lat long, failing that it simulates the weather)
     
  8. derkoi

    derkoi

    Joined:
    Jul 3, 2012
    Posts:
    2,255
    Just had a brief play around with it and I can see the potential. Just need to plug all the different systems in to it.
     
    AdamGoodrich likes this.
  9. PiterQ70

    PiterQ70

    Joined:
    Mar 3, 2015
    Posts:
    82
    Sounds great :) But.. What will be this? For other weather systems? Like UniStorm?
     
  10. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    Any weather system that wants to support it. I am playing around with a preview Enviro integration right now :)

    World Manager still very new - by tomorrow I will have a nice sample controller UX working, and it will be pretty cool seeing it all work together with Enviro.
     
    Mr-Logan, Teila and BackwoodsGaming like this.
  11. PiterQ70

    PiterQ70

    Joined:
    Mar 3, 2015
    Posts:
    82
    Oh I get it :)
     
  12. HL00

    HL00

    Joined:
    Oct 21, 2016
    Posts:
    2
    Ohh it's great.
    What do you expect from amazing asset? Free? Here we are.

    Gonna love to use it, thanks for sharing
     
  13. BackwoodsGaming

    BackwoodsGaming

    Joined:
    Jan 2, 2014
    Posts:
    2,229
    I think something like this has a lot of potential! Excited to see where it goes. I think it is a great resource for those of us who are asset junkies and can't make up our mind on which day/night/weather system we want to use. Any chance of a save system where we can backup/share our weather/fx/lighting configurations similar to CTS Profiles, Gaia Sessions, etc?
     
  14. Teila

    Teila

    Joined:
    Jan 13, 2013
    Posts:
    6,932
    You guys are AWESOME!
     
    TeagansDad and Mr-Logan like this.
  15. petersvp

    petersvp

    Joined:
    Dec 20, 2013
    Posts:
    63
    Please just rename this thing to WeatherManager. WorldManager name is to be used for something else indeed, that represents Worlds, not Weather features. (in my game there is already namespace WorldAPI, and classes like WorldManager, ChunksProcessor and so on, by coincidence :))

    And this system is a gem.
     
  16. Mr-Logan

    Mr-Logan

    Joined:
    Apr 13, 2006
    Posts:
    455
    I hadn't thought of it til Petersvp mentioned it, but it's actually more like an environment Manage than a world manager as far as I can see.
    That said, having a custom namespace to avoid clashes is a must, and something that has already been done in at least Gaia (and I imagine in all your other assets too).

    I'll need something like that for (an not infinite, but very very large) world I've been working on for a long while now, I'll likely look at the source and see if I can help out once I get to that point in our development. :)
     
  17. Teila

    Teila

    Joined:
    Jan 13, 2013
    Posts:
    6,932
    Looks like a way for us to integrate our own assets, such as Enviro or Tenkoku with CTS and Gaia or other terrain/environment assets.

    This is not a weather system, but a way to integrate all those parts, weather, terrain, shaders, etc., together more easily. So if you own Tenkoku then you can integrate it with Megaspats or CTS and/or Gaia. I own Enviro so I can integrate that easily with Gaia and CTS.

    This is not one asset that will do it all, but a free asset, yes, FREE, that we can use to integrate different assets together.

    And the name World Manager is absolutely perfect!
     
  18. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    I initially thought of Enviro API, but there is a product called Enviro, so chose World manager because I hadn't seen it before.

    Everything is namespaced within WorldAPI so in general this should not be a problem to use. Changing that to WAPI would also be a simple thing to do now.

    It is however more than just a weather system and I am open to different names. People are already integrating and I don't want to waste their time, so if you have alternatives then please fire them off now.
     
  19. CodeFriendlyArt

    CodeFriendlyArt

    Joined:
    Jun 3, 2016
    Posts:
    10
    I like the idea, seems like something everyone can put to good use. I have spent countless hours myself tweaking around settings of open world environment and atmosphere gestalt (overall).

    If something like this is to be born I would personally love to see a demo!

    Regarding Fog values -> don't make values absolute, don't get hold of the full value, just of *changes to initial values

    Also think that WorldManger is a good name. After all it refers (based on the description) to the Player's world and that could be anything from a Planet to a Galaxy say.. so your full World is whichever size you want it to be and composed of whichever amount of subWorlds on it. I guess?

    Then I guess it should have statusInfo puller methods on the different aspects its supporting too. Or does it only will support WorldManager.Instance.OnApiChangedHandler() for now? Like not only I be able to learn of a 'change' but also of actual conditions...

    So say if my Mage player is about to cast a water element based spell and say I can read something similar to:

    if (WorldManager.Instance.WorldInfo.Weather.isRain) Mage.Spells.WaterElement.isOnBonus = true;

    And things like this? Or am I asking/deviating too much from the original intention of the manager?
     
    TeagansDad and AdamGoodrich like this.
  20. Teila

    Teila

    Joined:
    Jan 13, 2013
    Posts:
    6,932
    Just a guess...if I read all this correctly....

    If an asset developer of a weather system wants to hook into something that will allow your mage to do this, then probably. But it depends on the weather system you use and if the developer wants to add that. :) Or you can add it yourself.

    You have to purchase the weather asset though.
     
  21. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    If you take a quick look at the code you will see it supports 3 mechanisms:

    Code (CSharp):
    1. /// <summary>
    2.         /// Player location world units - useful as a lot of FX require a location to work effectively
    3.         /// </summary>
    4.         public Vector3 PlayerLocation
    5.         {
    6.             get { return m_playerLocation; }
    7.             set
    8.             {
    9.                 if (m_playerLocation != value)
    10.                 {
    11.                     m_playerLocation = value;
    12.                     Shader.SetGlobalVector("_WAPI_PlayerLocation", m_playerLocation);
    13.                     if (OnPlayerLocationChanged != null)
    14.                     {
    15.                         OnPlayerLocationChanged(this, m_playerLocation);
    16.                     }
    17.                 }
    18.             }
    19.         }
    20.         public event Vector3ChangedEventHandler OnPlayerLocationChanged;

    Property based getters and setters.

    Global values for shaders that want to use it.

    And OnChanged style events for asynchronous change notifications.
     
    Last edited: Jul 22, 2017
    TeagansDad likes this.
  22. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    This was exactly why I made it - to make a simple common interface that everything can use.

    There is a demo - look in the demo scene.

    It's simple and growing, but it shows how everything is wired together. My plan is to add some simple keyboard control logic in there so that you can manually control the entire system with key presses.

    For example "[" and "]" to skip back and forward in time.

    I often want to explore light and weather when I am making test scenes, so these will be useful both for game setup and testing as well as showing how the system fits together.
     
  23. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,589
    Hi Adam, WorldManager API is a great idea. Thank you for that!

    I reviewed the code and noted a few things that you can find below. I tried to write all notes objectively. Not all my assumptions might be correct in WorldAPI context. I tried to describe why I personally don't use some code patterns that are present in WorldAPI.

    I spent quite a while reading the code and writing the following text. I did this, because I believe WorldAPI has the potential to become something great, therefore don't feel offended in any way.


    WorldManager.cs

    Code (CSharp):
    1. if (m_instance == null)
    2. {
    3.     m_instance = (WorldManager)FindObjectOfType(typeof(WorldManager));
    4.  
    5.     if (FindObjectsOfType(typeof(WorldManager)).Length > 1)
    6.     {
    7.         return m_instance;
    8.     }
    I'm not sure what the if (FindObjectsOfType(typeof(WorldManager)).Length > 1) is for. Did you intend to output a warning if multiple instances exist?


    Code (CSharp):
    1. if (m_instance == null)
    2. {
    3.     GameObject singleton = new GameObject();
    4.     m_instance = singleton.AddComponent<WorldManager>();
    5.     singleton.name = typeof(WorldManager).ToString();
    6.     if (Application.isPlaying)
    7.     {
    8.         DontDestroyOnLoad(singleton);
    9.     }
    10. }
    Uses of DontDestroyOnLoad caused several memory issues/leaks in past projects I worked on and we try to avoid using it where possible since then. The problem we experienced was that some singletons kept references to managed and Unity objects (eg to methods of Components on GameObjects) which caused them to get never released and we had to figure it out using Unity's Memory Profiler (tricky issue to find). It gets worse when editor code is accessing the singleton, since this will then create a new GameObject in the scene during edit mode which is serialized with the scene, if not properly marked with hideFlags.

    For this reason, we moved away from DontDestroyOnLoad for most things. Most singletons get destroyed with the scene and get created the first time they're accessed, which is usually during scene load when a component is accessing a singleton in their Awake(). Moving to this approach solved several memory issues for us.


    Code (CSharp):
    1. public static WorldManager Instance
    2. {
    3.     get
    4.     {
    5.         lock (m_lock)
    6.         {
    What problem does the lock solve here?

    Code (CSharp):
    1. [SerializeField]
    2. protected bool m_worldAPIActive = true;
    I don't understand why member variables are marked as protected. Because it's a singleton, I don't think people derive from this class and need to access them. Perhaps member variables should be private?

    Code (CSharp):
    1. public class WorldManager : MonoBehaviour
    Perhaps add the 'sealed' keyword to make sure nobody derives a class from WorldManager? WorldManager doesn't work with derived classes anyway, as far as I understand the code.

    Code (CSharp):
    1. Shader.SetGlobalVector("_WAPI_Wind", m_windData);
    Several Unity performance talks recommend to use the integer API over the string API, to avoid frequent string to hash conversions. This applies especially for variables that are expected to change every frame.

    Code (CSharp):
    1. if (m_playerLocation != value)
    2. {
    3.     m_playerLocation = value;
    4.     Shader.SetGlobalVector("_WAPI_PlayerLocation", m_playerLocation);
    5. }
    I was thinking perhaps it makes sense to defer all Shader.SetGlobalVector calls and submit them all at once. The benefit would be that even if PlayerLocation is assigned a different value multiple times during the same frame (for whatever reason), the code wouldn't constantly update shader globals but submit only the last value. It could make it also easier to support a ComputeBuffer path (for high(er) end hardware) if all the Shader.Set calls are in one place.

    Code (CSharp):
    1. WorldManager.Instance.OnApiChangedHandler += YourHandler()
    We moved away from using this kind of event subscription, because it allocates memory always. Furthermore, delegates often caused things to not get GC'ed in various projects I worked on, especially if the object that keeps the event uses DontDestroyOnLoad. The memory allocation probably isn't as much of an issue if things subscribe during initialization only, but if you don't have full control over its usage (which you don't really have as an API provider), I recommend to avoid using it.

    We replaced many of these events with interfaces, to workaround the memory allocation issue, and make debugging easier (events are linked lists and harder to inspect than just a flat list).

    In our case, rather than having an OnApiChangedHandler event, we would provide a IWorldApiChangedHandler interface as shown below:
    Code (CSharp):
    1. public class WorldManager : MonoBehaviour
    2. {
    3.     List<IWorldApiChangedHandler> m_worldApiChangedListeners;
    4.  
    5.     public void AddListener(IWorldApiChangedHandler listener)
    6.     {
    7.         m_worldApiChangedListeners.Add(listener));
    8.     }
    9.  
    10.     public void RemoveListener(IWorldApiChangedHandler listener)
    11.     {
    12.         m_worldApiChangedListeners.RemoveUnordered(listener));
    13.     }
    14.  
    15.     void RaiseEvent()
    16.     {
    17.         // go backwards in list in case someone removes a listener
    18.         // in its OnWorldManagerApiChanged event handler
    19.         for (var n=listener.Count-1; n>=0; --n)
    20.         {
    21.             var listener = m_worldApiChangedListeners[n];
    22.             if (listener == null)
    23.             {
    24.                 m_worldApiChangedListeners.RemoveAt(n);
    25.                 continue;
    26.             }
    27.      
    28.             listener.OnWorldManagerApiChanged();
    29.         }
    30.     }
    31. }
    32.  
    33. public interface IWorldApiChangedHandler
    34. {
    35.     void OnWorldManagerApiChanged();
    36. }
    I don't claim that this is the better solution. I only want to point out that it caused less issues than using the built-in C# events in our projects.

    Code (CSharp):
    1. if (OnWorldAPIActiveChanged != null)
    2. {
    3.     OnWorldAPIActiveChanged(this, m_worldAPIActive);
    4. }
    In the past, I saw a lot of code that makes a copy of the event prior raising it. Unfortunately I don't remember why, but there was definitely a reason. Perhaps it was related to adding/removing events from a different thread. I remember seeing events used like below instead:

    Code (CSharp):
    1. var tmp = OnWorldAPIActiveChanged;
    2. if (tmp != null)
    3. {
    4.    tmp(this, m_worldAPIActive);
    5. }
    PS: A quick google yielded that it does seem to be related to multi-threading, see here.

    Code (CSharp):
    1. public Vector3 PlayerLocation
    2. {
    I believe it would be preferable if the public API is following Unity's naming convention, just to fit in nicer with Unity.


    CompilerDefinesEditor.cs
    Code (CSharp):
    1. [InitializeOnLoad]
    2. public class CompilerDefinesEditor : Editor
    I don't understand why the class derives from Editor? It seems it does not use anything of 'Editor' and only makes use of the InitializeOnLoad initialization. I believe the class does not need to be public.


    CompilerDefinesRemovalEditor.cs
    Code (CSharp):
    1. public class WorldAPIRemovalEditor : UnityEditor.AssetModificationProcessor
    2. {
    I believe the class does not need to be public.

    Code (CSharp):
    1. public static AssetDeleteResult OnWillDeleteAsset(string AssetPath, RemoveAssetOptions rao)
    2. {
    3.     if (AssetPath.Contains("WorldAPI"))
    4.     {
    5.         string symbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
    6.         if (symbols.Contains(WorldConstants.WAPIPresentSymbol))
    It seems this code leaves the WORLDAPI_PRESENT scripting define behind for platforms other than the selectedBuildTargetGroup.

    • If you switch the current build target to WebGL, CompilerDefinesEditor will inject the WORLDAPI_PRESENT define into WebGL.
    • If you then switch to Standalone, CompilerDefinesEditor will inject the define into Standalone.
    • If you now remove the WorldAPI package, CompilerDefinesRemovalEditor will remove WORLDAPI_PRESENT from the Standalone target only.


    Code (CSharp):
    1. public static AssetDeleteResult OnWillDeleteAsset(string AssetPath, RemoveAssetOptions rao)
    2. {
    3.     if (AssetPath.Contains("WorldAPI"))
    4.     {
    Do you think it's safe to check whether AssetPath contains WorldAPI to decide it's part of the WorldAPI package? As people start using the API, I could imagine they use this in their filenames as well. Perhaps it would be safer to use a hard-coded asset guid of one of the WorldAPI assets, such as WorldManager.cs for example?


    WorldControllerEditor.cs
    The variable 'decimalTime' is assigned but its value is never used
    The private field 'WorldAPI.WorldControllerEditor.m_controller' is assigned but its value is never used


    WorldControllerDemo
    If I select the 'Controller' GameObject in the hierarchy, a new GameObject named 'WorldAPI.WorldManager' is created. The 'WorldAPI.WorldManager' seems to be the singleton 'DontDestroyOnLoad' GameObject, but the issue is it stays in the scene and also gets saved now.

    Is perhaps using hideFlags on the WorldManager GameObject a good idea, from preventing Unity to save the GameObject in the scene?


    That's it. Hope it makes sense. Again, thanks for coming up with WorldAPI! I hope you find the review useful :)
     
    Last edited: Jul 22, 2017
    jbooth, John-G, Whippets and 3 others like this.
  24. Mr-Logan

    Mr-Logan

    Joined:
    Apr 13, 2006
    Posts:
    455
    E.C. Environment Controller (the acronym reads as easy ^^)
     
  25. one_one

    one_one

    Joined:
    May 20, 2013
    Posts:
    621
    Great and informative code review @Peter77

    @AdamGoodrich, you've mentioned integrating this with Enviro. It seems that Enviro essentially covers the same functionality as World Manager by offering out-of-the-box integration with various shaders and systems. So the added benefit of World Manager would be its role as a 'neutral' open-source community project; serving as a central hub for environment management to ease the pain of integration between all these different environment systems and assets. Is that about right?
     
    Last edited: Jul 22, 2017
    Peter77 and Teila like this.
  26. Whippets

    Whippets

    Joined:
    Feb 28, 2013
    Posts:
    1,775
    Great Idea Adam. I will support this in my assets too.
     
  27. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    Yep.

    I already have a bunch of authors who have committed to it, and as soon as I have stabilized it I will send it out to all of the environmental asset authors and ask if they would like to support it as well.

    Then, one day in my dream world development utopia, I can plug and play environmental assets into a scene and have them 'just work'.

    From a general development perspective I could choose to control game time, or have my sky asset control it, or i could choose the weather I want, and then have everything respond as an integrated whole.

    I like the way Hendrik has approached the Enviro integration. For the sections of code he supports he gives you the option to have Enviro control it, or to have WAPI control it. This is awesome because I might have one asset that controls time, and others that control weather etc.

    You could do this individually, or write your own system, but as soon as you change assets, or introduce new ones, it becomes another big integration hassle.

    And by making it light weight, its completely up to asset producer to choose how much they want to implement, and to ensure that their system stays compatible as their capability evolves.
     
    one_one, Mr-Logan and BackwoodsGaming like this.
  28. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    Thanks for the awesome feedback Peter. I will review your review and explore the ideas you have suggested! :)
     
  29. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    I think there is value here for the right audience. It's a generic tool which makes it less valuable to professional developers would be my main comment. These kinds of tools often duplicate stuff we have, use different coding standards and conventions. So I'm always hesitant to use them.

    For instance, I would expect to see an api like this using interfaces or virtual methods at least, so I can provide my own implementations. Right off the bat this kills it for me.

    Time should be abstracted. A large number of professional developers use libraries like NodaTime, not the .NET built in date/time handling (because it sucks). Your implementation for time handling will likely not work for a lot of games where developers know how to handle this correctly. A simple way to handle this is just deal with seconds/milliseconds since the epoch, and an interface/virtual method that can take that and convert it to in game time. With a default that uses DateTime.

    Inconsistent coding styles used. Minor thing but since you are just starting, I'd pick one and adhere to it strictly.

    Using regions is generally considered bad. It enforces bad coding habits. If there is so much code it's easier to read if you hide parts of it, you are doing too much in that class/function.

    Singletons. Don't make the core classes singletons. What if we use dependency injection? I would make a separate simple wrapper singleton for those that need/want it.
     
  30. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    I would also avoid unity events for your own events. Your own events should just be using delegates most likely.
     
  31. Teila

    Teila

    Joined:
    Jan 13, 2013
    Posts:
    6,932
    Sure will be useful to me and many others. :)
     
    Whippets and AdamGoodrich like this.
  32. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    Hey Peter,

    Thanks so much for taking the time to review WAPI. Very much appreciated!

    I put a new version up today - ran out of time and will spend some more time on it tomorrow.

    Major changes include:
    • Changed namespace from WorldAPI to WAPI
    • Tidied up the the singleton code
    • Added a simple serialisation / deserialisation system
    • Added a simple extension system, which includes the ability to handle Update and LateUpdate events
    • Changed the Shader update system so that it is called at most once per frame in LateUpdate. Updates will only be sent if something was changed.
    I took this code out of an open source singleton system. I have now simplified this largely inline with your suggestions.

    Yep - its very much intended to be a singleton.

    Today I sealed the class and added an extension system that allows you to add in your own data and Update / LateUpdate handlers. So it is now quite easy to extend WAPI so that it handles less common use cases.

    I also added some simple serialisation and deserialisation methods to handle getting data into and out of WAPI.

    Now private.

    I like your idea here. I also like the fact that the existing system provides very specific targeted messaging that you can listen to. Wonder if this could somehow be templated to provide the best of both worlds. Ran out of time today to explore further.

    It's an edge case but I like this as well. Went looking to see if there is a way to iterate through all build groups in code. Couldn't find it but sure I have written this before, so will keep looking.

    Now hidden and i removed the load stuff as well. Need to explore the impact of this.

    I changed the namespace to WAPI and this will resolve your immediate issue. The world manager class is still there - because it manages your game world.. but maybe it could be changed as well. Need to stabilise this very quickly as nobody likes refactoring code over and over.

    I am a professional developer and am making and sharing it because it will be useful to me and because it solves the annoying and time consuming problem of integrating assets. Sure a more established studio will have this stuff already, and probably a lot more sophisticated as well, but this is ought to be an 80/20 useful sort of thing and will be useful to many people and that's all I need for the moment.

    This is designed to be simple and generic and not to force people to install other libraries to be able to use it. Agree however that time is painful to handle and want to add some more methods here. I chose .NET DateTime because there is already a wealth of code out there that understands it. Am open to adding any nice time handling routines that you want to contribute.

    Its a singleton controller and is not designed to be derived from. It has very specific data and intentionally limited capability so I am not sure what value adding interfaces will add.

    Its specifically designed to glue different systems together on a common way like say CTS and Tenkoku or Enviro etc, and to allow your game to have a simple and common control API. Love to hear more about your thoughts on this though.

    To address the expansion and serialisation deficiency I did however add a serialisation and extension system with virtual update and lateupdate handlers today, so this will be useful for those people that decide this is a tool they want to use that also need their own custom capabilities.

    Not really sure where you get this from - its pretty consistent as is the general pattern that is repeated over and over in the way it is designed to be used. I refined the singleton code today however as it was borrowed from elsewhere.

    While I don't necessarily disagree with your comment, this class is largely a wrapper over some common data a standard usage pattern and the regions make it more readable. I could abstract it more but i wasn't trying to inventing some super sophisticated system to solve every problem - just a simple command an control system to solve a common one.

    Its a singleton because there should only ever be one in your scene and the usage has to be common or it loses its ability to be generic. The general usage pattern is WorldManager.Instance.API().

    I added extensions and a serialisation approach today - which should address your concern here. Again, I am open to suggestions on how to do this better though.

    As a general comment to the community I am all for people commenting - but when you do - please provide specific examples of how you would make it better as @Peter77 has.
     
    Last edited: Jul 24, 2017
    TeagansDad, tapawafo, Teila and 2 others like this.
  33. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    Doh. Just realized I should have made the extension system an interface instead. Will update this tomorrow.
     
    BackwoodsGaming likes this.
  34. Whippets

    Whippets

    Joined:
    Feb 28, 2013
    Posts:
    1,775
    Here's the additions I would like to put forward to this project.

    PlayerLocation to become PlayerPosition, with a forward direction or heading as well as XYZ.

    PlayerLocation a collection of 4 ints or a V4 to keep things together, being a conglomeration of Country, State/Province, County/Shire, District/Area - as quite often in an RPG/FPS/Survival, the world is split over many areas and subareas, and this would provide the single access point as it's part of the world data. It's also generic enough, giving 4 degrees of locative positioning without implying meaning to any one.

    I would also like to propose changing Moon to CelestialBodies, as there could quite easily be moon than one moon, sun, space-station, or other phased objects in the sky. Making this a List or array would give maximum flexibility.
     
  35. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,589
    Hi Adam,

    I checked the updated code and have a few notes regarding the new extension system.

    WorldManager serializes its extensionList. I imagine this could cause issues, because Unity's serialization system does not support polymorphic data types (and Unity's json implementation therefore too?), which users of WAPI will most likely run into at some point.

    I believe it's safer if extensions don't get serialized with WorldManager. The game code should be responsible to add/remove extensions to WorldManager when needed. It's perhaps also more flexible if the extension manages (load/save) its data.

    WorldManagerDataExtension would perhaps be more flexible if it's an interface. This allows to implement an extension as ScriptableObject for example and makes data management trivial, including the full data inspector for free :)

    Code (CSharp):
    1. /// <summary>
    2. /// Extension data - store whatever you want here - if there is something in here thats not yours
    3. /// then dont touch it, as it will depend on this in some way.
    4. /// </summary>
    5. public List<WorldManagerDataExtension> Extensions
    6. {
    7.     get { return m_extensionList; }
    8.     set { m_extensionList = value; }
    9. }
    I think exposing the list can be dangerous, as different WAPI packages/users now can mess with other things. And will do, as your command already points to. For example, assume I provide PackA and it clears the Extensions list. Now PackB, that you provide, is broken due to PackA messing with the m_extensionList internals.

    I imagine it could be safer to hide the internal list and introduce add/remove methods instead:
    Code (CSharp):
    1. public void AddExtension(WorldManagerDataExtension extension);
    2. public void RemoveExtension(WorldManagerDataExtension extension);
    This now also allows to raise an event when an extension is added and removed, in case anything needs to respond to it.

    However, I'm not really sure what problem you would like to resolve with having extensions. Could you perhaps give me an example what you would implement as an extension? I'm afraid having the extension opens up a lot of things that are needed then, for example not only Update and LateUpdate, but also OnEnable/OnDisable and so on. Therefore I would like to understand what a real-world extension case would look like :)


    Another approach I used that maybe comes close to combine both worlds is to provide an interface with a "OnWorldChange" event. The implementation of that method filters in which messages it's interested then.

    I'm not claiming it's the better solution, just bouncing ideas.

    Code (CSharp):
    1. // it's not an enum, because we want to support more than 32 bits
    2. public static class WorldChangeEvents
    3. {
    4.     public const UInt64 WindDirection = 1<<0;
    5.     public const UInt64 WindSpeed = 1<<1;
    6.     public const UInt64 HailPower = 1<<41;
    7. }
    8.  
    9. // this isn't really neccessary, we could just pass 'changeMask' directly
    10. public struct WorldChangeArgs
    11. {
    12.     public UInt64 changeMask;
    13.     public WorldManager manager;
    14. }
    15.  
    16. public class WorldManager
    17. {
    18. public float HailPower
    19. {
    20.     get { return m_haildata.x; }
    21.     set
    22.     {
    23.         if (m_haildata.x != value)
    24.         {
    25.             m_haildata.x = value;
    26.             m_wasUpdated = true;
    27.             Send(WorldChangeEvents.HailPower);
    28.         }
    29.     }
    30. }
    31.  
    32. void Send(UInt64 changeMask)
    33. {
    34.     var args = new WorldChangeArgs();
    35.     args.changeMask = changeMask;
    36.     args.manager = this;
    37.  
    38.     for(int n=m_listeners.Count-1; n>=0; --n)
    39.     {
    40.         var listener = m_listeners[n];
    41.         listener.OnWorldChange(args);
    42.     }
    43. }
    44. }
    At the moment, WorldManager passes the new value to the callback method. However, I guess this isn't necessary, because you can also just get this new value from WorldManager directly.

    Below you can also find an approach that allows to send multiple change events in one go, once per frame only. For example, WorldManager could now also easily defer its change notifications and easily implement per-change shader updates. It might also make the property implementations leaner.

    Code (CSharp):
    1. class WorldManager
    2. {
    3.     UInt64 m_changeMask = 0;
    4.  
    5.     public float HailPower
    6.     {
    7.         get { return m_haildata.x; }
    8.         set
    9.         {
    10.             if (m_haildata.x != value)
    11.             {
    12.                 m_haildata.x = value;
    13.                 m_changeMask |= WorldChangeEvents.HailPower;
    14.             }
    15.         }
    16.     }
    17.  
    18.     void LateUpdate()
    19.     {
    20.         // Update only shader globals which changed
    21.         if ((m_changeMask & (WorldChangeEvents.WindDirection | WorldChangeEvents.WindSpeed)) != 0)
    22.             Shader.SetGlobalVector("_WAPI_Wind", m_windData); // TODO: replace "_WAPI_Wind" with integer id
    23.  
    24.         if ((m_changeMask & WorldChangeEvents.HailPower) != 0)
    25.             Shader.SetGlobalVector("_WAPI_Hail", m_haildata);
    26.  
    27.         // Send all changes in go
    28.         if (m_changeMask != 0)
    29.         {
    30.             Send(m_changeMask);
    31.             m_changeMask = 0;
    32.         }
    33.     }
    34. }
    35.  
    36. public interface IWorldManagerChangeHandler
    37. {
    38.     void OnWorldChange(WorldChangeArgs args);
    39. }
    An example implementation of how to use this could be like this:
    Code (CSharp):
    1. class Forest : MonoBehaviour, IWorldManagerChangeHandler
    2. {
    3.     void OnEnable()
    4.     {
    5.         // Add requires a IWorldManagerChangeHandler type
    6.         WorldManager.Add(this);
    7.     }
    8.  
    9.     void OnDisable()
    10.     {
    11.         WorldManager.Remove(this);
    12.     }
    13.  
    14.     IWorldManagerChangeHandler.void OnWorldChange(WorldChangeArgs args)
    15.     //public void OnWorldChange(WorldChangeArgs args)
    16.     {
    17.         if ((args.changeMask & WorldChangeEvents.WindDirection) != 0)
    18.         {
    19.            Do something with WorldManager.windDirection or args.manager.WindDirection
    20.         }
    21.     }
    22. }
    Having WorldChangeArgs also allows to add helpers to make working with bit masks easier, for example:
    Code (CSharp):
    1. public struct WorldChangeArgs
    2. {
    3.     public UInt64 changeMask;
    4.     public WorldManager manager;
    5.  
    6.     public bool HasChanged(UInt64 changes)
    7.     {
    8.         return ((changes & changeMask) != 0);
    9.     }
    10. }
    11.  
    12. if (args.HasChanged(WorldChangeEvents.WindDirection))
    13. // Do something here
    or turn it into a property for even simpler access:
    Code (CSharp):
    1. public struct WorldChangeArgs
    2. {
    3.     public UInt64 changeMask;
    4.     public WorldManager manager;
    5.  
    6.     public bool windDirectionChanged
    7.     {
    8.         get
    9.         {
    10.             return (changeMask & WorldChangeEvents.WindDirection) != 0;
    11.         }
    12.     }
    13. }
    14. if (args.windDirectionChanged)
    15. // Do something here
    Having control over what listener is notified when, also provides the benefit to be able to respond to an exception that a listener might have caused and just disable this one, like:
    Code (CSharp):
    1. void Send(UInt64 changeMask)
    2. {
    3.     var args = new WorldChangeArgs();
    4.     args.changeMask = changeMask;
    5.     args.manager = this;
    6.     for(int n=m_listeners.Count-1; n>=0; --n)
    7.     {
    8.         var listener = m_listeners[n];
    9.        
    10.         try
    11.         {
    12.             listener.OnWorldChange(args);
    13.         }
    14.         catch (System.Exception e)
    15.         {
    16.             Debug.LogException(e, this);
    17.            
    18.             // remove the listener that causes the exception
    19.             // so remaining stuff continues to work
    20.             m_listeners.RemoveAt(n);
    21.             Debug.LogError("Removed listener because it caused an error.");
    22.         }
    23.     }
    24. }
    Hope it makes sense :)
     
    Last edited: Jul 25, 2017
    AdamGoodrich likes this.
  36. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    Hey Peter - Awesome suggestions.

    Am in the process if implementing a bunch of them - its quite time intensive and I have no shortage of work on my plate already. I will update thread when done.
     
    Peter77 and Mr-Logan like this.
  37. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    I should be putting an update to this later today. Been super busy on other stuff.
     
    AndyNeoman and one_one like this.
  38. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    And the first update is up. Now to test it by creating a CTS integration :)
     
  39. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    New update online.

    Added change api, simplified the whole event handling thing.
    Added simple keyboard control for time -> [ and ] controls it.

    Tested with integration between CTS and Enviro - works quite nicely.
     
    TeagansDad and one_one like this.
  40. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    And here we have something very cool!

    World manager now has Timeline / Playables API support in Unity 2017.x, as well as integration with CTS and Enviro. Many thanks to John Sietsma, Evangelist, Unity Australia for his prodding and support to get this going.

    As soon as it is a little bit more robust I will add it to the general world manager package!

    Assets used: Gaia, CTS, AQUAS, Enviro, Fog Volume 3 and World Manager as the middleware glue.

    World Manager Playables 2017-08-22_23-03-22.jpg

     
    Last edited: Aug 22, 2017
  41. AndyNeoman

    AndyNeoman

    Joined:
    Sep 28, 2014
    Posts:
    938
    Do you take requests Adam lol. I will get around to integrating with Unistorm and Uber but have just found an annoying bug in my game.

    Looks great, I cant wait to have a play around with it.
     
  42. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    And with some fast love from Hendrik / Enviro, here we have Enviro weather being driven by WAPI / Timeline!

     
  43. sharkapps

    sharkapps

    Joined:
    Apr 4, 2016
    Posts:
    145
    Hi Adam, the WAPI and Timeline videos are an excellent showcase of putting WAPI to work. Is it possible to get snow coverage on foliage (trees/grass) somehow? Best regards, Tony
     
  44. RobsonFMaciel

    RobsonFMaciel

    Joined:
    Jan 12, 2013
    Posts:
    187
    I would also like to know about :)
     
  45. one_one

    one_one

    Joined:
    May 20, 2013
    Posts:
    621
    Probably by having an additional snow-covered texture. To make it gradual, you'd need a shader that lerps it with the regular texture.
     
  46. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    Ok - here it all is - the blog post where I will be putting most of the information on WAPI - and the latest Timeline integration is now available in the repository!

    http://www.procedural-worlds.com/blog/wapi/

    WAPI is just an API - if the snow asset supports WAPI, then you will be able to control it as well. It is very easy to set up - took me only a few hours - so if you have an asset in mind send the author to me and we will get it going.
     
    BackwoodsGaming and AndyNeoman like this.
  47. AndyNeoman

    AndyNeoman

    Joined:
    Sep 28, 2014
    Posts:
    938
    I have mentioned it in the forums of assets I use (unistorm,Unature,AFS5,RTP)
     
    AdamGoodrich likes this.
  48. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    Thanks. I am slammed with work at moment, but will also be reaching out to other asset producers as well :)
     
  49. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,780
    Couple of videos showing WAPI at work :





    Support is coming from CTS, Enviro and now confirmed on the way for Tenkoku as well.
     
  50. JayCrossler

    JayCrossler

    Joined:
    Jul 10, 2013
    Posts:
    10
    I love this and how it looks. I have GAIA, GeNa, Aquas, Enviro and am putting them together for a game with many other components - and want to have a day/time feature to go through years of evolution. I'm considering getting CTS. I watched the HowTo video, but am still missing some basic steps. Would it be possible for you to do a basic video/blog post showing how to start from the basics (load WM in from GitHub zip file, load GAIA... and other assets, then... what? Is the next step to add code from the demo or to add the timeline and if so how?) . Thanks in advance!
     
    AdamGoodrich likes this.