Beginner here so this might be dumb. Just wondering why we can’t call basically any Unity function from a Job/System using a threadsafe messaging system instead of direct calls? I know there’s the Entity Command system, but can that be used for anything/my own functions? It seems to mostly just have Entity spawning?
I get you’d have to realise that messages don’t get run immediately and can’t expect state to be updated in a later bit of code, but it would be nice to reuse most of my existing Unity knowledge.
The reason you can’t is because the team at Unity didn’t think of a trivial-to-implement solution that had good performance. I don’t blame them.
What is it you are trying to do?
I’m not sure if you’re saying it’s a good idea or a bad one? I’m simply trying to use Unity in a way that is closer to normal use, instead of having to learn everything from scratch.
I didn’t say anything about that. I just said that given the other design constraints of Unity’s ECS, the problem is a very difficult if not impossible one to solve and the ECS team at Unity hasn’t solved it yet.
Jobs and traditional GameObject APIs don’t really go together. There are exceptions for specific systems provided by the specific teams developing those systems. But in general when mixing GameObjects and ECS, you are going to be doing things on the main thread.
If you want to use new things, then you have to learn to use new things.
Sure, but the Entity command buffer does what I’m talking about right? I only know it can spawn entities, but I was wondering if it can do more, or anything custom?
ECB is limited by the set of commands it can apply to the internal unmanaged data store.
It can’t modify managed data stored in the heap thread-safely or by any other way.
Everything that is unmanaged can be burst compiled on top and its blazing fast. (up to 100x+ times)
Unity’s MonoBehaviour API is not thread-safe and can be accessed only on main thread.
(like >90% of it)
Which is slow. Like really slow. Plus marshalling costs.
Even if you somehow manage to write your own custom logic on top of ECB to modify MonoBehaviours in a same manner - you’d probably have better performance by just querying respective behaviour in SystemBase and calling required methods directly. By prefiltering data in jobs you’ll get better performance.
Applying results is cheap. Computing data - that’s the main bulk of work.
The more you dive into DOD, the more you’ll understand that events are antipattern.
Because events aren’t events. They are more specific data transformations in specific order.
Events turn data transformations into spaghetti which leads to unknown and undesired application behaviour.
If you want to utilize both - use hybrid approach and just use MonoBehaviours and Entities.
Its doable. Its less performant than pure but still faster than just MonoBehaviours. Slowly chip away at codebase & move logic to systems. Refactor MonoBehaviours as neccessary and you’ll end up at some point with a proper pure solution.
I think maybe they need like 600 examples of “Here’s a little thing in GOs and here it is as ECS”, because it’s simply the mapping between how I know to do something and how it’s expected to be done in ECS. They often have no recognisable similarities at all, so we just need a lot of tiny examples of this mapping.
The existing examples are fine, but don’t address this aspect.
So basically 600 low quality youtube tutorials on how to code?
That’s up to content creators.
Scripting API is the best manual.
Rest is the same once you’ve figure out how to design by data instead of cats dogs and plushies.
If you have solid questions, just ask them instead of asking for a generic solution.
Because that won’t happen ever.
No, I mean a github project with “This is how you used to do this thing and this is how it should be done with ECS”, for existing coders to remap their thinking to a different way of doing things (which is probably harder than starting with no code experience and just learning this as ‘the’ way)
That will be hard (nigh impossible) to maintain through the versions.
Even prior examples are old enough already to not work properly.
If DOD programming doesn’t “click” - read this before diving into ECS:
https://www.dataorienteddesign.com/dodbook/
Then read the manual:
https://docs.unity3d.com/Packages/com.unity.entities@1.0/manual/concepts-intro.html
Its quite good. If its still hard to grasp - write a couple of small prototypes until it clicks.
Split learning of DOD separately from Jobs / Burst. Learning multithreading with DOD simultaneously might be overwhelming.
Once you understand DOD - transferring logic from MonoBehaviours to systems is pretty straight-forward.
Update is pretty much a separate system. And MonoBehaviour data you set from inspector is data / component.
There’s potentially two different things you are struggling with.
You could be struggling with the mindset shift. In OOP you might ask: How do I notify the enemy when it is hit by a bullet so that it can subtract the bullet’s damage from the enemy’s health?
In DOD: You would ask the question this way: On the first frame that a moving bullet hits the collider of the enemy, I want the health of the enemy to be reduced by the bullet’s damage amount. How do I do that?
The other potential struggle could just be API discovery. Honestly, the best way to figure that out is to just ask the community specific questions, because the tech is still too new for the public “how to X” resources that classical Unity has built up over the years.
this was about it for me, I was reading about “physarum” or something like that, a mycelium generator and it was interesting reading how it works on the inside, it was nothing like OO and that kind of difference stuck out to me and it was at that moment that it clicked for me, but that kind of difference was all throughout for me when going over into ECS (very nice, very worth).
I wanted something along this lines too and i ended up with a way to create an “event” and then check if an “event” was active, maybe this could help you @Vectrex , for example:
EntityCommandBuffer ecb = new(Allocator.Temp);
ecb.CreateEvent(state.EntityManager, new LogicStepIncremented())
would be an extension method that creates an entity and disables the component but enables it at the beginning of the frame next frame (this is to make sure the event is enabled for at least 1 full frame). I had a system for enabling the event component and then a system for disabling it at the starts and ends of frames.
When I wrote it I wasnt sure of performance but I was using entity command buffers to make sure they get created and destroyed in the way I thought was best. Later i recycled the entity instead of destroying it. I used manually created queries using the EntityQueryBuilder to fetch for singletons, and the unmanaged systems were able to create an entity in OnCreate to contain dynamic buffers of event entities to manage. All of the systems/verb code were unmanaged ISystem systems and I didnt need in scene initialization of any kind (declaring a new type for the event is all thats needed).
Checking for events or “handling” them as you would with a System.Action, itd be a regular system that queries for the event because I wrote my events so that theyre a subtype of IComponentData (so its public interface IEvent : IComponentData, IEnableableComponent
). Since theyre disabled at end of frame and are components, my code would look like either of these two:
if (state.EntityManager.IsAnyEnabled<LogicStepIncremented>())
{
//handle event with no args
foreach (var q in SystemAPI.Query<RefRO<PlayerHasJumped>>())
{
Entity playerEntity = q.playerEntity;
//handle event with args
}
}
(I ideally wanted a way to create an event like this, and have it call a IJob of some kind so that other code doesnt need to poll or query and for having 1 declaration to define event but haven’t gotten to trying to write this yet)
Yeah, that’s the type of thing I’m talking about. Literally examples on how to do different design patterns mostly.
As for the ‘they could never keep up with API changes’. Well… don’t they test their own code? Wouldn’t little examples like this be required internally to even see if what they’re doing makes sense? Surely they realise the importance of getting people onboard to ECS to give the engine a good reputation for speed?
eg, events, statemachines, can I use interfaces, how would a camera keep a reference to the thing its tracking etc etc. The existing examples seem to pretend that none of these existing ways of programming/thinking ever existed.
It also makes me wonder if something like this is how you might do an event style pattern, then couldn’t they make a wrapper as something like UnityEvent does to support this? I generally don’t like pointless wrappers around things, but if it’s REALLY laborious then it’s the point of an engine to speed that up.
They have examples. But they didn’t make sense to you so you dismissed them. The community had a hard time keeping up because it is not the community’s code. Don’t conflate Unity themselves with the community.
Some of them don’t in the general sense. In DOD, you solve the problems in more direct ways. There are different sets of tools you use in DOD because they perform better and are easier to maintain and evolve in a DOD-focused project. The others you describe (like camera tracking is simply a component holding an entity reference) do exist, but you need to ask about each one specifically, and not just throw them all in a bucket.
Ideally, you don’t have events in DOD. At least not in the traditional sense. Everything is built for very specific problems and no more. Now if you want event-like systems from an authoring perspective that a non-programmer can wire up in the Editor, that is possible and a much more interesting discussion. It then becomes a tradeoff of number of restrictions vs performance overhead. Fewer restrictions results in more overhead of the solution. If you can constrain the problem well, you’ll get tons of performance benefits.
But again, you need to ask specific questions if you want better answers.