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

Question Possible to implement custom code style rules in Visual Studio?

Discussion in 'Code Editors & IDEs' started by cat_hill, Mar 16, 2023.

  1. cat_hill

    cat_hill

    Joined:
    Feb 23, 2023
    Posts:
    27
    I've been trying to research (with no clear resolution) how to implement custom warnings for my Unity projects in Visual Studio.

    For example, I keep getting burned when I have a public field initialized in the field declaration (rather than Awake()) because there's the very likely chance something in the inspector will silently override it. The Unity tutorials seem to be indiscriminate with how they initialize fields adding to the problem. So I was wondering if I could write a custom rule to trigger warnings for conditions I define. Example below.

    Code (CSharp):
    1. public class MyClass: MonoBehaviour
    2. {
    3.     //prefer this
    4.     public float speed;
    5.     //over this
    6.     public float speed = 5;
    7. }
    It seems in Visual Studio I can only enable/disable or set severity levels for existing rules. I can't figure out how to set custom ones. I have a feeling this has to be possible but can't quite find the right article or help topic to do it.

    Related - if anyone has done this and has created a set of messages and warnings tuned to Unity, I'd love to see what you have.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,518
    Interesting. My IDE-fu isn't strong enough to know if the rule language is good enough to figure this out.

    BUT... for my dollar, I think there is great value in understanding that this:

    .. is not random at all.

    I guarantee you will become a far more effective Unity user if you can instantly pinpoint that "something."

    And the compiler warning can only address one of the steps in the cascade.

    This the cascade, btw:

    Serialized properties in Unity are initialized as a cascade of possible values, each subsequent value (if present) overwriting the previous value:

    - what the class constructor makes (either default(T) or else field initializers)

    - what is saved with the prefab

    - what is saved with the prefab override(s)/variant(s)

    - what is saved in the scene and not applied to the prefab

    - what is changed in Awake(), Start(), or even later etc.

    Make sure you only initialize things at ONE of the above levels, or if necessary, at levels that you specifically understand in your use case. Otherwise errors will seem very mysterious.

    Here's the official discussion: https://blog.unity.com/technology/serialization-in-unity

    Field initializers versus using Reset() function and Unity serialization:

    https://forum.unity.com/threads/sensitivity-in-my-mouselook-script.1061612/#post-6858908

    https://forum.unity.com/threads/crouch-speed-is-faster-than-movement-speed.1132054/#post-7274596
     
  3. cat_hill

    cat_hill

    Joined:
    Feb 23, 2023
    Posts:
    27
    Oh yeah - it's clearly not random. 100% agree. But in a lot of the code I've seen, I can't discern any pattern as to why they could chose two different initialization approaches for completely analogous purposes. It just creates a lot of cognitive load in even a medium size project to have to memorize how everything is initialized.

    So given that, I'm looking for ways to simplify. Even better if I can have some automation help me do it. I realize everyone / every team / every business unit will have their own preferences. Not here to argue for one set of preferences over the other. I just want to find a way to create automation to cater to my own personal preferences.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,518
    Oh wow, imagine a diagram, like an engine schematic, left to right, drawing knowledge of the compiled code AND the Unity assets...

    Both inside Unity on each field OR available by right-clicking on the identifier in code:

    Right click -> See Initialization Sequence

    And then you could instantly tell... "Oh, it's the prefab OVERRIDE that's doing it!"

    Oh yes: then warnings if something changes underneath an overridden value: new initializer... new base setting... it would have to be a pretty crazy complex diagram to truly capture all choices....
     
  5. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,606
    I know you can edit Visual studio styles rules, but I doubt you can tell it to warn you about inline initialisation. It's a pretty fundamental feature of the language.

    You can only inline initialise with static or constant values. Which is why you probably see some things initialised in Awake/Start/OnEnable, because they can't be initialised inline. Or the initialisation depends on something else being initialise first.

    Code (CSharp):
    1. private int _someInt = 5; // can initialise inline
    2.  
    3. private Rigidbody _rigidbody; // can't initialise inline
    4.  
    5. private void Awake()
    6. {
    7.     _rigidbody = this.GetComponent<Rigidbody>();
    8. }
    In serialised values the inline initialise is used only once to set the initial value. Anything after that is whatever value you set in the inspector. Changing the value of the inline initialised value won't change any instances already serialised.

    On non-serialized fields, it will be used to reset the the field to the inline initialised value when the asset is reserialised (ergo, during a domain reload).
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,518
    And it gets pretty hairy when you think about it:

    Code (csharp):
    1. public int foo = 1;
    2.  
    3. void Reset()
    4. {
    5.     foo = 2;
    6. }
    Now I put that on a prefab and set
    foo
    to 3.

    Now I make a prefab override and set
    foo
    to 4.

    Now I drag the prefab override into a scene and set
    foo
    to 5 and save the scene.

    All of the above gets source-controlled and committed and done.

    Now what happens to
    foo
    in each of these cases, considered one at a time:

    I open the scene and press Reset() and save the scene.

    I edit the prefab override and press Reset()

    I edit the base prefab and press Reset()

    And what is
    foo
    if I simply use .AddComponent<T>() to add my MonoBehaviour?

    etc.

    "Stay calm and attach the debugger."
     
  7. cat_hill

    cat_hill

    Joined:
    Feb 23, 2023
    Posts:
    27
    Yeah that would be quite useful. A little more than I need but could be a great tool.
     
  8. cat_hill

    cat_hill

    Joined:
    Feb 23, 2023
    Posts:
    27
    Ok - a little bummed the answer is the debugger.

    If anyone has experience with writing custom static code analysis in Visual Studio, looking forward to hearing a response. I could imagine many small (but annoying) problems being solved with simple static analyses.