Custom Controls

A com.jme3.scene.control.Control is a customizable jME3 interface that allows you to cleanly implement game logic, such as game rules, or artificially intelligent behaviour in NPCs. You use Controls to control the behaviour of types of spatials. To control global game behaviour see Application States – you can use both together. To control the behaviour of types of entities:

  1. Create one control for each type of behavior. When you add several controls to one spatial, they will be executed in the order they were added.
    For example, an NPC can be controlled by a PhysicsControl and an AIControl.
  2. You define a custom control and implement its behaviour in the Control's update() method.
    • In the control, you can pass arguments and manipulate the spatial in any way: Modify its transformation (move, scale, rotate), play animations, check for enemies around it and react, etc.
  3. Add the control to a spatial and the Spatial's game state is updated automatically from now on.
    spatial.addControl(myControl)

To implement game logic for a type of spatial, you will either extend AbstractControl, or implement the Control interface, as explained in this article.

Usage Examples

For example, you could write a CharacterAnimControl that animates a character accordingly while it is being moved by a CharacterControl. Or you can write an AIControl that remote-controls NPC behaviour in fight situatons. Or you could write a DestructionControl that automatically replaces a structure with an appropriate piece of debris after collision with a projectile… The possibilities are endless. :-) Existing examples in the code base include:

The Control Interface

The interface can be found under com.jme3.scene.control.Control. It has the following method signatures:

If you want to create a Control that also extends an existing class, then create a custom extension of the Control Interface. Usage example: 1. Create a custom control interface

public interface MyControl extends Control {
    public void setSomething(int x); // add your custom methods
}

2. Create custom classes implementing your control interface

public class ControlledThing extends MyThing implements MyControl {
    protected Spatial spatial;
    protected boolean enabled = true;
    public ControlledThing() { }
    public ControlledThing(int x) {
        super(x);
    }
    @Override
    public void update(float tpf) {
        if (enabled && spatial != null) {
            // ...
            // Write custom code to control the spatial here!
        }
    }
    @Override
    public void render(RenderManager rm, ViewPort vp) {
        // optional, e.g. to display a debug shape
    }
    @Override
    public Control cloneForSpatial(Spatial spatial) {
        ControlledThing control = new ControlledThing(y);
        // ...
        spatial.setControl(control);
        return control;
    }
    @Override
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
        // ...
    }
    @Override
    public boolean isEnabled() {
        return enabled;
    }
    @Override
    public void setSomething(int z) {
        // your custom method ...
    }
    @Override
    public void write(JmeExporter ex) throws IOException {
        super.write(ex);
        OutputCapsule oc = ex.getCapsule(this);
        oc.write(enabled, "enabled", true);
        oc.write(spatial, "spatial", null);
        // write custom variables ....
    }
    @Override
    public void read(JmeImporter im) throws IOException {
        super.read(im);
        InputCapsule ic = im.getCapsule(this);
        enabled = ic.readBoolean("enabled", true);
        spatial = (Spatial) ic.readSavable("spatial", null);
        // read custom variables ....
    }
}

AbstractControl

This class can be found under com.jme3.scene.control.AbstractControl.

Usage: Your custom subclass must implement the three methods controlUpdate(), controlRender(), and cloneForSpatial() as shown here:

public class MyControl extends AbstractControl implements Savable, Cloneable {
  private Thing thing; // some custom class of yours
  public MyControl(){} // empty serialization constructor
  public MyControl(thing) { // some custom constructor
    super(spatial);
    this.thing = thing;
  }
  @Override
  protected void controlUpdate(float tpf){
    if(spatial != null && thing != null) {
      // Implement your custom control here ...
    }
  }
  @Override
  protected void controlRender(RenderManager rm, ViewPort vp){
     // ... optional
  }
  @Override
  public Control cloneForSpatial(Spatial spatial){
    final MyControl control = new MyControl(...);
    spatial.setControl(control);
    return control;
  }
  @Override
  public void read(JmeImporter im) throws IOException {
      super.read(im);
      // im.getCapsule(this).read(...);
  }
  @Override
  public void write(JmeExporter ex) throws IOException {
      super.write(ex);
      // ex.getCapsule(this).write(...);
  }
}

Best Practices

Tip: Use the getControl() accessor to get Control objects from Spatials. No need to pass around lots of object references. Here an example from the MonkeyZone code:

public class CharacterAnimControl implements Control {
  ...
  public void setSpatial(Spatial spatial) {
    ...
    animControl      = spatial.getControl(AnimControl.class);
    characterControl = spatial.getControl(CharacterControl.class);
    ...
  }
}

Tip: You can create custom Control interfaces so a set of different Controls provide the same methods and can be accessed with the interface class type.

public interface ManualControl extends Control {
    public void steerX(float value);
    public void steerY(float value);
    public void moveX(float value);
    public void moveY(float value);
    public void moveZ(float value);
   ...
}

Then you create custom sub-Controls and implement the methods accordingly to the context:

public class ManualVehicleControl   extends ManualControl {...}

and

public class ManualCharacterControl extends ManualControl {...}

Then add the appropriate controls to spatials:

characterSpatial.addControl(new ManualCharacterControl());
...
vehicleSpatial.addControl(new ManualVehicleControl());
...

Tip: Use the getControl() method on a Spatial to get a specific Control object, and activate its behaviour!

ManualControl c = mySpatial.getControl(ManualControl.class);
c.steerX(steerX);

view online version