Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

ConMan - The GUI based solution for game config variable management.

Discussion in 'Made With Unity' started by KyleStaves, Jun 5, 2011.

  1. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    Hello Everyone,

    I've been working recently on a small piece of middware I call "ConMan" (short for ConstantManager). Like many of you, I've frequently used XML to store game configuration data that I load at runtime (from the Resources folder) to separate the design-side data from the script-side implementation of those variables. I finally decided I wanted to write a more robust, extensible, in-editor script for handling this (as opposed to hand editing XML - and maintaining several versions specific to individual projects to support special data types).

    What is ConMan?
    ConMan is a GUI-based method for creating configuration variables saved to XML and loaded into the game for use in any of your scripts. The purpose of this is separating the design work from the script implementation.

    How does it work?
    Easy, you simply open the ConMan editor window - add/edit/delete variables as needed. Whenever you make an update, the XML is automatically updated as well (and the assets are refreshed as needed to ensure Unity is always up to date on the changes). You can access these variables with static functions like:

    Code (csharp):
    1.  
    2. ConMan.GetString("stringId");
    3. ConMan.GetString("stringId", "default value"); // For when you aren't sure whether a value exists and you have a value you want it to default to if it does not exist. Otherwise it will return default(type) if missing.
    4.  
    5. ConMan.GetFloat("id");
    6. ConMan.GetColor('id");
    7.  
    Can I add support for my own data-types?
    Why yes, yes you can. ConMan was designed specifically with extensibility in mind. I toyed around with a few solutions before landing on my current implementation. Basically, for every data type you will add a new wrapper class that extends ConManElement and has a few specific delegates in it (for editor support, serialization and deserialization).

    In addition, you'll need functions for actually accessing your custom data types. There are three ways you can add these functions.

    1: Edit ConMan.cs and add them manually. This is the cleanest, as it allows you to use ConMan.GetYourDatatype() - however, it is requires you maintain it after possible version updates.

    2: C# Extension Methods. Sadly, C# does not support static extension methods. Instead, we need to add these methods to an instance of ConMan. An empty instance is already in the code (but not instantiated if not used. With this you can add your own methods in the following format: ConMan.Instance.GetDatatype("") - without having to edit ConMan itself.

    3: Wrapper Class. You can also maintain your own wrapper class for ConMan that simply adds your own static functions and access all the values through your wrapper class. (for example KyleConstants.GetDatatype). This has the advantage of not requiring separate maintainable from the main class, but also not requiring the extra .Instance. as the extension methods.

    I will provide examples for all three of these methods with the initial release of ConMan.

    Why are you posting this before releasing ConMan?
    Basically I wanted to show the tool pretty early on to get some feedback from the community in regards to what they would like to see added/supported and any GUI changes that may be proposed before putting much additional work into the project. We see threads pop up on a fairly regular basis asking about how to handle this, so I figured the community could use a well maintained, universal, GUI-driven solution.

    Any feedback/questions/suggestions are encouraged!

    Feel free to check out this quick demo video that shows the tool and how to use it. The GUI is definitely a bit on the ugly-side still, I'm working on making it more attractive before I finish adding the basic data types and push the first version out for testing.

    http://www.youtube.com/watch?v=-uImXttBXuA
     
  2. psyclone

    psyclone

    Joined:
    Nov 17, 2009
    Posts:
    245
    Sounds like something that could be very useful... Will be watching this closely.
     
  3. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    Hey guys, just wanted to toss this list out there and see if anyone had requests. This is the currently planned supported data types for the initial release.
    • string
    • bool
    • float
    • int
    • Color
    • Vector2
    • Vector3
    • Vector4
    • Quaternion (will have 2 value rows, one for the true quaternion and one for euler - will be stored as a quaternion with both)

    Let me know if there's any other types you would like to see supported out of the gate!
     
  4. 2dfxman

    2dfxman

    Joined:
    Oct 1, 2010
    Posts:
    178
    The comment section needs to be multiline. Expandable/shrinkable if possible.
     
  5. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    Thanks for the feedback 2d. Chances are I will keep the comment to a single line; we like to enforce short, succinct comments in our XML to maintain readability (in the XML). However, I may add a multi-line comment section to the categories themselves for more verbose explanations of how whatever that category applies to functions.

    All the listed data-types are now supported. I am about ready for a few beta-testers if anyone is interested in an early look at the tool.
     
  6. 2dfxman

    2dfxman

    Joined:
    Oct 1, 2010
    Posts:
    178
    Generally enforcing stuff in stuff you sell is bad.
    Also how does longer comments change xml readability?
     
  7. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    Chances are this tool will be freely available to those interested, or very inexpensive. I don't see it being worth more than $5-$10 when finished though, and honestly - I can't imagine it would sell enough copies to impact my financial situation in the slightest, so free is likely the route I'll take.

    Still though, I'm not really enforcing anything, I'm just choosing to go with a more compact GUI - I don't actually limit the length of the string at all (beyond the limits of the datatype itself). If there were a clean way to have a TextArea automatically scale vertically to fit the size of the contents I wouldn't hesitate to add support. I'll probably add a const variable to configure the displayed lines though - but it would likely be global.

    (my "enforcing" comment was specific to my own teams anyway though - as this tool was written for personal use and just happens to be going public)

    Code (csharp):
    1.  
    2. <ConMan>
    3.  
    4.     <ConManCategory id="Cameras">
    5.  
    6.         <ConManCategory id="Player">
    7.  
    8.             <float id="ZoomSpeed" value="25" comment="This variable changes the rate at which the mouse-wheel zooms the camera in and out. Example: currentDistance = currentDistance + Input.GetAxis("Mousewheel") * Time.deltaTime * ZoomSpeed" />
    9.  
    10.         </ConManCategory>
    11.  
    12.     </ConManCategory>
    13.  
    14. </ConMan>
    15.  
    Here is an example of a comment which takes up about two lines with the editor window set pretty small. In a file with potentially hundreds of nodes, it quickly becomes the bulk of the file.

    Honestly though, the GUI is in a huge state of flux and likely will end up looking very different before I upload this to the wiki/asset store anyway. As it's a feature I don't forsee my own teams using at all (verbose comments) its pretty low on my priority list at the moment. Sorry.
     
  8. PhilliamApps

    PhilliamApps

    Joined:
    Mar 13, 2011
    Posts:
    64
    Hey this looks awesome! Release date? :D
     
  9. namoricoo

    namoricoo

    Joined:
    Apr 14, 2011
    Posts:
    534
    Looks like you've been watching My UniDDatabase Videos and you are trying to clone my product. Or Maybe you saw my UniSqlite Videos dating back to over a month ago. http://forum.unity3d.com/threads/91826-UniDDatabase-2.0.
    Your tool will allow people to manage constants. Lol.

    There's only one tool that will allow developers to manage Prefabs,audio,textures,String, float,int,position,Rotation, etc, with as little as one line of code. That's UniDDatbase. While you keep looking for ways to clone the my wisdom. People will be able to pick up a copy of UniDDatbase this week in the asset store. So ConMan, I just hope you don't violate any of my copyrights.
     
  10. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    I just checked out your product and I can assure you they are very different products that server different purposes entirely. Your product does look very interesting, and is significantly more robust than mine.

    ConMan is something that literally every development house has in one form or another - a quick way to get variable data from XML. The only difference between ConMan and the tool I wrote over a year ago for in-house use (ConstantManager) is that ConMan is type safe and has a GUI frontend.

    Just to give you an entire of how vastly different our tools are - here is the entire sourcecode to the first version of ConstantManager that I wrote over a year ago:

    Code (csharp):
    1.  
    2.  
    3. using UnityEngine;
    4.  
    5. using System.Collections;
    6.  
    7. using System.Collections.Generic;
    8.  
    9. using System.Xml;
    10.  
    11. using System.Xml.Serialization;
    12.  
    13. using System.IO;
    14.  
    15.  
    16.  
    17. public class ConstantManager {
    18.  
    19.  
    20.  
    21.     static ConstantManager instance = null;
    22.  
    23.  
    24.  
    25.     private Dictionary<string, string> _constants = new Dictionary<string, string>();
    26.  
    27.  
    28.  
    29.     ConstantManager()
    30.  
    31.     {
    32.  
    33.         LoadConstants();
    34.  
    35.     }
    36.  
    37.  
    38.  
    39.     public static ConstantManager Instance
    40.  
    41.     {
    42.  
    43.         get
    44.  
    45.         {
    46.  
    47.             if (instance == null)
    48.  
    49.             {
    50.  
    51.                 instance = new ConstantManager();
    52.  
    53.             }
    54.  
    55.             return instance;
    56.  
    57.         }
    58.  
    59.     }
    60.  
    61.    
    62.  
    63.     public void ForceDataReload(){
    64.  
    65.         _constants = new Dictionary<string, string>();
    66.  
    67.         LoadConstants();
    68.  
    69.     }
    70.  
    71.  
    72.  
    73.     void LoadConstants()
    74.  
    75.     {
    76.  
    77. //      List<TextAsset> constantFiles = new List<TextAsset>();
    78.  
    79. //      constantFiles.Add(Resources.Load("Data/GameConstants") as TextAsset);
    80.  
    81. //      constantFiles.Add(Resources.Load("Data/HuntingConstants") as TextAsset);
    82.  
    83. //      constantFiles.Add(Resources.Load("Data/ItemConstants") as TextAsset);
    84.  
    85. //      constantFiles.Add(Resources.Load("Data/GUIConstants") as TextAsset);
    86.  
    87.        
    88.  
    89.         Object[] constantFiles = (Object[])Resources.LoadAll("Data/Constants", typeof(TextAsset));
    90.  
    91.        
    92.  
    93.         foreach (Object co in constantFiles){
    94.  
    95.             if (co is TextAsset){
    96.  
    97.                 TextAsset currentAsset = co as TextAsset;
    98.  
    99.                
    100.  
    101.                 XmlDocument doc = new XmlDocument();
    102.  
    103.                 doc.LoadXml(currentAsset.text);
    104.  
    105.        
    106.  
    107.                 foreach (XmlNode nodeXML in doc.ChildNodes)
    108.  
    109.                 {
    110.  
    111.                     foreach (XmlNode childNode in nodeXML.ChildNodes)
    112.  
    113.                     {
    114.  
    115.                         if (childNode.Name != "#comment"){
    116.  
    117.                             if (childNode.Attributes["id"] != null){
    118.  
    119.                                 ParseSpecial(childNode);
    120.  
    121.                             } else {
    122.  
    123.                                 ParseGeneric(childNode);
    124.  
    125.                             }
    126.  
    127.                         }
    128.  
    129.                     }
    130.  
    131.                 }
    132.  
    133.             }
    134.  
    135.         }
    136.  
    137.     }
    138.  
    139.    
    140.  
    141.     void ParseAttribute(XmlAttribute attribute, string constantPrefix){
    142.  
    143.         if (!_constants.ContainsKey(constantPrefix + attribute.Name)){
    144.  
    145.             _constants.Add (constantPrefix + attribute.Name, attribute.Value);
    146.  
    147.         }
    148.  
    149.     }
    150.  
    151.    
    152.  
    153.     void ParseSpecial(XmlNode specialNode){
    154.  
    155.         string id = specialNode.Attributes.GetNamedItem("id").Value;
    156.  
    157.        
    158.  
    159.         foreach (XmlAttribute currentAttribute in specialNode.Attributes){
    160.  
    161.             if (currentAttribute.Name != "id"){
    162.  
    163.                 ParseAttribute(currentAttribute, id);
    164.  
    165.             }
    166.  
    167.         }
    168.  
    169.     }
    170.  
    171.    
    172.  
    173.     void ParseGeneric(XmlNode genericNode){
    174.  
    175.         if (genericNode.ChildNodes.Count > 0){
    176.  
    177.             foreach (XmlNode childNode in genericNode.ChildNodes){
    178.  
    179.                 if (childNode.Name != "#comment"){
    180.  
    181.                     string variableName = genericNode.Name.ToString() + childNode.Name.ToString();
    182.  
    183.                     if (!_constants.ContainsKey(variableName)){
    184.  
    185.                         _constants.Add (variableName, childNode.Attributes.GetNamedItem ("val").Value.ToString ());
    186.  
    187.                         CheckSpecialCases(childNode.Name.ToString(), childNode.Attributes.GetNamedItem ("val").Value.ToString ());
    188.  
    189.                     } else {
    190.  
    191.                         Debug.LogError ("ConstantManager already contains key: " + genericNode.Name);
    192.  
    193.                     }
    194.  
    195.                 }
    196.  
    197.             }
    198.  
    199.         } else {
    200.  
    201.             if (!_constants.ContainsKey (genericNode.Name.ToString ())) {
    202.  
    203.                 _constants.Add (genericNode.Name.ToString (), genericNode.Attributes.GetNamedItem ("val").Value.ToString ());
    204.  
    205.                 CheckSpecialCases (genericNode.Name.ToString (), genericNode.Attributes.GetNamedItem ("val").Value.ToString ());
    206.  
    207.             } else {
    208.  
    209.                 Debug.LogError ("ConstantManager already contains key: " + genericNode.Name);
    210.  
    211.             }
    212.  
    213.         }
    214.  
    215.     }
    216.  
    217.    
    218.  
    219.     void CheckSpecialCases(string Key, string Value){
    220.  
    221.         // Weapon Manager Cases
    222.  
    223.         if (Key == "HearingMultiplier"){
    224.  
    225.             if (_constants.ContainsKey("BestHearingMultiplier")){
    226.  
    227.                 if (float.Parse(Value) > float.Parse(_constants["BestHearingMultiplier"])){
    228.  
    229.                     _constants["BestHearingMultiplier"] = Value;
    230.  
    231.                 }
    232.  
    233.             } else {
    234.  
    235.                 _constants.Add("BestHearingMultiplier", Value);
    236.  
    237.             }
    238.  
    239.         }
    240.  
    241.     }
    242.  
    243.  
    244.  
    245.     public float GetFloat(string variableName)
    246.  
    247.     {
    248.  
    249.         if (_constants.ContainsKey(variableName))
    250.  
    251.         {
    252.  
    253.             //return float.TryParse(_constants[variableName]);
    254.  
    255.             //Debug.Log(_constants[variableName].ToString());
    256.  
    257.             return float.Parse(_constants[variableName]);
    258.  
    259.         }
    260.  
    261.  
    262.  
    263.         Debug.LogError(variableName + " not found!");
    264.  
    265.         return 0;
    266.  
    267.     }
    268.  
    269.    
    270.  
    271.     public float GetFloatFromRange(string variableName){
    272.  
    273.         if (_constants.ContainsKey(variableName)){
    274.  
    275.             string[] splitRange = _constants[variableName].Split(',');
    276.  
    277.            
    278.  
    279.             if (splitRange.Length == 2){
    280.  
    281.                 return Random.Range(float.Parse(splitRange[0]), float.Parse(splitRange[1]));
    282.  
    283.             } else {
    284.  
    285.            
    286.  
    287.             }
    288.  
    289.         }
    290.  
    291.        
    292.  
    293.         Debug.LogError(variableName + " not found!");
    294.  
    295.         return 0;
    296.  
    297.     }
    298.  
    299.    
    300.  
    301.     public int GetInt(string variableName)
    302.  
    303.     {
    304.  
    305.         if (_constants.ContainsKey(variableName))
    306.  
    307.         {
    308.  
    309.             //return float.TryParse(_constants[variableName]);
    310.  
    311.             //Debug.Log(_constants[variableName].ToString());
    312.  
    313.             return int.Parse(_constants[variableName]);
    314.  
    315.         }
    316.  
    317.  
    318.  
    319.         Debug.LogError(variableName + " not found!");
    320.  
    321.         return 0;
    322.  
    323.     }
    324.  
    325.    
    326.  
    327.     public int GetIntFromRange(string variableName){
    328.  
    329.         if (_constants.ContainsKey(variableName)){
    330.  
    331.             string[] splitRange = _constants[variableName].Split(',');
    332.  
    333.            
    334.  
    335.             if (splitRange.Length == 2){
    336.  
    337.                 return Random.Range(int.Parse(splitRange[0]), int.Parse(splitRange[1]) + 1);
    338.  
    339.             }
    340.  
    341.         }
    342.  
    343.        
    344.  
    345.         Debug.LogError(variableName + " not found!");
    346.  
    347.         return 0;
    348.  
    349.     }
    350.  
    351.    
    352.  
    353.     public IntPair GetIntPair(string variableName){
    354.  
    355.         if (_constants.ContainsKey(variableName)){
    356.  
    357.             string[] splitPair = _constants[variableName].Split(',');
    358.  
    359.            
    360.  
    361.             if (splitPair.Length == 2){
    362.  
    363.                 return new IntPair(int.Parse(splitPair[0]), int.Parse(splitPair[1]));
    364.  
    365.             }
    366.  
    367.         }
    368.  
    369.        
    370.  
    371.         Debug.LogError(variableName + " not found!");
    372.  
    373.         return new IntPair(0,0);
    374.  
    375.     }
    376.  
    377.    
    378.  
    379.     public Color GetColor(string variableName)
    380.  
    381.     {
    382.  
    383.         if (_constants.ContainsKey(variableName)){
    384.  
    385.             string[] splitColor = _constants[variableName].Split(',');
    386.  
    387.            
    388.  
    389.             if (splitColor.Length == 4){
    390.  
    391.                 return new Color(float.Parse(splitColor[0]), float.Parse(splitColor[1]), float.Parse(splitColor[2]), float.Parse(splitColor[3]));
    392.  
    393.             } else {
    394.  
    395.                 Debug.LogError(variableName + " is not a color!");
    396.  
    397.             }
    398.  
    399.         } else {
    400.  
    401.             Debug.LogError(variableName + " not found!");
    402.  
    403.         }
    404.  
    405.        
    406.  
    407.         return Color.white;
    408.  
    409.     }
    410.  
    411.  
    412.  
    413.     public bool GetBool(string variableName)
    414.  
    415.     {
    416.  
    417.         if (_constants.ContainsKey(variableName))
    418.  
    419.         {
    420.  
    421.             if (_constants[variableName] == "true")
    422.  
    423.             {
    424.  
    425.                 return true;
    426.  
    427.             }
    428.  
    429.             else if (_constants[variableName] == "false")
    430.  
    431.             {
    432.  
    433.                 return false;
    434.  
    435.             }
    436.  
    437.             else {
    438.  
    439.                 Debug.LogWarning(variableName + " is not a bool.");
    440.  
    441.             }
    442.  
    443.            
    444.  
    445.         }
    446.  
    447.  
    448.  
    449.         Debug.LogError(variableName + " not found!");
    450.  
    451.         return true;
    452.  
    453.     }
    454.  
    455.  
    456.  
    457.     public string GetString(string variableName)
    458.  
    459.     {
    460.  
    461.         if (_constants.ContainsKey(variableName))
    462.  
    463.         {
    464.  
    465.             return _constants[variableName];
    466.  
    467.         }
    468.  
    469.  
    470.  
    471.         Debug.LogError(variableName + " not found!");
    472.  
    473.         return null;
    474.  
    475.     }
    476.  
    477. }
    478.  
    479.  
    I can assure you that the concept of "separating the data from the implementation" is by no means unique, and storing this data in XML is technique that virtually every development house working with Unity already uses.

    But hey, if it makes you feel good to accuse me of stealing your very, very different product that was posted 3 days ago - feel free. Anyone with an ounce of experience in the industry will immediately identify the vast differences between the two.

    Looks like you have a very interesting product though!
     
  11. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    Just for the record:
    http://forum.unity3d.com/threads/89...omatically-clears-when-hitting-quot-play-quot
    Here is my post from three weeks ago discussing how to avoid data clearing when hitting play, for this tool.

    http://stackoverflow.com/questions/...riable-types-to-a-dictionary-management-class
    There is my related stack overflow question, where you can even see some pseudo code from the project.

    http://forum.unity3d.com/threads/67332-How-do-you-store-game-data-(damage-spell-data-etc)
    Here is a thread where I discuss ConstantManager (the tool that is a non-typesafe, non gui-based version of ConMan) back in 2010.


    If you want to accuse me of trying to "clone your wisdom" you may want to at least ensure that our products are designed to do the same thing though. Really, I find it a little alarming that you are even capable of writing UniDDatabase but are incapable of seeing the vast differences in our product - or unaware that a huge portion of the Unity and Flash gaming world has been using XML to store game data for a very, very long time (in fact, many portal sites require you to include an XML configuration file).

    But hey, if you wish to act like a tried and honestly pretend there is only one tool (as opposed to thousands of separate in-house solutions) that allows quickly extracting a string/int/float from XML feel free. You are only making yourself look like an inexperienced child.

    For those interested, the alpha version of ConMan will be posted in a few minutes.
     
  12. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    ConMan Alpha 1.0:

    Feel free to ask any questions, or make any suggestions! I know the GUI still needs a lot of work.

    EDIT:
    Removing the download links to ensure everyone uses the latest version (Assets thread).
     
    Last edited: Jun 6, 2011
  13. namoricoo

    namoricoo

    Joined:
    Apr 14, 2011
    Posts:
    534
    My UniSqlite post date back to almost two month ago. Your quote does say everything. There's room for healthy competition here. Brother KyleStaves, I take back what I said. you can remove your code here so vultures don't get there grubby hands on it. I wish you luck. I hope we both sell 20,000 copies and make a lot of money. Maybe one day we'll look back and laugh at this. LOL.
     
    Last edited: Jun 6, 2011
  14. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    If you even bothered to read my thread before accusing me of potential copyright violation, and "stealing your wisdom" you would see that my current plan is to release the tool for free.

    I really do not see our tools as in-competition though. From what I understand your tool uses SQL, correct? Where as mine uses the industry standard XML approach? Your tool allows the loading of entire assets? Mine is simply to load configuration variables (a technique of data separation that has been used in the industry for many, many years).

    To be honest, the only similarity I can find between our two products is that they both - in their own way - separate data location from data implementation. If that were a able to be copyrighted, as a concept, we would both be in trouble as it's a concept that has been used in virtually every industry that employs computer programming for many, many years.

    I've posted the original ConstantManager code all over the place (including the old Unity forums), I'm not worried about anyone getting their "grubby hands" on it - it is my gift to an incredible community. The new version is much better though (if for no reason other than it is type-safe, so no runtime casting from string to float) - I suggest they use that version instead, also free of charge.

    I do hope your product is a success though. Over the years I've got to work with a lot of other middleware developers for Unity and it's always been a pleasure, Brady (EzGUI/SM) and Bob Berkebile (iTween) in particular are always a pleasure, and always quick to help out with any questions/concerns. Sadly, my only interaction with you thus far has been this incredibly childish display showing a grave lack of understanding of widely used approaches to data separation.
     
  15. namoricoo

    namoricoo

    Joined:
    Apr 14, 2011
    Posts:
    534
    I would not have any problems. See this link for i4i vs Microsoft for patent infringement with XML.

    http://en.swpat.org/wiki/I4i_v._Microsoft_(2009,_USA)

    We both little guys. So those fat cats would not come after us. in UniDDatbase I use a solution that I invented. It's based very loosely on XML,HTML,CSS,Sqlite, etc. However, 90% of the technology is my original work over a 6 month period. You're one of the good guys. There are a lot of vultures out there who just sit around looking for ideas to steal. "Microsoft" is a good example of a vulture. They're always looking for stuff to steal.
     
  16. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    I think you are still missing the point; you cannot patent an idea, you can patent a specific implementation and method of using that idea though. I've read through the patent (available here http://www.i4ilp.com/patent.php) and I fail to see how it has anything to do with either of our products. Microsoft used the same, specific, implementation of a concept as i4i - which i4i had a patent on. Open Office employed the exact same concept in their products, but because their implementation of that concept was different they were not in violation of the patent. Again, exact same idea - different implementation.

    It has nothing to do with "fat cats" not bothering to come after us and everything to do with our products not being in violation of any patents (that I'm aware of anyway - certainly not the particular patent related to that case).

    Anyway, borrowing ideas is absolutely the crux of the gaming industry - genres are evolutionary, not revolutionary. One only has to look at the Fruit Ninja vs Veggie Samurai debate to see how people commonly misunderstand the differences between what is "rude" and what is grounds for a civil suit. Fans of Fruit Ninja were up in arms when VS was first released because of how blatantly similar it is to FN. However, in Phil Larson's own words:

    But again, I really - really fail to see how this discussion has anything to do with ConMan. ConMan is an obvious evolution to the heavily used paradigm of putting game variables in XML to be loaded by scripts at runtime. I am absolutely confident that there are many in-house solutions that do the exact same thing as ConMan being employed in Unity already, GUI and all. Every commercial Flash game I've worked on has used this paradigm. Every Unity game I've worked on has used this paradigm. If someone else on the forums came up with a more elegant solution to the problem ConMan is attempting to address (gui editing of the XML and type-safe access at runtime while being easily extended to support new data types) I wouldn't come accuse them of "copyright violation" without any idea what that term even means. I would be excited, I wrote ConstantManager over a year ago for personal use. I have a similar in-house solution for flash games. If someone can come along and do a better job than me I'd love to see it, and use it myself.

    ConMan has mostly just been an educational exercise, I learned quite a bit about reflection and building extensible systems while writing this - and certainly quite a bit about C# as a language. I'm absolutely positive there is a better way to approach this problem, but for now - ConMan works and is blazingly fast (in my profiler tests it doesn't appear any slower than loading values from a member dictionary of the same type after the initial XML loading), so I'm happy.

    If you treat everyone who has a remotely similar idea as a "vulture" in this industry, you are not going to get very far - and you are going to burn a lot of potential valuable bridges over the years.
     
  17. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    ConMan Alpha 1.01 Changelog:
    • Added ConMan.GetVector2 methods
    • Added ConMan.GetVector3 methods
    • Added ConMan.GetVector4 methods
    • Added ConMan.GetQuaternion methods

    Sorry about that folks, added the editor/serialization/deserialization support for the above methods, but forgot to add static methods for actually retrieving those values.

    EDIT:
    Removing download links from this thread to ensure people use the latest release from the Assets thread.
     
    Last edited: Jun 6, 2011
  18. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
  19. GennadiyKorol

    GennadiyKorol

    Joined:
    Jul 10, 2010
    Posts:
    107
    KyleStaves:

    Looks like a very nice tool. The only question I would ask is, how about a Json version :)?

    namoricoo:

    Hold your horses. Copy right infringement? There are no copy rights on any development work of this kind and I am sure you haven't patented any of it. So please take your time before water-gun-threatening people spending time to create tools for our community.
    Unity tools are definitely not going to pay your pay checks (for most of us and as of today) so we should appreciate and love those spending time to contribute and help others.

    Secondly, your products are vastly different, there's no case for even a remote comparison.

    P.S. On your side remark about ideas. Ideas are not worth much. At all. It's the ability to take the idea, iterate upon it and bring it to that final polish level. Having an idea will not get you very far. Being able to execute on that idea with excellence will.
     
  20. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    Hello GennadiyKorol,

    Json is definitely something I've considered in the past. If I can find an easy way to encode/decode it I'll definitely add that as an option down the line. Adding the suggestion to the Assets thread! As far as I know the JavaScriptSerializer .Net class is not available in the version of Mono Unity uses - I'll look into doing a custom implementation though.
     
  21. GennadiyKorol

    GennadiyKorol

    Joined:
    Jul 10, 2010
    Posts:
    107
    Just an idea:

    It would be very nice to introduce editor auto completion by auto generating a class hierarchy corresponding to the xml (json, pretty please!) file.
    Something similar to iTweenHinting.
    Actually, let's take that even further. Imagine that root category would be called "Player". You could do stuff like this in code:

    Code (csharp):
    1. int health = ConMan.Player.Health.MaxHealth; (// can we have some implicit conversion to int? if not, add .asInt() at the end).
    2.  
    3. // EDIT: of course we can. MaxHealth would just be an int property, silly me!
    4. This will be much easier to use and safer than get<Type>().
    5.  
    This way you can also implement delay loading as well as caching in an absolutely transparent way and it's much easier on the scripter to write that instead of the actual path.
    A simple editor post processing script should be able to auto generate this file without any problem at all.

    Also, is there an option to split things into different xml (json, please, please!) files? A separation on the root category level would be nice.
    The less values you have to edit in a single file, the less merges / svn trouble you ought to get in production.


    Oh, and being able to customize the root namespace for your settings could be ace. ConMan is okayish but I would much rather have something like Settings or Constants, etc.

    Thanks!
     
    Last edited: Jun 6, 2011
  22. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    Hello again GennadiyKorol:

    1) The major categories should already automatically be given their own separate XML files (there was an issue in 1.0 and 1.01 preventing the addition of new major categories, this is fixed in 1.02 which is available in the assets thread).

    2) You can customize the access by adding your own extension class. Something like

    Code (csharp):
    1.  
    2. public class Constants : ConMan { }
    3.  
    Just make sure this file is also in the Plugins folder (it needs to be in the same Assembly as ConMan).

    3) I'll look into the auto-generated static helper class, added to the suggestions list!
     
  23. GennadiyKorol

    GennadiyKorol

    Joined:
    Jul 10, 2010
    Posts:
    107
    Cheers!

    I think the auto generation could be bloody fantastic. You could even pre-compile all of the values inside to the script file (unless you want to do in game serialization, which is probably another topic? read below) and not have the need to have an xml file sitting near your executable.

    So things like
    Player.Health.MaxHealth would simply turn to:

    Code (csharp):
    1.  
    2. public int MaxHealth {
    3. get { return 100; }
    4. }
    5.  
    This would perfectly serve the purpose of allowing the team to collaborate on their svn (or any other revision control system) without any runtime overhead for static data that is defined at 'compile time'.

    You could however still generate set methods for properties that are expected to be changed in a persistent way at runtime. But this sounds like a whole different category which is more relevant to serialization than it is to settings or configuration.

    AND you get a compile time sanity check on all of your configuration using code (accessing non existing values, type safety, etc) for free! Compile time is always better than runtime.

    Food for thought :)
     
    Last edited: Jun 6, 2011
  24. GennadiyKorol

    GennadiyKorol

    Joined:
    Jul 10, 2010
    Posts:
    107
    I guess what I'm suggesting is much more than just a helper class but a more robust design for the whole thing. It might be a bit of work but I am sure it will be very much worth it.

    Later on we could allow assigning things like textures, materials and maybe even prefabs to the xml values (using their GUID's, easily) making it a complete configuration system for the masses.

    Some thoughts on the UI:
    A way to somehow design the UI of the categories would be ace. Being able resize, layout those 'value boxes', group, position them in a much more space efficient and user friendly way would be a huge thing in production.

    The create new value dialog should probably be on a drop down menu to save more screen real estate.

    I apologize if I'm being a bit 'naggy' with all the ideas and criticism, it's just that I think this has the potential to be an extremely useful tool for every Unity user.
     
  25. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    Not at all, nitpick away!

    At the moment the GUI is pretty terrible, everything is super-bulky. I was trying to do something quick without having to add my own GUISkin, but it's looking like that's the direction I'll end up going on.

    I do really like the concept of the helper class though, should be an interesting educational experiment too. Especially for the compile time sanity check, that would be an enormous benefit.
     
  26. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    Just wanted to give you a quick update. I just did a test to see how easily I could generate a C# file/class from scratch. Ended up quite a bit easier than I expected. This is definitely a feature I want to implement sooner rather than later.

    For a little while, I definitely plan to make it event driven (there will be an editor menu option called something like "Generate ConMan AutoComplete Files") - and it will be in addition to XML for quite a while to make sure it's stable.

    I will have to generate a C# class for every category - the following classes will be generated...

    /Assets/Plugins/KVS/ConMan/AutoClasses/AutoMan.cs
    /Assets/Plugins/KVS/ConMan/AutoClasses/AutoManCategory+CategoryString.cs

    Every category including sub-categories is going to need it's own file, so the AutoMan class list may get relatively large. However, since they will all include the AutoManCategory as the start of their class name, it should not interfere with anyones actual project classes.

    I'll let you know as I make more progress. Actual work-week time for me now, so I probably won't have anything interesting to show until next weekend - but hopefully then I can bang this feature out.
     
  27. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    Okay, hopefully you are going to love this as much as I do.

    Done...ish...!

    It generates the classes fine for all currently supported data types, they can be accessed via:

    AutoMan.MajorCategory.SubCategory.SubCategory.VariableName

    All category and variable names are now cleansed to only contain alphanumeric characters, and to ensure that the first character is a letter.

    Also, the comments are automatically added to intellisense (via /// <summary>).

    Thanks for the suggestion! It's pretty awesome actually.

    EDIT:
    Need to clean some things before I upload this version, but I'll get it into the assets thread tomorrow for you to check out.
     
  28. GennadiyKorol

    GennadiyKorol

    Joined:
    Jul 10, 2010
    Posts:
    107
    Sounds greato!

    Do you also 'precompile' the values inside of those properties or do you simply delegate to "Get<Type>(valueFullPath)"?

    I am trying to figure out how one could add support for "asset references" for such a system.
    Imagine having a variable that contains a texture rather than just color. Same for materials and prefabs. Wouldn't it be great if I could create a variable for a health bar object that is being used for every killable object. That prefab would be set in one place and have a global access point to it at runtime. Users would not have to set that reference on every single one of their scripts and changing the healthbar would be a matter of changing that variable.

    But it's not trivial since I don't know how you could hard code references to those assets in the code. That's probably the case because of all the 'don't include unused assets in the build" thing.
    SO asset references need to be on a prefab of some sort in order to access them.

    I had an idea of how that could be done, like this:
    You can load and access prefabs (and other assets) in memory by doing a Resource.Load and by specifying their path.
    Now, putting all of your textures, prefabs and materials there is not the best idea.

    So what if we would have an auto generated prefab, with a single script on it called "ConfigurationAssetReferences" or something like that.
    That script would contain public references to all of the assets that are referenced in the configuration file.
    The assets are referenced using GUIDs in the XML / Json file and being looked up and assigned to the auto generated prefab using the asset database API.
    The ConfigurationAssetReferences then implements a singleton which is being loaded with Resource.Load the first time it is being accessed.

    NOW, your auto generated configuration classes can access that prefab using the singleton pattern (and its script, therefore its public variables) transparently to the user.

    The healthbar example would look like this:

    Code (csharp):
    1. Configuration.HealthBarObject { get { return ConfigurationAssetReferences.Instance.HealthBarObject;}}
    2.  
    So what do we get? We get the ability to set asset references in our global configuration and transparently access them through our type safe class hierarchy. And that with all the benefits of having that configuration stored as a text file, merge and svn friendly.
    Isn't that awesome? I think it is.

    And hooking this with the post processing event to run every time any file in the configuration folder changes shouldn't be a matter of minutes.

    After this is done, given a nice UI (the one other tools like Shader editors, PlayMaker are using, shouldn't be hard, or is it??) this will be an absolute killer tool for any production.

    Keep up the good work, can't wait to check the latest version :)
     
    Last edited: Jun 7, 2011
  29. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    Asset references is definitely the next big step. For now, I want to get some kinks worked out with the normal AutoMan script (it's pretty much there though) and I really want to get an attractive GUI slapped on the thing. After that I'll work on being able to store/retrieve asset references (probably Texture2D, Prefab, and audio assets to start with - adding support for more types in a later version) is next up on the agenda.


    AutoMan hard-codes the values btw, here's an example:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class AutoManCategoryPlayerCamera {
    6.  
    7.  
    8. static Color _BackgroundColor =  new Color(1f,0.4825175f,0f,1f);
    9. /// <summary>
    10. /// The color of the camera background.
    11. /// </summary>
    12. public Color BackgroundColor { get { return _BackgroundColor;}}
    13.  
    14. static Color _ClearColor =  new Color(0f,0.7902098f,1f,1f);
    15. /// <summary>
    16. ///
    17. /// </summary>
    18. public Color ClearColor { get { return _ClearColor;}}
    19.  
    20. static float _MaxDistance =  25f;
    21. /// <summary>
    22. /// Max distance from the player that the camera can zoom to.
    23. /// </summary>
    24. public float MaxDistance { get { return _MaxDistance;}}
    25.  
    26. static float _MinDistance =  1.75f;
    27. /// <summary>
    28. /// Min distance from the player that the camera can zoom to.
    29. /// </summary>
    30. public float MinDistance { get { return _MinDistance;}}
    31.  
    32. static float _ZoomSpeed =  5.25f;
    33. /// <summary>
    34. /// Rate at which scroll-wheel zooms the camera.
    35. /// </summary>
    36. public float ZoomSpeed { get { return _ZoomSpeed;}}
    37.  
    38. static float _ScrollSpeed =  1.25f;
    39. /// <summary>
    40. /// Rate at which click+drag pans the camera.
    41. /// </summary>
    42. public float ScrollSpeed { get { return _ScrollSpeed;}}
    43.  
    44. }
    45.  
    46.  
    So once AutoMan generates itself, you can actually safely move the XML out of the resources folder (so it doesn't get included with the build) if you do not also use ConMan. (I personally will also be using ConMan, so I can load variables dynamically like ConMan.GetString("Items/"+_id+"/Name") without having to use reflection at runtime).

    Now, AutoMan does come with one potentially serious issue. If for some reason it doesn't generate proper, or you heave any compile error in your code when you close Unity - next time you open Unity, ConMan will be unable to compile as well and you will not be able to automatically fix the AutoMan code. There are a couple of options for recovering from this. First, manually fixing your AutoMan code shouldn't take more than a minute or so (since you likely have very few issues). However, if the AutoMan generation screws up on something entirely - you could also make an empty project, import ConMan, add your XML and generate the AutoMan files there, then move the files over to your main project.

    A bit of a PiTA if it royally screws up on a project with hundreds of thousands of AutoMan variables being used - but that's what SVN/Git is for.

    Attaching some screen-shots of the latest working version quick, then it's off to paid-work time, and I'll get back on ConMan later this evening to push an update live.

    In the first screenshot you can see the latest UI. It's still far from perfect, but it's much better (in my opinion) - more compact if nothing else. As you can see, variables can be contracted/expanded. In their contracted state they take up only one line (see colors and most of the floats), in their expanded state they grow to add room for the accessor and comment fields (which are hidden otherwise). Clicking the name of the variable toggles between the two.

    The filter got a nice update too, aside from general improvements to the way it displays results - you can how go to the home page and search with filter. It will show results in all categories and sub-categories, and sort/label them by which category they appear in (so if you forget where your camera MaxDistance is - you can search "maxdist" and it'll pop up no matter what category it was added to).

    The second screenshot is a quick shot showing how AutoMan works with MonoDevelop's auto-complete, including the comment :D
     

    Attached Files:

  30. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    BTW, AutoMan stores sub-categories like this:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class AutoManCategoryPlayer {
    6.  
    7. static AutoManCategoryPlayerCamera _Camera = new AutoManCategoryPlayerCamera();
    8. public AutoManCategoryPlayerCamera Camera { get { return _Camera; } }
    9.  
    10. }
    11.  
    It had to be an instance of the sub-category to be able to use the AutoMan.Category.SubCategory format (since to the best of my knowledge C# cannot store a named reference to a static class in a variable). Not really a big deal though, there will only ever be one instance of each category, so the memory impact compared to a fully static class should be negligible.