README.md 10.4 KB
Newer Older
Muratet Mathieu's avatar
Muratet Mathieu committed
1
2
# <img src="docs/Logo/Logo FYFY gris.png" alt="FYFY logo" width="50"/> - FamilY For unitY #

Muratet Mathieu's avatar
Muratet Mathieu committed
3
FYFY is an Entity Component System (ECS) made for Unity. ECS software architecture is interesting for two main reasons: optimization and modularity. See [API documentation](https://webia.lip6.fr/~muratetm/docFYFY/) for details.
Mathieu Muratet's avatar
Mathieu Muratet committed
4

Muratet Mathieu's avatar
Muratet Mathieu committed
5
## What FYFY is? ##
Muratet Mathieu's avatar
Muratet Mathieu committed
6

Muratet Mathieu's avatar
Muratet Mathieu committed
7
FYFY was designed for teaching purposes to help beginners to understand data-driven simulation and to play with ECS mechanisms. FYFY is focused on the modularity interest of ECS. We try to make it easy to use and well-integrated into the Unity editor.
Mathieu Muratet's avatar
Mathieu Muratet committed
8

Muratet Mathieu's avatar
Muratet Mathieu committed
9
## What FYFY is not? ##
Muratet Mathieu's avatar
Muratet Mathieu committed
10

Muratet Mathieu's avatar
Muratet Mathieu committed
11
FYFY doesn't focus on optimization. If you are interested in high performances games use the native ECS of Unity instead: https://learn.unity.com/tutorial/entity-component-system#
Mathieu Muratet's avatar
Mathieu Muratet committed
12

Muratet Mathieu's avatar
Muratet Mathieu committed
13
## Repository structure ##
Muratet Mathieu's avatar
Muratet Mathieu committed
14

Muratet Mathieu's avatar
Muratet Mathieu committed
15
16
17
18
19
* FYFY folder is the main part of this project, it includes source code that enables you to manage Systems, Families and so on.
* FYFY_Inspector folder includes source code to easily manage FYFY in Unity editor.
* FYFY_plugins folder includes official plugins made to complete FYFY core.

# Short Introduction to FYFY #
Muratet Mathieu's avatar
Muratet Mathieu committed
20

Muratet Mathieu's avatar
Muratet Mathieu committed
21
## How to add FYFY to your project? ##
Muratet Mathieu's avatar
Muratet Mathieu committed
22

Muratet Mathieu's avatar
Muratet Mathieu committed
23
24
1. Drag and drop dll and xml files of the latest release in your Asset folder.
2. Create the MainLoop with the FYFY menu
Muratet Mathieu's avatar
Muratet Mathieu committed
25
26
27
<p align="center">
  <img src="docs/CreateMainLoop.PNG" alt="How to create the MainLoop?" width="400"/>
</p>
Muratet Mathieu's avatar
Muratet Mathieu committed
28
29
30
31

3. Now you can begin to code your systems and play with ECS, enjoy!

## What are FYFY entities? ##
Muratet Mathieu's avatar
Muratet Mathieu committed
32

Muratet Mathieu's avatar
Muratet Mathieu committed
33
34
35
Each Unity GameObject is processed as an entity in the FYFY context.

## What are FYFY components? ##
Muratet Mathieu's avatar
Muratet Mathieu committed
36

Muratet Mathieu's avatar
Muratet Mathieu committed
37
38
39
40
41
42
43
44
45
46
47
48
49
50
A FYFY component extends MonoBehaviour and contains only public data (no functions):

```C#
using UnityEngine;

public class FYFYComponentExample : MonoBehaviour {
    // Advice: FYFY component aims to contain only public members
    public int firstValue;
    public GameObject secondValue;
    public string thirdValue;
}
```

## What are FYFY systems? ##
Muratet Mathieu's avatar
Muratet Mathieu committed
51

Muratet Mathieu's avatar
Muratet Mathieu committed
52
53
54
55
56
57
58
A FYFY system extends FSystem:

```C#
using UnityEngine;
using FYFY;

public class FYFYSystemExemple : FSystem {
Muratet Mathieu's avatar
Muratet Mathieu committed
59
60
61
62
63
64
65
66
67

    public static FYFYSystemExemple instance;
    
    public FYFYSystemExemple () {
      instance = this;
    }
    
    // Use this to init system before the first onProcess call
    protected override void onStart() {
Muratet Mathieu's avatar
Muratet Mathieu committed
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
    }

    // Use this to update member variables when system pause. 
    protected override void onPause(int currentFrame) {
    }

    // Use this to update member variables when system resume.
    protected override void onResume(int currentFrame){
    }

    // Use to process your families.
    protected override void onProcess(int familiesUpdateCount) {
    }
}
```
Muratet Mathieu's avatar
Muratet Mathieu committed
83

Muratet Mathieu's avatar
Muratet Mathieu committed
84
A FYFY system uses families to get entities to process on. In the following example, the system builds a family that filters all game objects that contains the ```FYFYComponentExample``` component and increments their ```firstValue``` member:
Muratet Mathieu's avatar
Muratet Mathieu committed
85

Muratet Mathieu's avatar
Muratet Mathieu committed
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
```C#
using UnityEngine;
using FYFY;

public class FYFYSystemExample : FSystem {

    private Family familyExample = FamilyManager.getFamily(new AllOfComponents(FYFYComponentExample));

    // Use to process your families.
    protected override void onProcess(int familiesUpdateCount) {
        foreach (GameObject go in familyExample)
        {
            go.GetComponent<FYFYComponentExample>().firstValue++;
        }
    }
}
```

## What are families? ##
Muratet Mathieu's avatar
Muratet Mathieu committed
105

Muratet Mathieu's avatar
Muratet Mathieu committed
106
Family is the mechanism that enables systems to access entities (GameObjects). Families are built by the FamilyManager with the help of a set of matchers:
Muratet Mathieu's avatar
Muratet Mathieu committed
107

Muratet Mathieu's avatar
Muratet Mathieu committed
108
### Matchers on components ###
Muratet Mathieu's avatar
Muratet Mathieu committed
109

Muratet Mathieu's avatar
Muratet Mathieu committed
110
111
112
113
114
* AllOfComponents enables to filter GameObjects that contain all of specified components.
```C#
// Define a family that selects all GameObjects that contain Move and RandomTarget components
Family myFamily = FamilyManager.getFamily(new AllOfComponents(typeof(Move), typeof(RandomTarget)));
```
Muratet Mathieu's avatar
Muratet Mathieu committed
115
* AnyOfComponents enables to filter GameObjects that contain at least one of the specified components.
Muratet Mathieu's avatar
Muratet Mathieu committed
116
117
118
119
120
121
122
123
124
```C#
// Define a family that selects all GameObjects that contain Move or RandomTarget components (or both)
Family myFamily = FamilyManager.getFamily(new AnyOfComponents(typeof(Move), typeof(RandomTarget)));
```
* NoneOfComponents enables to filter GameObjects that don't contain the specified components.
```C#
// Define a family that selects all GameObjects that don't contain Move and RandomTarget components
Family myFamily = FamilyManager.getFamily(new NoneOfComponents(typeof(Move), typeof(RandomTarget)));
```
Muratet Mathieu's avatar
Muratet Mathieu committed
125

Muratet Mathieu's avatar
Muratet Mathieu committed
126
### Matchers on layers ###
Muratet Mathieu's avatar
Muratet Mathieu committed
127

Muratet Mathieu's avatar
Muratet Mathieu committed
128
129
130
131
132
133
134
135
136
137
* AnyOfLayers enables to filter GameObjects that are part of the specified layers.
```C#
// Define a family that selects all GameObjects that are part of layer 5, 6 or 7
Family myFamily = FamilyManager.getFamily(new AnyOfLayers(5, 6, 7));
```
* NoneOfLayers enables to filter GameObjects that are not part of the specified layers.
```C#
// Define a family that selects all GameObjects that are not part of layer 5, 6 or 7
Family myFamily = FamilyManager.getFamily(new NoneOfLayers(5, 6, 7));
```
Muratet Mathieu's avatar
Muratet Mathieu committed
138

Muratet Mathieu's avatar
Muratet Mathieu committed
139
### Matchers on tags ###
Muratet Mathieu's avatar
Muratet Mathieu committed
140

Muratet Mathieu's avatar
Muratet Mathieu committed
141
142
143
144
145
146
147
148
149
150
* AnyOfTags enables to filter GameObjects that are associated to one of the specified tags.
```C#
// Define a family that selects all GameObjects tagged "virus" or "bactery"
Family myFamily = FamilyManager.getFamily(new AnyOfTags("virus", "bactery"));
```
* NoneOfTags enables to filter GameObjects that are not associated to one of the specified tags.
```C#
// Define a family that selects all GameObjects that are not tagged "virus" and "bactery"
Family myFamily = FamilyManager.getFamily(new NoneOfTags("virus", "bactery"));
```
Muratet Mathieu's avatar
Muratet Mathieu committed
151

Muratet Mathieu's avatar
Muratet Mathieu committed
152
### Matchers on properties ###
Muratet Mathieu's avatar
Muratet Mathieu committed
153

Muratet Mathieu's avatar
Muratet Mathieu committed
154
Four properties are currently managed by FYFY. These properties are defined inside ```PropertyMatcher``` enum: ACTIVE_SELF, ACTIVE_IN_HIERARCHY, HAS_PARENT and HAS_CHILD.
Muratet Mathieu's avatar
Muratet Mathieu committed
155

Muratet Mathieu's avatar
Muratet Mathieu committed
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
* AllOfProperties enables to filter GameObjects that check all of specified properties.
```C#
// Define a family that selects all GameObjects that are active in hierarchy and has a parent
Family myFamily = FamilyManager.getFamily(new AllOfProperties(PropertyMatcher.PROPERTY.ACTIVE_IN_HIERARCHY,
                      PropertyMatcher.PROPERTY.HAS_PARENT));
```
* AnyOfProperties enables to filter GameObjects that check at least one of the specified properties.
```C#
// Define a family that selects all GameObjects that are active in hierarchy or has a parent
Family myFamily = FamilyManager.getFamily(new AnyOfProperties(PropertyMatcher.PROPERTY.ACTIVE_IN_HIERARCHY,
                      PropertyMatcher.PROPERTY.HAS_PARENT));
```
* NoneOfProperties enables to filter GameObjects that do not check all of specified properties.
```C#
// Define a family that selects all GameObjects that are not active in hierarchy and has not a parent
Family myFamily = FamilyManager.getFamily(new NoneOfProperties(PropertyMatcher.PROPERTY.ACTIVE_IN_HIERARCHY,
                      PropertyMatcher.PROPERTY.HAS_PARENT));
```
Muratet Mathieu's avatar
Muratet Mathieu committed
174

Muratet Mathieu's avatar
Muratet Mathieu committed
175
### Matchers combination ###
Muratet Mathieu's avatar
Muratet Mathieu committed
176

Muratet Mathieu's avatar
Muratet Mathieu committed
177
It is possible to combine matcher to build the system appropriate filter. For instance, following family will contain all GameObjects active in hierarchy with components Move and RandomTarget and not Velocity, and part of layer 3 or 7
Muratet Mathieu's avatar
Muratet Mathieu committed
178

Muratet Mathieu's avatar
Muratet Mathieu committed
179
180
181
182
183
184
185
```C#
Family myFamily = FamilyManager.getFamily(
    new AllOfProperties(PropertyMatcher.PROPERTY.ACTIVE_IN_HIERARCHY),
    new AllOfComponents(typeof(Move), typeof(RandomTarget)),
    new NoneOfComponents(typeof(Velocity)),
    new AnyOfLayers(3, 7));
```
Muratet Mathieu's avatar
Muratet Mathieu committed
186

Muratet Mathieu's avatar
Muratet Mathieu committed
187
### Family listeners ###
Muratet Mathieu's avatar
Muratet Mathieu committed
188

Muratet Mathieu's avatar
Muratet Mathieu committed
189
Because game simulation is dynamic some GameObjects enters and leaves families at runtime. You can define listener on families to catch these events.
Muratet Mathieu's avatar
Muratet Mathieu committed
190

Muratet Mathieu's avatar
Muratet Mathieu committed
191
192
193
```C#
// Family definition
Family myFamily = FamilyManager.getFamily (...);
Muratet Mathieu's avatar
Muratet Mathieu committed
194
195
196
197
198
199
200
201

// Use this to init system before the first onProcess call
protected override void onStart(){
    // Add listener to call back when a GameObject enters into this family
    myFamily.addEntryCallback (onGameObjectEnter);
    // Add listener to call back when a GameObject leaves this family
    myFamily.addExitCallback (onGameObjectExit);
}
Muratet Mathieu's avatar
Muratet Mathieu committed
202
203
204
205
206
207
208
209

void onGameObjectEnter (GameObject addingGo){
    ...
}
void onGameObjectExit (int removingGoInstanceId){
    ...
}
```
Muratet Mathieu's avatar
Muratet Mathieu committed
210
211
212
213
214
215
216
217
218

## How to integrate a system to my project? ##

Use MainLoop inspector to add your systems to one of the three execution contexts (FixedUpdate, Update or LateUpdate).

<p align="center">
  <img src="docs/MainLoopAddSystem.PNG" alt="How to add a system to the MainLoop" width="400"/>
</p>

Muratet Mathieu's avatar
Muratet Mathieu committed
219
## GameObjectManager ##
Muratet Mathieu's avatar
Muratet Mathieu committed
220

Muratet Mathieu's avatar
Muratet Mathieu committed
221
222
The GameObjectManager enables FYFY to keep synchronized with Unity simulation. If you choose to experiment FYFY you have to replace some of the classic Unity calls with overloaded GameObjectManager calls:

Muratet Mathieu's avatar
Muratet Mathieu committed
223
```C#
Muratet Mathieu's avatar
Muratet Mathieu committed
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
GameObject go = Instantiate(prefab);
// Bind each new GameObject to FYFY
GameObjectManager.bind(go);

// Unbind all GameObject before destroying it
GameObjectManager.unbind(go);
Destroy(go);

// Replace all classic AddComponent calls
go.AddComponent<FYFYComponentExample>();
// with
GameObjectManager.addComponent<FYFYComponentExample>(go);

// Replace all component destruction
FYFYComponentExample fce = go.GetComponent<FYFYComponentExample>();
Destroy(fce);
// with
GameObjectManager.removeComponent<FYFYComponentExample>(go);

// Replace all changes on active state
go.SetActive(false);
// with
GameObjectManager.setGameObjectState(go, false);

// Replace all hierarchical updates
go.transform.SetParent(newParent, true);
// with
GameObjectManager.setGameObjectParent(go, newParent, true);

// Replace all scene loadind
UnityEngine.SceneManagement.SceneManager.LoadScene(sceneName);
// with
GameObjectManager.loadScene(sceneName);

// Replace all DontDestroyOnLoad calls
DontDestroyOnLoad(go);
// with
GameObjectManager.dontDestroyOnLoadAndRebind(go);
```
Muratet Mathieu's avatar
Muratet Mathieu committed
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286

## Access systems’ fields and methods in Inspector ##

When you add a system to the MainLoop, FYFY scans your system and adds automatically a Monobehavior wrapper to your MainLoop. This wrapper exposes serializable fields of your system that enables you to initialize these fields via the Inspector.

Example of fields exposition:

```C#
using FYFY;

public class Syst1 : FSystem {
    public string myParam1;
    public bool myParam2;
    ...
}
```
<p align="center">
  <img src="docs/FYFY_wrapper.PNG" alt="Show how a system is exposed in Inspector through its wrapper" width="400"/>
</p>

If you define inside a system a void public function with a maximum of one parameter, FYFY give you access to this function through its wrapper of the MainLoop to link it on UI events.
<p align="center">
  <img src="docs/UI_Event.PNG" alt="How to link UI event with a function defined inside a system" width="400"/>
</p>