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

Unique components. Injecting directly. Injection trigger conditions.

Discussion in 'Entity Component System' started by illinar, Apr 12, 2018.

  1. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    1) I don't think there is a way to make sure that only one instance of a component per world (or app) can be created. Is such feature planned?

    2) Also, API would get much lighter in many cases when ComonentDataArray<> Can be injected directly.

    Instead of:
    Code (CSharp):
    1.     private struct Data
    2.     {
    3.         public ComponentDataArray<MousePosition> mousePosition;
    4.     }
    5.     [Inject] private Data data;
    Would be nice to have this:
    Code (CSharp):
    1. [Inject] private ComponentDataArray<MousePosition> mousePosition;
    And better yet:
    Code (CSharp):
    1. [Inject] private MousePosition mousePosition;
    Or:
    Code (CSharp):
    1. [Inject] private UniqueComponent<MousePosition> mousePosition;
    Where MousePosition is a UniqueComponentData. If such injection is possible. Feels messy and unsafe currently accessing such a component directly by index 0 in an array with a risk that there might be another one created accidentally somewhere.


    3) And lastly, is there a way to make sure that all of the specified injections are made for the system to trigger? (Without making checks manually.) For example, a system that kills enemies with explosions should only execute when there are explosions AND enemies injected. Or when a button pressed even AND a gun are present. Maybe it could look like this:

    [Inject] [Required] private Input input;


    But then there are more complex cases where you need (A) and (B or C). And of course, I'm not talking about different components, but entirely different archetypes.
     
    Last edited: Apr 12, 2018
  2. timjohansson

    timjohansson

    Unity Technologies

    Joined:
    Jul 13, 2016
    Posts:
    473
    1+2) There is no singleton or unique components right now. We did experiment a bit with something very close to what you describe with UniqueComponent<> a while ago, but it did not reduce code complexity much and some developers wanted singleton components to work differently, more like a combination of what you describe and shared components.
    (Injecting the struct directly does not work because it would embed the data into the system and writing to it cannot be forwarded to the chunk where the component data is stored)

    If there is a strong wish for it we can pick up those experiments again, but it is not something we are actively working on and not in our short term plans right now.

    3) We do not have something like that today, but doing the check manually does not have a significant performance impact unless we are talking about a huge amount of systems which are mostly inactive - and for complex cases I suspect it is easier to read a simple if(empty) return; than a combination of [Required] attributes.
     
    MadeFromPolygons and Zoey_O like this.
  3. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    Most of those suggestions come down to just simpler syntax, less code hopefully at no cost. I just want to put those suggestions out there. Definitely not a high-priority stuff.

    Although declaring a struct with an array inside to just receive one "singleton" piece of data bugs me a little X)

    This one line monstrosity works too:
    var mousePosition = GetComponentGroup(typeof(MousePosition)).GetComponentDataArray<MousePosition>()[0];


    But I really like injection because it sits in the top of the system class and makes it very clear which data the system is working with.
     
    Last edited: Apr 12, 2018
  4. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    do you really need a "MousePosition" component to be singleton?
    you can do
    Code (CSharp):
    1. public struct MousePosition : IComponentData { float2 value;}
    2. public class MouseSystem : JobComponentSystem {
    3.     public struct UpdateMouseJob : IJobProcessComponentData<MousePosition> {
    4.         public float2 inputValue;
    5.         public void Execute([WriteOnly] ref MousePosition mousePosition) { mousePosition.value = inputValue; }
    6.         }
    7.     }
    8.     JobHandle OnUpdate(JobHandle inputDeps) {
    9.         var job = new UpdateMouseJob { inputValue = Input.mousePosition }; // convert Vector3 to float2
    10.         return job.Schedule(this, 64, inputDeps);
    11.     }
    12. }
    and attach a MousePosition component to each entity that requires it. it may be faster than having a singleton because you access the (local) mouse position linearly with the other component data for each entity. each singleton basically "wastes" a cache line.
    even in the examples, there is a "Settings" singleton object, but it's not a IComponentData and only accessed during setting up the jobs. this type of data can be attached to the systems (via custom bootstrap) or the world
     
  5. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    In "Settings" classes I only keep immutable data, so things like mouse position I keep on entities, just for the sake of purity.

    Attaching mouse position to every entity that needs it means that mouse position system assumes a lot about what other systems are doing. Really not good, in my opinion. One cache line sounds like nothing.

    I appreciate the suggestions, but can't say that I understood what the example was about.
     
  6. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    669
    I think we'd need someone with a better understanding to clarify, but what I'm getting from this is, for all intents and purposes, blitting a value to a bunch of memory locations so that the same value is locally cached near where it's used is faster than a bunch of disparate routines trying to access one memory location that may or may not be in the fast cache.

    As nice as unique components work conceptually, it's still OOP-type thinking and basically a global variable or a singleton: They're useful in simple cases, but actually bad practice for more reasons than the obvious and should be avoided unless they're the only solution that works (such as for bootstrap code).

    In the mouse position case, the systems don't need to read "The" mouse position, all the need access to is "A" mouse position. It should be the same mousePosition that's valid in the current frame. Blitting it as above, early in the frame, is basically a rule that enforces this concept.
     
  7. optimise

    optimise

    Joined:
    Jan 22, 2014
    Posts:
    2,114
    Quote from Incremental Compiler post:
    I think what you really want is preventing you or your team member from creating another redundant same component again. I guess when this Incremental Compiler starts taking shape, you can define the rule for the component u want to create only once and will throw compile error when trying to create second one.

    It's the better solution that can give the singleton like restriction and still preserve the current performance at the same time.
     
  8. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    If you create a ComponentSystem that doesn't operate on any components and just does stuff on update, wouldn't that basically be a singleton?
     
    Last edited: Apr 13, 2018
  9. optimise

    optimise

    Joined:
    Jan 22, 2014
    Posts:
    2,114
    Another possible solution but it will require Unity to implement this solution is like
    CreateUniqueComponentData
    API and able to [Inject]. It works like what @illinar mentions that when you create another one same component it will throw error in Unity Editor. When build player build it will strip out the safety checking code to improve the performance just like what the safety system of C# Job System.
     
  10. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    All I mean is preventing more than one instance of a Component data from being created. Nothing OOP about it.

    And I no longer use any immutable static data at least repeatedly. Any repeatedly accessed data is in ECS. Scriptable objects are converted into ComponentData when the project is loading.
     
    Last edited: May 15, 2018
  11. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    By the way. There could be not only Singleton (Unique) but also Immutable ComponentData perhaps?