@wezrule @nehon @Momoko_Fan and others
I just committed sensor support that sends the data to the application as a simulated joystick. The values returned in onAnalog should match what is sent when an actual joystick is used on the PC version of the application.
I only added orientation for now. If it works as everyone likes for orientation, I’ll add more sensors.
I’ll wait a couple of days to see if anyone has any major issues with this version and then I’ll create a new post to announce the support.
In MainActivity, you need to add the following line to enable sensors:
[java]
joystickEventsEnabled = true;
[/java]
My test project is below. Allows for setting an absolute rotation on an object or an incremental rotation based on the device orientation.
[java]
package mygame;
import com.jme3.app.SimpleApplication;
import com.jme3.input.Joystick;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Line;
import com.jme3.system.AppSettings;
import com.jme3.texture.Texture;
import com.jme3.util.IntMap;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Main extends SimpleApplication implements ActionListener, AnalogListener {
private static final Logger logger = Logger.getLogger(Main.class.getName());
private Geometry geomZero = null;
// variables to save the current rotation
private float[] anglesCurrent = new float[] {0f, 0f, 0f};
private float[] anglesCalibrated = new float[] {0f, 0f, 0f};
private Quaternion rotationQuat = new Quaternion();
// switch to use to apply an absolute rotation (geometry.setLocalRotation) or
// an incremental constant rotation (geometry.rotate)
private boolean useAbsolute = true;
// rotation speed to use when apply constant incremental rotation
private float rotationSpeedX = 1f;
private float rotationSpeedY = 1f;
// flag to record the starting orientation when using absolute rotations
private boolean recordCalibration = false;
// Map of joysticks saved with the joyId as the key
private IntMap<Joystick> joystickMap = new IntMap<Joystick>();
// name of joystick when Android is using device orientation
private final String ORIENTATION_SENSOR = “Device Orientation”;
// mappings used for onAnalog
private final String ORIENTATION_X_PLUS = “Orientation_X_Plus”;
private final String ORIENTATION_X_MINUS = “Orientation_X_Minus”;
private final String ORIENTATION_Y_PLUS = “Orientation_Y_Plus”;
private final String ORIENTATION_Y_MINUS = “Orientation_Y_Minus”;
// Make sure to set joystickEventsEnabled = true in MainActivity for Android
public static void main(String[] args) {
Main app = new Main();
AppSettings settings = new AppSettings(true);
settings.setUseJoysticks(true);
app.setSettings(settings);
app.start();
}
private float toDegrees(float rad) {
return rad*FastMath.RAD_TO_DEG;
}
@Override
public void simpleInitApp() {
flyCam.setDragToRotate(true);
// flyCam also uses joysticks to rotate the camera, so disable it for
// this example
flyCam.setEnabled(false);
Mesh lineX = new Line(Vector3f.ZERO, Vector3f.ZERO.add(Vector3f.UNIT_X.mult(3)));
Mesh lineY = new Line(Vector3f.ZERO, Vector3f.ZERO.add(Vector3f.UNIT_Y.mult(3)));
Mesh lineZ = new Line(Vector3f.ZERO, Vector3f.ZERO.add(Vector3f.UNIT_Z.mult(3)));
lineX.setLineWidth(2);
lineX.setLineWidth(2);
lineX.setLineWidth(2);
Geometry geoX = new Geometry(“X”, lineX);
Material matX = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
matX.setColor(“Color”, ColorRGBA.Red);
geoX.setMaterial(matX);
rootNode.attachChild(geoX);
Geometry geoY = new Geometry(“Y”, lineY);
Material matY = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
matY.setColor(“Color”, ColorRGBA.Green);
geoY.setMaterial(matY);
rootNode.attachChild(geoY);
Geometry geoZ = new Geometry(“Z”, lineZ);
Material matZ = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
matZ.setColor(“Color”, ColorRGBA.Blue);
geoZ.setMaterial(matZ);
rootNode.attachChild(geoZ);
Box b = new Box(Vector3f.ZERO, 1, 1, 1);
geomZero = new Geometry(“Box”, b);
Material mat = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
mat.setColor(“Color”, ColorRGBA.Yellow);
Texture tex_ml = assetManager.loadTexture(“Interface/Logo/Monkey.jpg”);
mat.setTexture(“ColorMap”, tex_ml);
geomZero.setMaterial(mat);
geomZero.setLocalTranslation(Vector3f.ZERO);
geomZero.setLocalRotation(Quaternion.IDENTITY);
rootNode.attachChild(geomZero);
// Touch (aka MouseInput.BUTTON_LEFT) is used to record the starting
// orientation when using absolute rotations
inputManager.addMapping(“MouseClick”, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
inputManager.addListener(this, “MouseClick”);
Joystick[] joysticks = inputManager.getJoysticks();
if (joysticks == null || joysticks.length < 1) {
logger.log(Level.INFO, “Cannot find any joysticks!”);
} else {
// Android Orientation is defined as the first joystick (joystick[0])
// so it can use the same mapping as the PC version of the app if
// a single actual joystick is connected.
// Joysticks return a value of 0 to 1 based on how far the stick is
// push on the axis. This value is then scaled based on how long
// during the frame the joystick axis has been in that position.
// If the joystick is push all the way for the whole frame,
// then the value in onAnalog is equal to tpf.
// If the joystick is push 1/2 way for the entire frame, then the
// onAnalog value is 1/2 tpf.
// Similarly, if the joystick is pushed to the maximum during a frame
// the value in onAnalog will also be scaled.
// For Android sensors, rotating the device 90deg is the same as
// pushing an actual joystick axis to the maximum.
logger.log(Level.INFO, “Number of joysticks: {0}”, joysticks.length);
// Joystick joystick = joysticks[0];
for (Joystick joystick: joysticks) {
// You could do a custom rotation speed and mappings based
// on using the device orientation vs an acutal joystick
if (joystick.getName().equalsIgnoreCase(ORIENTATION_SENSOR)) {
// Orientation Sensor found
rotationSpeedX = 2f;
rotationSpeedY = 2f;
logger.log(Level.INFO, “Found {0} Joystick, assigning mapping for X axis: {1}”,
new Object[]{joystick.toString(), joystick.getXAxisIndex()});
joystick.assignAxis(ORIENTATION_X_PLUS, ORIENTATION_X_MINUS, joystick.getXAxisIndex());
inputManager.addListener(this, ORIENTATION_X_PLUS, ORIENTATION_X_MINUS);
joystickMap.put(joystick.getJoyId(), joystick);
logger.log(Level.INFO, “Found {0} Joystick, assigning mapping for Y axis: {1}”,
new Object[]{ORIENTATION_SENSOR, joystick.getYAxisIndex()});
joystick.assignAxis(ORIENTATION_Y_PLUS, ORIENTATION_Y_MINUS, joystick.getYAxisIndex());
inputManager.addListener(this, ORIENTATION_Y_PLUS, ORIENTATION_Y_MINUS);
joystickMap.put(joystick.getJoyId(), joystick);
// adjust the deadzone for joysticks, if desired.
inputManager.setAxisDeadZone(0.003f);
} else {
// Normal joystick being used
rotationSpeedX = 1f;
rotationSpeedY = 1f;
logger.log(Level.INFO, “Found {0} Joystick, assigning mapping for X axis: {1}”,
new Object[]{joystick.toString(), joystick.getXAxisIndex()});
joystick.assignAxis(ORIENTATION_X_PLUS, ORIENTATION_X_MINUS, joystick.getXAxisIndex());
inputManager.addListener(this, ORIENTATION_X_PLUS, ORIENTATION_X_MINUS);
joystickMap.put(joystick.getJoyId(), joystick);
logger.log(Level.INFO, “Found {0} Joystick, assigning mapping for Y axis: {1}”,
new Object[]{ORIENTATION_SENSOR, joystick.getYAxisIndex()});
joystick.assignAxis(ORIENTATION_Y_PLUS, ORIENTATION_Y_MINUS, joystick.getYAxisIndex());
inputManager.addListener(this, ORIENTATION_Y_PLUS, ORIENTATION_Y_MINUS);
joystickMap.put(joystick.getJoyId(), joystick);
// adjust the deadzone for joysticks, if desired.
inputManager.setAxisDeadZone(0.05f);
}
}
}
}
@Override
public void simpleUpdate(float tpf) {
if (recordCalibration) {
logger.log(Level.INFO, “Resetting orientation back to zero”);
if (useAbsolute) {
anglesCalibrated[0] = anglesCalibrated[0] + anglesCurrent[0];
anglesCalibrated[1] = anglesCalibrated[1] + anglesCurrent[1];
anglesCalibrated[2] = anglesCalibrated[2] + anglesCurrent[2];
}
anglesCurrent[0] = anglesCurrent[1] = anglesCurrent[2] = 0f;
geomZero.setLocalRotation(Quaternion.IDENTITY);
recordCalibration = false;
} else {
rotationQuat.fromAngles(anglesCurrent);
rotationQuat.normalizeLocal();
if (useAbsolute) {
geomZero.setLocalRotation(rotationQuat);
} else {
geomZero.rotate(rotationQuat);
}
anglesCurrent[0] = anglesCurrent[1] = anglesCurrent[2] = 0f;
}
}
public void onAction(String string, boolean pressed, float tpf) {
if (string.equalsIgnoreCase(“MouseClick”) && pressed) {
recordCalibration = true;
}
}
public void onAnalog(String string, float value, float tpf) {
logger.log(Level.INFO, “onAnalog for {0}, value: {1}, tpf: {2}”,
new Object[]{string, value, tpf});
float scaledValue = value;
if (string.equalsIgnoreCase(ORIENTATION_X_PLUS)) {
if (useAbsolute) {
// set rotation amount
// divide by tpf to get back to actual axis value (0 to 1)
// multiply by 90deg so that 90deg = full axis (value = tpf)
anglesCurrent[0] = (scaledValue / tpf * FastMath.HALF_PI) - anglesCalibrated[0];
} else {
// apply an incremental rotation amount based on rotationSpeed
anglesCurrent[0] += scaledValue * rotationSpeedX;
}
}
if (string.equalsIgnoreCase(ORIENTATION_X_MINUS)) {
if (useAbsolute) {
// set rotation amount
// divide by tpf to get back to actual axis value (0 to 1)
// multiply by 90deg so that 90deg = full axis (value = tpf)
anglesCurrent[0] = (-scaledValue / tpf * FastMath.HALF_PI) - anglesCalibrated[0];
} else {
// apply an incremental rotation amount based on rotationSpeed
anglesCurrent[0] -= scaledValue * rotationSpeedX;
}
}
if (string.equalsIgnoreCase(ORIENTATION_Y_PLUS)) {
if (useAbsolute) {
// set rotation amount
// divide by tpf to get back to actual axis value (0 to 1)
// multiply by 90deg so that 90deg = full axis (value = tpf)
anglesCurrent[1] = (scaledValue / tpf * FastMath.HALF_PI) - anglesCalibrated[1];
} else {
// apply an incremental rotation amount based on rotationSpeed
anglesCurrent[1] += scaledValue * rotationSpeedY;
}
}
if (string.equalsIgnoreCase(ORIENTATION_Y_MINUS)) {
if (useAbsolute) {
// set rotation amount
// divide by tpf to get back to actual axis value (0 to 1)
// multiply by 90deg so that 90deg = full axis (value = tpf)
anglesCurrent[1] = (-scaledValue / tpf * FastMath.HALF_PI) - anglesCalibrated[1];
} else {
// apply an incremental rotation amount based on rotationSpeed
anglesCurrent[1] -= scaledValue * rotationSpeedY;
}
}
}
}
[/java]