-
Notifications
You must be signed in to change notification settings - Fork 2
Example: Writing a New Component
Writing new components is a simple way to add new behaviors to the system that can be exposed to the GUI. No UI changes are necessary to incorporate new components. Make sure the Component is in the root folder of the "Component" subdirectory in the source code. In order to add your new component via GUI, make sure to update the UserComponets.csv file located in the "Assets" directory of the project.
To start, first make sure to inherit from the Component base class. A template has been provided as part of this repository. To use it, select "Component (Cell Mechanics Simulator)" when writing a new script. You can also inherit from another component subclass, such as Mesh or Force. A new component using this template should look something like this:
package Component;
public class MyNewComponent extends Component{
// Start is called once per frame
@Override
public void start(){
}
// Update is called once per physics step
@Override
public void update(){
}
}
Note: The template was made for IntelliJ IDEA, it may not be compatible with other IDEs such as Eclipse. If the template is not available, go to Settings->Editor->File and Code Templates, and make sure the Component template is available. If so, use the apply button to activate it.
The awake method is called when the instance of the behavior is created. If we want to set a default parameter, we can do so here. This method will not be called again, even as the simulation changes states. There is another important use of the awake function, for subscribing to events, which will be detailed later.
package Component;
public class MyNewComponent extends Component{
int foo;
@Override
public void awake(){
foo = 5;
}
}
By declaring a variable as "public", you can edit it in the GUI. If you want changes in this variable to cause immediate recalculations/ changes to other variables, you need to use onValidate, which is called every time a GUI field is changed.
package Component;
public class MyNewComponent extends Component{
int foo;
public int bar; //bar is public and can be modified in the GUI
@Override
public void onValidate(){
foo = bar + 1;
}
}
If you don't need the calculation to happen immediately every time the GUI is called, setup calculations are best run during the start function. This function is run once prior to the update loop beginning.
package Component;
public class MyNewComponent extends Component{
int foo;
public int bar; //bar is public and can be modified in the GUI
@Override
public void start(){
foo = bar + 1;
}
}
To evolve forces over time, use earlyUpdate, update, and lateUpdate. Though technically you can use any of these methods to have a calculation evolve over time, there are some recommendations regarding their usage:
lateUpdate should only be used when directly moving nodes.
earlyUpdate should be used for "corrections", i.e. checking for non-manifold mesh structures/ self-intersection.
update is used for changing physics forces.
package Component;
public class MyNewComponent extends Component{
int foo;
@Override
public void update(){
foo = foo + 1; // foo will increment every step
}
}
If you want something to happen when this component is removed (or its parent object is destroyed), use the onDestroy method
package Component;
public class MyNewComponent extends Component{
@Override
public void onDestroy(){
Debug.Log("I am destroyed"); //Log to console
}
}
Using the events package as part of the repository, it is easy to subscribe and unsubscribe to events that can trigger your code to perform certain actions. It is best to subscribe to events in the awake method and unsubscribe to them in the onDestroy method to prevent memory leaks.
package Component;
public class MyNewComponent extends Component{
@Override
public void awake(){
Foo.onSendInteger.subscribe(this::performWhenEventOccurs);
}
// perform a simple task, in this case our event needs to also require an integer so the types match.
private void performWhenEventOccurs(int i){
Debug.Log(String.valueOf(i));
}
@Override
public void onDestroy(){
Foo.onSendInteger.unsubscribe(this::performWhenEventOccurs);
}
}