Skip to content

Example: Writing a New Component

jsutlive edited this page May 2, 2023 · 8 revisions

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.

Inheriting from the base class and using the component template

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.

Using the awake method to set an initial value for a variable

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;
     }
}

Using the onValidate method to recalculate parameters after a change in the GUI

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;
     }
}

Using the start method to run calculations at the start of a simulation

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;
     }
}

Using the earlyUpdate, update, and lateUpdate to make changes per physics step

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
     }
}

Using the onDestroy method to make a change when the object is destroyed

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
     }
}

Events and the 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);
     }
}