I created just for fun an example. It is except “FadeAppState”, which I use in my own project, untested and not strain-forward but very object oriented (polymorphic and reusable), it may helps and if not, it doesn’t matter. It use some trivial classes from my own apis. Please note, the callback of the load-Method is called in the loading thread.
The Callback of FadeAppState is either called if the fading is done or interrupted by calling one of the fade-Methods again, whatever occurs first. So you must be sure, the fading is not interrupted by another call.
private void load(final String levelConfig, final Runnable callback) {
final FilterPostProcessor fpp = new FilterPostProcessor(getAssetManager());
getViewPort().addProcessor(fpp);
final FadeFilter fade = new FadeFilter();
//fade.setValue(0.0f); // To initial fade the screen out on initializing.
fpp.addFilter(fade);
getStateManager().attach(new FadeAppState(fade));
// ^ Prerequise, reuse until here
final Spatial loadScreenParent = createLoadScreen();
// ^ Reuse, if you always use the same loading screen
final FadeAppState state = getStateManager().getState(FadeAppState.class);
state.fadeOut(new MultiRunnable(
new DeactivateInputsRunnable(), // <- Implement as you want
new AttachToNodeEnqueuedRunnable(app, getGuiNode(), loadScreenParent),
new StartThreadRunnable(new MultiRunnable(
new LoadLevelRunnable(app, levelConfig), // <- Implement as you want (app is for enqueueing, you could also create a parent node for the level and add them via in seperated classes)
new DetachToNodeEnqueuedRunnable(app, guiNode, loadScreenParent),
new FadeAppStateInRunnable(
state,
new ActivateInputsRunnable(), // <- Implement as you want,
callback
)
))
));
}
public final class FadeAppState implements AppState {
private final FadeFilter filter;
private boolean initialized;
private boolean enabled;
private boolean awaitListener;
private float listenerTarget;
private Runnable listener;
public FadeAppState(final FadeFilter filter) {
this.filter = Requires.notNull(filter, "filter == null");
this.enabled = true;
}
@Override
public void update(final float tpf) {
if(awaitListener) {
if(filter.getValue() == listenerTarget) {
listener.run();
listener = new NullRunnable();
awaitListener = false;
}
}
}
public void fadeIn(final Runnable l) {
Requires.notNull(l, "l == null");
if(awaitListener) {
listener.run();
} else {
awaitListener = true;
}
listenerTarget = 1.0f;
listener = l;
filter.fadeIn();
}
public void fadeOut(final Runnable l) {
Requires.notNull(l, "l == null");
if(awaitListener) {
listener.run();
} else {
awaitListener = true;
}
listenerTarget = 0.0f;
listener = l;
filter.fadeOut();
}
public void setDuration(final float duration) {
filter.setDuration(duration);
}
@Override
public void initialize(final AppStateManager stateManager, final Application app) {
initialized = true;
}
@Override
public boolean isInitialized() {
return(initialized);
}
@Override
public String getId() {
return(null);
}
@Override
public void setEnabled(final boolean active) {
this.enabled = active;
}
@Override
public boolean isEnabled() {
return(enabled);
}
@Override
public void stateAttached(final AppStateManager stateManager) {
}
@Override
public void stateDetached(final AppStateManager stateManager) {
}
@Override
public void render(final RenderManager rm) {
}
@Override
public void postRender() {
}
@Override
public void cleanup() {
}
}
public final class AttachToNodeEnqueuedRunnable implements Runnable {
private final Application app;
private final Node parent;
private final Spatial toAttach;
public AttachToNodeEnqueuedRunnable(final Application app, final Node parent, final Spatial toAttach) {
this.app = Requires.notNull(app, "app == null");
this.parent = Requires.notNull(parent, "parent == null");
this.toAttach = Requires.notNull(toAttach, "toAttach == null");
}
@Override
public void run() {
app.enqueue(new Runnable() {
@Override
public void run() {
parent.attachChild(toAttach);
}
});
}
}
public final class DetachToNodeEnqueuedRunnable implements Runnable {
private final Application app;
private final Node parent;
private final Spatial toDetach;
public DetachToNodeEnqueuedRunnable(final Application app, final Node parent, final Spatial toDetach) {
this.app = Requires.notNull(app, "app == null");
this.parent = Requires.notNull(parent, "parent == null");
this.toDetach = Requires.notNull(toDetach, "toDetach == null");
}
@Override
public void run() {
app.enqueue(new Runnable() {
@Override
public void run() {
parent.detachChild(toDetach);
}
});
}
}
public final class FadeAppStateInRunnable implements Runnable {
private final FadeAppState state;
private final Runnable callback;
public FadeAppStateInRunnable(final FadeAppState state) {
this(state, new NullRunnable());
}
public FadeAppStateInRunnable(final FadeAppState state, final Runnable callback) {
this.state = Requires.notNull(state, "state == null");
this.callback = Requires.notNull(callback, "callback == null");
}
@Override
public void run() {
state.fadeIn(callback);
}
}
public final class StartThreadRunnable implements Runnable {
private final Runnable parent;
public StartThreadRunnable(final Runnable parent) { // <- May add a model containing informations like is a deamon or priority
this.parent = Requires.notNull(parent, "parent == null");
}
@Override
public void run() {
new Thread(parent).start(); // <- May change to non-native thread implement
}
}