-
Notifications
You must be signed in to change notification settings - Fork 1.1k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ECS Game Architecture with Unity and Entitas #610
Comments
If I strictly apply this model to Match One, I'd have to remove the How does this sound? Am I going crazy? :) |
UI and Views visualize the current game state. They don't affect the game simulation. They can emit input like button clicks, but the game simulation doesn't know if this input came from a button, from the network or from some other program. That's why we don't need Entitas systems for them and can decouple them from the core logic as observers of the game state. UI and Views know how to represent the current game state. This allows us to start with placeholder assets which later can be replaced with the real game assets without touching the game logic. |
We are doing it exactly like shown in the graphic. Furthermore we splitted the game logic into different parts: Action -> Command -> Core -> View. The View or other external inputs are able to create actions. The systems there are doing checks if it's possible and emit commands. The command systems will change the state of the core. Core systems only change core state. View is reacting on core and creating view state. Listeners are getting called, because something changed directly in core or in view. We also have a meta context which is just loaded balance values (from json just for easier access). This is done because action and view are part of unity (client side). Command and core are part of the simulation and will run not only on the client, but also on the server (we have a simulation game). |
1st result of applying this strictly with TDD (Audio):
After:
Less complex and almost certainly more efficient I guess I'm going down this road further :) |
Can you post your experiments into some repository? |
i thinks it's good to use react system to tell ui & view logic. |
Model looks nice and very intuitive, should be in wiki somewhere 😄 I wouldn't place |
I also refactored my views now. All testable and very nice :)
|
I really start loving the new events. It feels so clean now :) |
Awesome. Btw @sschmid does it support loading some game objects first at loading time? For example, I want to load 10 enemies into game object pool first before starting game. I expect there is an API to preload the game object first into game object pool. |
Yes :) |
Updated diagram to include commands. Invalid input could be for example buying an item, but you don't have enough resources. Commands don't verify, they only act. |
I recently built a few smaller games following the "Game Architecture with Entitas" diagram very strictly and I was really happy with the results. After the latest Video Tutorial about TDD #611 I started with strict testing again, and I have to tell you: |
As another consequence I could get rid of the following contexts: Input, UI, Audio. I only have game and command context left. |
ViewService, AudioService and all the other services are all less than 50 lines of code. Things became veeery simple now :) |
This sounds very interesting. Will you be sharing your game examples using this structure? I've been struggling a bit in my current project to convert input actions/intents into validated commands and that apply the state changes. I was trying to follow a design discussion you had on gitter a few months ago. |
Would you do A.I. in the "External Input" section or "Game Logic" section? Which logic should be done directly in systems and which should be passed off to do in View's? |
I don't understand why you'd want to move Input out of a system if it could exist in a system. If it lives inside the "view/presenter" domain, i.e. in a Monobehaviour, you have much less control over it. I generally try to keep as little as possibe in Monobehaviours. However, I also rarely use OnMouse... handlers, but rather roll my own input with raycasts and state etc., to account for multi-touch etc. |
You can definitely have systems for input, no problem. I'm thinking in a very general and very broad context, one in where you can take the game logic as is without any modification and run it everywhere, e.g. server. On the server, probably don't run unity and also you don't have input like mouse. Only commands, e.g. as a result from validated network input. Same is true for views etc. I'm really reducing it down to the core logic. You can still have systems for input and views if you want, but then you'd have to modify the logic in a scenario where code is shared |
What's also really helpful in our project is that we have an Action Context before Command Context. Like in the updated architecture commands are only verified data for the game logic which gets checked in action systems. But actions also help with view systems and the data in view. Actions are allowed to change the view context directly. That's very helpful for menues and other view stuff where you need reactive behaviour, because they are complex (best practice here is, that menues never have state in MonoBehaviour and only react to view components). So you don't have to make commands out of actions that are only interesting for the view and the game logic doesn't need to know about these. Therefore I would create a new rectangle before commands and call it action with a second line to view. (Could be to complex in the MatchOne example or Schmup, but in a bigger project this step is important imo) |
There are lots of benefits when you have a defined architecture in your game. One of those benefits is that you can automate and generate code.
You need to update the the CommandSystems list and the InputActions list. I wrote a custom roslyn code generator that does all that for me. I only need to maintain a static class with fields (basically the input action types) public const string START_ROUND = "StartRound";
[DataField(Data.POSITION_X, typeof(float))]
[DataField(Data.POSITION_Y, typeof(float))]
[CommandField("position", typeof(Vector3))]
[Event]
public const string AIM = "Aim";
[DataField(Data.POSITION_X, typeof(float))]
[DataField(Data.POSITION_Y, typeof(float))]
[CommandField("position", typeof(Vector3))]
[CommandField("velocity", typeof(Vector3))]
[CommandField("ammo", typeof(int))]
public const string SHOOT = "Shoot"; The code generator will then generate:
All I need to do is to implement the ShootInputService and fill out the rest of the partial ShootCommandSystem (which is only the execute method). Very nice :) 👍 |
Would be nice to have a link to gist with the result of code generation. Otherwise there are too many questions in my head 😁 |
@sschmid Awesome. I hope I can get that new feature soon. |
@sschmid Can you integrate custom roslyn code generator into Entitas? So for those who need it can directly use it. |
To write a custom roslyn generator you have to set up a new c# project with .NET 4.6 and install roslyn. If you reference DesperateDevs.Roslyn you can use some utility methods already. I will probably make a video about it. |
Awesome. It can be a very good starting point to learn how to write custom code gen as until now I still dunno how to do it. lol |
I updated the diagram. Feedback very welcome |
I would also say, the whole "logic talking to view layer == bad" thing is probably overblown. It's a game, it's safe to say that it's being drawn, but less safe to say how it's being drawn. I don't mind having these systems that talk to views because they're only ever talking to my interfaces. I use events to minimise the amount i have to do this, but i find it really useful to have some references around in case i need to talk to them. That's not to say it's not a bit of a crutch, but i built these before Simon did events and I'm clinging to them for a bit longer :) |
yeah, i use the new non-alloc groups though so i dont notice the cost. |
@FNGgames Thanks for your help! |
@FNGgames I see. Thanks for the clarification. |
@FNGgames What is your tag on UnityGameView for? |
@cruiserkernan it's an enum tag i give to all entities - similar to unity's tag system, so i can find entities with tag or check a tag for something when i have a collision or whatever. It's just a component with an enum field. |
That's exactly how I did before, too. If you think about, using the events, that's still exactly the same: a system will call a method that's implemented from an interface. The only difference is, these systems are now automatically generated and you don't have to write them + those systems work on all kinds of things, not only views. |
@FNGgames Btw, thank you very much for your post. I love it :) Thanks for taking the time, I think it will help a lot of people who are new to all of this. |
@sschmid good point RE: the events - there's still something talking to the views, but it's all hidden away. No problem on the article, tbh i probably wrote it all in chat a couple of times before - it's nice to just be able to give some a link now :). |
To answer a question from the chat:
This is an optional idea that some of use to streamline adding new features and inputs.
|
the larger the game and the more inputs you have, the more important it becomes to have a defined way how to handle those inputs. This approach helped me managing this even with lots of inputs |
I just want to make sure if I understand it correctly. This would mean I have an Input, Command and Game context version of the components? Then, I have an InputSystem to filter the Input entity which creates a Command Entity, then have a CommandSystem to operate on the Command entity which it applies to the Game entity? |
see comments above |
Yes, that's the idea. Most of it can be generated as described in the comment above. So all I implement is just the execute of the input system and the command system. Everything else will be generated |
@sschmid I wonder how you can like emits |
What do you think about physics interaction with ECS/Entitas? If we need to interact with the physics, where would you put the "logic"? Here my example : I have a plane controlled by physics (Rigidbody.AddForce) and a PID controller to calculate the right amount of force to apply.
With various Entitas examples, I see that the views listen the position component to update its transform values, because it easy to see the position of the view as a state of the view. But with physics, like the velocity, is it a view state controlled by the rendering engine or is it part of the game logic and it could be unit tested? |
@OmiCron07 I'd say it's up to the developer to decide. There is a balance what data to put into ECS layer and what to keep in View layer. I'd handle most of physics interaction inside View layer(or create FixedSystems to run in FixedUpdate). And occasionally send data through ECS components so that systems and eventSystems could react. |
Sounds ok to create system to calculate something and another system to apply calculations.
It would work, could be tempting and easier to make but sounds more fragile in the long run.
Often
Physics changes unity transform values. It's possible to sync changes afterwards by checking |
Thank you for the responses, will check it out how it turns out. |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
ECS Game Architecture with Unity and Entitas
This is my mental model based on which I decide where to put my code.
Feel free to comment so we can improve this model further
The text was updated successfully, but these errors were encountered: