First of all, let me thank all of you for the wonderful jMonkey SDK and the work that has been done on extending jMonkey to Android. I have found the whole system a joy to use and learn.
I am confused on how connecting a jMonkey application to native Android code should be implemented in an expected manor. From the main Android page (that gets linked to countless times in this forum), I read this:
As an example, if you want to use the phones camera as an image input stream for a texture, you can create e.g. the AppState that manages the image and makes it available to the application inside the main project (no android code is needed). Then in the android part of the code you make a connection to the camera and update the image in the AppState.
The concept of this makes complete sense to me. It is essentially saying that jMonkey shouldn’t care about where the data comes from and that you should only connect native Android code to a “middle man” that connects the Android code to the main code. However, I am confused by the implementation of this. It is my understanding that AppStates are for when you want the jMonkey game to jump between different states like a main menu, a paused state, or a game running state. Why is this “middle man” class an AppState?
My particular project involves retrieving live GPS data and saving it while the game is going on while also having a character on the screen update based on the user’s location. In this case, it seems to me that the GPS reading needs to either run in its own thread or be used in a callback fashion such as updating the game screen when a new GPS reading comes in. If this is the case, and the “middle man” class is an AppState, doesn’t that break? From what I understand, only one AppState can be running at a time and in the main thread you attach/detach the AppStates as appropriate. Since I want to be gather GPS data constantly, it seems like making the “middle man” class an AppState defeats the purpose (but I also realize I am probably WAY off base in my way of thinking).
Any help or insight that could be provided would be greatly appreciated.
P-Worm
Anyway, this is what I have right now concerning the GPS:
Main
[java]import com.jme3.app.SimpleApplication;
import com.jme3.niftygui.NiftyJmeDisplay;
import com.jme3.system.AppSettings;
import de.lessvoid.nifty.Nifty;
import de.lessvoid.nifty.NiftyEventSubscriber;
import de.lessvoid.nifty.controls.CheckBoxStateChangedEvent;
import de.lessvoid.nifty.controls.Label;
import de.lessvoid.nifty.controls.ListBox;
import de.lessvoid.nifty.controls.TextField;
import de.lessvoid.nifty.elements.render.TextRenderer;
import de.lessvoid.nifty.screen.Screen;
import de.lessvoid.nifty.screen.ScreenController;
import de.lessvoid.nifty.tools.SizeValue;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Main extends SimpleApplication implements ScreenController {
private Nifty nifty;
private BlankState blankState;
private GraphicsState graphicsState;
private LocationStream locationStream;
// Used to test out label updating in GUI
private int switchScreenCounter;
// Flags for whether the program is logging data
boolean loggerRunning;
public static void main(String[] args) {
Main app = new Main();
Logger.getLogger("").setLevel(Level.SEVERE);
app.setDisplayStatView(false);
app.setShowSettings(false);
AppSettings newSettings = new AppSettings(true);
newSettings.setResolution(480, 854);
newSettings.setFrameRate(60);
app.setSettings(newSettings);
app.start();
}
// Connections for GUI
public void bind(Nifty nifty, Screen screen) {
System.out.println("bindFromMain( " + screen.getScreenId() + ")");
}
public void onStartScreen() {
System.out.println("onStartScreen Main");
}
public void onEndScreen() {
System.out.println("onEndScreen Main");
}
public void quit() {
nifty.gotoScreen("end");
}
/*
* GUI methods
*/
// Switch states and GUI screens
public void switchScreen(String screenName) {
if (screenName.equals("graphics")) {
System.out.println("Switching state to graphics");
graphicsState.setEnabled(true);
stateManager.detach(blankState);
stateManager.attach(graphicsState);
nifty.gotoScreen(screenName);
} else {
System.out.println("Switching state to blank state");
graphicsState.setEnabled(false);
stateManager.detach(graphicsState);
stateManager.attach(blankState);
nifty.gotoScreen(screenName);
}
switchScreenCounter++;
String message;
message = "Number of screen switches: " + Integer.toString(switchScreenCounter);
message += " \nplus here's a bunch of \ntext to make sure that \nthe words rollover to \nnew lines when they need to.";
if (screenName.equals("connection")) {
updateLabel("checkConnOutput", message);
}
}
// Changes a label in the GUI
private void updateLabel(String labelId, String message) {
Label mLabel = nifty.getCurrentScreen().findNiftyControl(labelId, Label.class);
mLabel.setText(message);
// As Nifty currently stands, the width of the displayed text does not
// dynamically change to accomadate larger strings than what is first
// rendered.
TextRenderer mRenderer = mLabel.getElement().getRenderer(TextRenderer.class);
mLabel.setWidth(new SizeValue(mRenderer.getTextWidth() + "px"));
// Update the panel
mLabel.getElement().getParent().layoutElements();
nifty.getCurrentScreen().layoutLayers();
}
// Add an item to the list box and remove the oldest item if the box is
// currently full
private void addItemToLogger(String newItem) {
ListBox listBox = nifty.getCurrentScreen().findNiftyControl("loggerListBox", ListBox.class);
int numItems = listBox.itemCount();
// The list box can only handle 15 items. If the box is already full,
// pop out the oldest item and add the newest item
if (numItems == 15) {
listBox.removeItemByIndex(0);
}
listBox.addItem("\t"+newItem);
}
// After the user presses the Check Connections button, run through
// connection tests
public void checkConnections() {
System.out.println("Pressed check connections");
updateLabel("checkConnOutput", "Button pushed!");
updateLabel("internetSsidOutput", "Button pushed!");
updateLabel("mocapSsidOutput", "Button pushed!");
updateLabel("localIp", "Button pushed!");
updateLabel("connectionStatus", "Button pushed!");
}
@NiftyEventSubscriber(id="gCheckBox")
public void gCheckBoxToggled(String id, CheckBoxStateChangedEvent event) {
if (event.isChecked()) {
updateLabel("checkConnOutput", "3G checkbox is checked!");
} else {
updateLabel("checkConnOutput", "3G checkbox is unchecked!");
}
}
@NiftyEventSubscriber(id="wifiCheckBox")
public void wifiCheckBoxToggled(String id, CheckBoxStateChangedEvent event) {
if (event.isChecked()) {
updateLabel("checkConnOutput", "Wifi checkbox is checked!");
} else {
updateLabel("checkConnOutput", "Wifi checkbox is checked!");
}
}
// Start capturing the mocap data
public void startCapturingData() {
// Set the timer to 0
timer.reset();
loggerRunning = !loggerRunning;
// Read in the name for the output file
TextField filenameTextField = nifty.getCurrentScreen().findNiftyControl("filenameInput", TextField.class);
String filename = filenameTextField.getText();
updateLabel("connectedIpLabel", filename);
updateLabel("gpsData1", "1");
updateLabel("gpsData2", "2");
updateLabel("gpsData3", "3");
updateLabel("gpsData4", "4");
}
// Send an email with the data as an attachment
public void sendEmail() {
// Read info from the email field
TextField emailTextField = nifty.getCurrentScreen().findNiftyControl("emailEntry", TextField.class);
String emailAddress = emailTextField.getText();
TextField filenameTextField = nifty.getCurrentScreen().findNiftyControl("filenameInput", TextField.class);
String filename = filenameTextField.getText();
float timeInSec = timer.getTimeInSeconds();
addItemToLogger(String.format("%.2f: ", timeInSec) + filename);
updateLabel("connectedIpLabel", filename);
updateLabel("gpsData1", "1");
updateLabel("gpsData2", "2");
updateLabel("gpsData3", "3");
updateLabel("gpsData4", "4");
}
// Updates the live time logger
private void updateLoggerTimeDisplay() {
float timeInSec = timer.getTimeInSeconds();
String timeOutput;
timeOutput = String.format("%.2f", timeInSec);
updateLabel("elapsedTime", timeOutput);
}
@Override
public void simpleInitApp() {
blankState = new BlankState(this);
graphicsState = new GraphicsState(this);
locationStream = new LocationStream(this);
loggerRunning = false;
// Setup the GUI
NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager,
inputManager,
audioRenderer,
guiViewPort);
nifty = niftyDisplay.getNifty();
nifty.fromXml("Interface/settingsScreens.xml", "connection", this);
nifty.addXml("Interface/graphicsScreen.xml");
guiViewPort.addProcessor(niftyDisplay);
inputManager.setCursorVisible(true);
System.out.println("Initialized Main");
stateManager.attach(blankState);
}
/*
* Override the main update loop
*/
@Override
public void simpleUpdate(float tpf) {
// Only update the display if the timer is running and we are on that
// particular screen
if (loggerRunning && nifty.getCurrentScreen() == nifty.getScreen("logger")) {
updateLoggerTimeDisplay();
}
}
}[/java]
LocationStream which is my “middle man” class
[java] import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.audio.AudioRenderer;
import com.jme3.input.InputManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
public class LocationStream extends AbstractAppState {
public SimpleApplication app;
private Node rootNode = new Node("Root Node");
private AssetManager assetManager;
private InputManager inputManager;
private AudioRenderer audioRenderer;
private ViewPort guiViewPort;
private AppStateManager stateManager;
// Will be continuously updated to the location of the device
Location location;
public LocationStream(SimpleApplication app) {
this.assetManager = app.getAssetManager();
this.inputManager = app.getInputManager();
this.audioRenderer = app.getAudioRenderer();
this.guiViewPort = app.getGuiViewPort();
this.stateManager = app.getStateManager();
location = new Location();
}
public Location getLocation() {
return location;
}
public void setLocation(float longitude, float latitude, int altitude) {
location.setLocation(longitude, latitude, altitude);
}
@Override
public void initialize(AppStateManager stateManager, Application app) {
super.initialize(stateManager, app);
this.app = (SimpleApplication)app;
System.out.println("Initialized: app is "+this.app);
}
@Override
public void update(float tpf) {
super.update(tpf);
rootNode.updateLogicalState(tpf);
rootNode.updateGeometricState();
}
}
[/java]