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:
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.
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 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. The AssetManager uses this method if the same spatial is loaded twice. You can specify which fields you want your object to reuse (e.g. collisionshapes) in this case.setEnabled(boolean)
: Enable or disable the control. If disabled, update() does nothing. Goes with accessor isEnabled();
.setSpatial(Spatial s)
, update(float tpf);
, render(RenderManager rm, ViewPort vp)
.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 .... } }
This class can be found under com.jme3.scene.control.AbstractControl
.
enabled
, and a Spatial spatial
. 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(...); } }
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);