Weiter gearbeitet an allem möglichen
This commit is contained in:
420
com/jme3/app/SimpleApplication.java
Normal file
420
com/jme3/app/SimpleApplication.java
Normal file
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2025 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.app;
|
||||
|
||||
import com.jme3.app.state.AppState;
|
||||
import com.jme3.app.state.ConstantVerifierState;
|
||||
import com.jme3.audio.AudioListenerState;
|
||||
import com.jme3.font.BitmapFont;
|
||||
import com.jme3.font.BitmapText;
|
||||
import com.jme3.input.FlyByCamera;
|
||||
import com.jme3.input.KeyInput;
|
||||
import com.jme3.input.controls.ActionListener;
|
||||
import com.jme3.input.controls.KeyTrigger;
|
||||
import com.jme3.profile.AppStep;
|
||||
import com.jme3.renderer.RenderManager;
|
||||
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.scene.Spatial.CullHint;
|
||||
import com.jme3.scene.threadwarden.SceneGraphThreadWarden;
|
||||
import com.jme3.system.AppSettings;
|
||||
import com.jme3.system.JmeContext.Type;
|
||||
import com.jme3.system.JmeSystem;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* `SimpleApplication` is the foundational base class for all jMonkeyEngine 3 (jME3) applications.
|
||||
* It provides a streamlined setup for common game development tasks, including scene management,
|
||||
* camera controls, and performance monitoring.
|
||||
*
|
||||
* <p>By default, `SimpleApplication` attaches several essential {@link com.jme3.app.state.AppState} instances:
|
||||
* <ul>
|
||||
* <li>{@link com.jme3.app.StatsAppState}: Displays real-time frames-per-second (FPS) and
|
||||
* detailed performance statistics on-screen.</li>
|
||||
* <li>{@link com.jme3.app.FlyCamAppState}: Provides a convenient first-person fly-by camera
|
||||
* controller, allowing easy navigation within the scene.</li>
|
||||
* <li>{@link com.jme3.audio.AudioListenerState}: Manages the audio listener, essential for 3D sound.</li>
|
||||
* <li>{@link com.jme3.app.DebugKeysAppState}: Enables debug functionalities like displaying
|
||||
* camera position and memory usage in the console.</li>
|
||||
* <li>{@link com.jme3.app.state.ConstantVerifierState}: A utility state for verifying constant
|
||||
* values, primarily for internal engine debugging.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p><b>Default Key Bindings:</b></p>
|
||||
* <ul>
|
||||
* <li><b>Esc:</b> Closes and exits the application.</li>
|
||||
* <li><b>F5:</b> Toggles the visibility of the statistics view (FPS and debug stats).</li>
|
||||
* <li><b>C:</b> Prints the current camera position and rotation to the console.</li>
|
||||
* <li><b>M:</b> Prints memory usage statistics to the console.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Applications extending `SimpleApplication` should implement the
|
||||
* {@link #simpleInitApp()} method to set up their initial scene and game logic.
|
||||
*/
|
||||
public abstract class SimpleApplication extends LegacyApplication {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(SimpleApplication.class.getName());
|
||||
|
||||
public static final String INPUT_MAPPING_EXIT = "SIMPLEAPP_Exit";
|
||||
public static final String INPUT_MAPPING_CAMERA_POS = DebugKeysAppState.INPUT_MAPPING_CAMERA_POS;
|
||||
public static final String INPUT_MAPPING_MEMORY = DebugKeysAppState.INPUT_MAPPING_MEMORY;
|
||||
public static final String INPUT_MAPPING_HIDE_STATS = "SIMPLEAPP_HideStats";
|
||||
|
||||
protected Node rootNode = new Node("Root Node");
|
||||
protected Node guiNode = new Node("Gui Node");
|
||||
protected BitmapText fpsText;
|
||||
protected BitmapFont guiFont;
|
||||
protected FlyByCamera flyCam;
|
||||
protected boolean showSettings = true;
|
||||
private final AppActionListener actionListener = new AppActionListener();
|
||||
|
||||
private class AppActionListener implements ActionListener {
|
||||
|
||||
@Override
|
||||
public void onAction(String name, boolean isPressed, float tpf) {
|
||||
if (!isPressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (name.equals(INPUT_MAPPING_EXIT)) {
|
||||
stop();
|
||||
} else if (name.equals(INPUT_MAPPING_HIDE_STATS)) {
|
||||
StatsAppState statsState = stateManager.getState(StatsAppState.class);
|
||||
if (statsState != null) {
|
||||
statsState.toggleStats();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a `SimpleApplication` with a predefined set of default
|
||||
* {@link com.jme3.app.state.AppState} instances.
|
||||
* These states provide common functionalities like statistics display,
|
||||
* fly camera control, audio listener, debug keys, and constant verification.
|
||||
*/
|
||||
public SimpleApplication() {
|
||||
this(new StatsAppState(),
|
||||
new FlyCamAppState(),
|
||||
new AudioListenerState(),
|
||||
new DebugKeysAppState(),
|
||||
new ConstantVerifierState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a `SimpleApplication` with a custom array of initial
|
||||
* {@link com.jme3.app.state.AppState} instances.
|
||||
*
|
||||
* @param initialStates An array of `AppState` instances to be attached
|
||||
* to the `stateManager` upon initialization.
|
||||
*/
|
||||
public SimpleApplication(AppState... initialStates) {
|
||||
super(initialStates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
// set some default settings in-case
|
||||
// settings dialog is not shown
|
||||
boolean loadSettings = false;
|
||||
if (settings == null) {
|
||||
logger.log(Level.INFO, "AppSettings not set, creating default settings.");
|
||||
setSettings(new AppSettings(true));
|
||||
loadSettings = true;
|
||||
}
|
||||
|
||||
// show settings dialog
|
||||
if (showSettings) {
|
||||
if (!JmeSystem.showSettingsDialog(settings, loadSettings)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
//re-setting settings they can have been merged from the registry.
|
||||
setSettings(settings);
|
||||
super.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current speed multiplier of the application.
|
||||
* This value affects how quickly the game world updates relative to real time.
|
||||
* A value of 1.0f means normal speed, 0.5f means half speed, 2.0f means double speed.
|
||||
*
|
||||
* @return The current speed of the application.
|
||||
*/
|
||||
public float getSpeed() {
|
||||
return speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the application's speed multiplier.
|
||||
* A `speed` of 0.0f effectively pauses the application's update cycle.
|
||||
*
|
||||
* @param speed The desired speed multiplier. A value of 1.0f is normal speed.
|
||||
* Must be non-negative.
|
||||
*/
|
||||
public void setSpeed(float speed) {
|
||||
this.speed = speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the `FlyByCamera` instance associated with this application.
|
||||
* This camera allows free-form navigation within the 3D scene.
|
||||
*
|
||||
* @return The `FlyByCamera` object, or `null` if `FlyCamAppState` is not attached
|
||||
* or has not yet initialized the camera.
|
||||
*/
|
||||
public FlyByCamera getFlyByCamera() {
|
||||
return flyCam;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the `Node` dedicated to 2D graphical user interface (GUI) elements.
|
||||
* Objects attached to this node are rendered on top of the 3D scene,
|
||||
* typically without perspective effects, suitable for HUDs and UI.
|
||||
*
|
||||
* @return The `Node` object representing the GUI root.
|
||||
*/
|
||||
public Node getGuiNode() {
|
||||
return guiNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the root `Node` of the 3D scene graph.
|
||||
* All main 3D spatial objects and models should be attached to this node
|
||||
* to be part of the rendered scene.
|
||||
*
|
||||
* @return The `Node` object representing the 3D scene root.
|
||||
*/
|
||||
public Node getRootNode() {
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the settings dialog is configured to be shown at application startup.
|
||||
*
|
||||
* @return `true` if the settings dialog will be displayed, `false` otherwise.
|
||||
*/
|
||||
public boolean isShowSettings() {
|
||||
return showSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the jME3 settings dialog should be displayed before the application starts.
|
||||
*
|
||||
* @param showSettings `true` to show the settings dialog, `false` to suppress it.
|
||||
*/
|
||||
public void setShowSettings(boolean showSettings) {
|
||||
this.showSettings = showSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the font that will be set to the guiFont field
|
||||
* and subsequently set as the font for the stats text.
|
||||
*
|
||||
* @return the loaded BitmapFont
|
||||
*/
|
||||
protected BitmapFont loadGuiFont() {
|
||||
return assetManager.loadFont("Interface/Fonts/Default.fnt");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
super.initialize();
|
||||
|
||||
//noinspection AssertWithSideEffects
|
||||
assert SceneGraphThreadWarden.setup(rootNode);
|
||||
//noinspection AssertWithSideEffects
|
||||
assert SceneGraphThreadWarden.setup(guiNode);
|
||||
|
||||
// Several things rely on having this
|
||||
guiFont = loadGuiFont();
|
||||
|
||||
guiNode.setQueueBucket(Bucket.Gui);
|
||||
guiNode.setCullHint(CullHint.Never);
|
||||
|
||||
viewPort.attachScene(rootNode);
|
||||
guiViewPort.attachScene(guiNode);
|
||||
|
||||
if (inputManager != null) {
|
||||
// Special handling for FlyCamAppState:
|
||||
// Although FlyCamAppState manages the FlyByCamera, SimpleApplication
|
||||
// historically initializes and configures a default FlyByCamera instance
|
||||
// and sets its initial speed. This allows subclasses to directly access
|
||||
// 'flyCam' early in simpleInitApp().
|
||||
|
||||
FlyCamAppState flyCamState = stateManager.getState(FlyCamAppState.class);
|
||||
if (flyCamState != null) {
|
||||
flyCam = new FlyByCamera(cam);
|
||||
flyCam.setMoveSpeed(1f); // Set a default movement speed for the camera
|
||||
flyCamState.setCamera(flyCam); // Link the FlyCamAppState to this camera instance
|
||||
}
|
||||
|
||||
// Register the "Exit" input mapping for the Escape key, but only for Display contexts.
|
||||
if (context.getType() == Type.Display) {
|
||||
inputManager.addMapping(INPUT_MAPPING_EXIT, new KeyTrigger(KeyInput.KEY_ESCAPE));
|
||||
}
|
||||
|
||||
// Register the "Hide Stats" input mapping for the F5 key, if StatsAppState is active.
|
||||
StatsAppState statsState = stateManager.getState(StatsAppState.class);
|
||||
if (statsState != null) {
|
||||
inputManager.addMapping(INPUT_MAPPING_HIDE_STATS, new KeyTrigger(KeyInput.KEY_F5));
|
||||
inputManager.addListener(actionListener, INPUT_MAPPING_HIDE_STATS);
|
||||
}
|
||||
|
||||
// Attach the action listener to the "Exit" mapping.
|
||||
inputManager.addListener(actionListener, INPUT_MAPPING_EXIT);
|
||||
}
|
||||
|
||||
// Configure the StatsAppState if it exists.
|
||||
StatsAppState statsState = stateManager.getState(StatsAppState.class);
|
||||
if (statsState != null) {
|
||||
statsState.setFont(guiFont);
|
||||
fpsText = statsState.getFpsText();
|
||||
}
|
||||
|
||||
// Call the user's application initialization code.
|
||||
simpleInitApp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(boolean waitFor) {
|
||||
//noinspection AssertWithSideEffects
|
||||
assert SceneGraphThreadWarden.reset();
|
||||
super.stop(waitFor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
if (prof != null) {
|
||||
prof.appStep(AppStep.BeginFrame);
|
||||
}
|
||||
|
||||
// Executes AppTasks from the main thread
|
||||
super.update();
|
||||
|
||||
// Skip updates if paused or speed is zero
|
||||
if (speed == 0 || paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
float tpf = timer.getTimePerFrame() * speed;
|
||||
|
||||
// Update AppStates
|
||||
if (prof != null) {
|
||||
prof.appStep(AppStep.StateManagerUpdate);
|
||||
}
|
||||
stateManager.update(tpf);
|
||||
|
||||
// Call user's per-frame update method
|
||||
simpleUpdate(tpf);
|
||||
|
||||
// Update scene graph nodes (logical and geometric states)
|
||||
if (prof != null) {
|
||||
prof.appStep(AppStep.SpatialUpdate);
|
||||
}
|
||||
rootNode.updateLogicalState(tpf);
|
||||
guiNode.updateLogicalState(tpf);
|
||||
|
||||
rootNode.updateGeometricState();
|
||||
guiNode.updateGeometricState();
|
||||
|
||||
// Render AppStates and the scene
|
||||
if (prof != null) {
|
||||
prof.appStep(AppStep.StateManagerRender);
|
||||
}
|
||||
stateManager.render(renderManager);
|
||||
|
||||
if (prof != null) {
|
||||
prof.appStep(AppStep.RenderFrame);
|
||||
}
|
||||
renderManager.render(tpf, context.isRenderable());
|
||||
// Call user's custom render method
|
||||
simpleRender(renderManager);
|
||||
stateManager.postRender();
|
||||
|
||||
if (prof != null) {
|
||||
prof.appStep(AppStep.EndFrame);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the visibility of the frames-per-second (FPS) display on the screen.
|
||||
*
|
||||
* @param show `true` to display the FPS, `false` to hide it.
|
||||
*/
|
||||
public void setDisplayFps(boolean show) {
|
||||
StatsAppState statsState = stateManager.getState(StatsAppState.class);
|
||||
if (statsState != null) {
|
||||
statsState.setDisplayFps(show);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the visibility of the comprehensive statistics view on the screen.
|
||||
* This view typically includes details about memory, triangles, and other performance metrics.
|
||||
*
|
||||
* @param show `true` to display the statistics view, `false` to hide it.
|
||||
*/
|
||||
public void setDisplayStatView(boolean show) {
|
||||
StatsAppState statsState = stateManager.getState(StatsAppState.class);
|
||||
if (statsState != null) {
|
||||
statsState.setDisplayStatView(show);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void simpleInitApp();
|
||||
|
||||
/**
|
||||
* An optional method that can be overridden by subclasses for per-frame update logic.
|
||||
* This method is called during the application's update loop, after AppStates are updated
|
||||
* and before the scene graph's logical state is updated.
|
||||
*
|
||||
* @param tpf The time per frame (in seconds), adjusted by the application's speed.
|
||||
*/
|
||||
public void simpleUpdate(float tpf) {
|
||||
// Default empty implementation; subclasses can override
|
||||
}
|
||||
|
||||
/**
|
||||
* An optional method that can be overridden by subclasses for custom rendering logic.
|
||||
* This method is called during the application's render loop, after the main scene
|
||||
* has been rendered and before post-rendering for states.
|
||||
* Useful for drawing overlays or specific rendering tasks outside the main scene graph.
|
||||
*
|
||||
* @param rm The `RenderManager` instance, which provides access to rendering functionalities.
|
||||
*/
|
||||
public void simpleRender(RenderManager rm) {
|
||||
// Default empty implementation; subclasses can override
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user