A com.jme3.scene.control.Control is a customizable jME3 interface that allows you to cleanly steer the behaviour of game entities (Spatials), such as artificially intelligent behaviour in NPCs, traps, automatic alarms and doors, animals and pets, self-steering vehicles or platforms – anything that moves and interacts. Several instances of customs Controls together implement the behaviours of a type of Spatial.
To control global game behaviour see Application States – you often use AppStates and Control together.
To control the behaviour of spatials:
spatial gives you access to the spatial and subspatials that the control is attached to.spatial's transformation (move, scale, rotate), play animations, check its environement, define how it acts and reacts. spatial.addControl(myControl);
To implement game logic for a type of spatial, you will either extend AbstractControl (most common case), or implement the Control interface, as explained in this article.
Use Controls to implement the behaviour of types of game entities.
update() loop that hooks into simpleUpdate(). Use Controls to move blocks of code out of the simpleUpdate() loop.Examples: You can write
The possibilities are endless.
Other examples include the built-in RigidBodyControl in JME's physics integration, the built-in TerrainLODControl that updates the terrain's level of detail depending on the viewer's perspective, etc.
Existing examples in the code base include:
The AbstractControl can be found under com.jme3.scene.control.AbstractControl. This is a default abstract class that implements the Control interface.
isEnabled().spatial. controlUpdate() method to implement the Spatial's behaviour. setEnabled(boolean) method. This activates or deactivates this Control's behaviour in this spatial temporarily. While the AbstractControl is toggled to be disabled, the controlUpdate() loop is no longer executed.
Usage: Your custom subclass implements the three methods controlUpdate(), controlRender(), setSpatial(), and cloneForSpatial() as shown here:
public class MyControl extends AbstractControl implements Savable, Cloneable { privat int index; // can have custom fields -- example public MyControl(){} // empty serialization constructor /** Optional custom constructor with arguments that can init custom fields. * Note: you cannot modify the spatial here yet! */ public MyControl(int i){ // index=i; // example } /** This is your init method. Optionally, you can modify * the spatial from here (transform it, initialize userdata, etc). */ @Override public void setSpatial(Spatial spatial) { super.setSpatial(spatial); // spatial.setUserData("index", i); // example } /** Implement your spatial's behaviour here. * From here you can modify the scene graph and the spatial * (transform them, get and set userdata, etc). * This loop controls the spatial while the Control is enabled. */ @Override protected void controlUpdate(float tpf){ if(spatial != null) { // spatial.rotate(tpf,tpf,tpf); // example behaviour } } @Override public Control cloneForSpatial(Spatial spatial){ final MyControl control = new MyControl(); /* Optional: use setters to copy userdata into the cloned control */ // control.setIndex(i); // example control.setSpatial(spatial); return control; } @Override protected void controlRender(RenderManager rm, ViewPort vp){ /* Optional: rendering manipulation (for advanced users) */ } @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(...); } }
See also:
setUserData(), see Spatial.
The Control interface can be found under com.jme3.scene.control.Control. It has the following method signatures:
cloneForSpatial(Spatial): Clones the Control and attaches it to a clone of the given Spatial. setEnabled(boolean): Toggles a boolean that enables or disables the Control. Goes with accessor isEnabled();. You test for it in the update(float tpf) loop before you execute anything.setSpatial(Spatial s), update(float tpf);, render(RenderManager rm, ViewPort vp).Usage example: 1. Create a custom control interface
public interface MyControlInterface extends Control { public void setSomething(int x); // optionally, add custom methods }
2. Create custom Controls implementing your Control interface.
public class MyControl extends MyCustomClass implements MyControlInterface { protected Spatial spatial; protected boolean enabled = true; public MyControl() { } // empty serialization constructor public MyControl(int x) { // custom constructor 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 for advanced users, e.g. to display a debug shape } @Override public Control cloneForSpatial(Spatial spatial) { MyControl control = new MyControl(); // set custom properties control.setSpatial(spatial); control.setEnabled(isEnabled()); // set some more properties here... return control; } @Override public void setEnabled(boolean enabled) { this.enabled = enabled; } @Override public boolean isEnabled() { return enabled; } @Override public void setSomething(int z) { // You can add custom methods ... } @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 .... } }
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);