Skip to content

Commit 3557dbe

Browse files
authored
Update SimpleApplication.java
1 parent 9a10591 commit 3557dbe

File tree

1 file changed

+148
-52
lines changed

1 file changed

+148
-52
lines changed

jme3-core/src/main/java/com/jme3/app/SimpleApplication.java

Lines changed: 148 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009-2022 jMonkeyEngine
2+
* Copyright (c) 2009-2025 jMonkeyEngine
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -49,22 +49,42 @@
4949
import com.jme3.system.JmeContext.Type;
5050
import com.jme3.system.JmeSystem;
5151

52+
import java.util.logging.Level;
53+
import java.util.logging.Logger;
54+
5255
/**
53-
* <code>SimpleApplication</code> is the base class for all jME3 Applications.
54-
* <code>SimpleApplication</code> will display a statistics view
55-
* using the {@link com.jme3.app.StatsAppState} AppState. It will display
56-
* the current frames-per-second value on-screen in addition to the statistics.
57-
* Several keys have special functionality in <code>SimpleApplication</code>:<br>
56+
* `SimpleApplication` is the foundational base class for all jMonkeyEngine 3 (jME3) applications.
57+
* It provides a streamlined setup for common game development tasks, including scene management,
58+
* camera controls, and performance monitoring.
59+
*
60+
* <p>By default, `SimpleApplication` attaches several essential {@link com.jme3.app.state.AppState} instances:
61+
* <ul>
62+
* <li>{@link com.jme3.app.StatsAppState}: Displays real-time frames-per-second (FPS) and
63+
* detailed performance statistics on-screen.</li>
64+
* <li>{@link com.jme3.app.FlyCamAppState}: Provides a convenient first-person fly-by camera
65+
* controller, allowing easy navigation within the scene.</li>
66+
* <li>{@link com.jme3.audio.AudioListenerState}: Manages the audio listener, essential for 3D sound.</li>
67+
* <li>{@link com.jme3.app.DebugKeysAppState}: Enables debug functionalities like displaying
68+
* camera position and memory usage in the console.</li>
69+
* <li>{@link com.jme3.app.state.ConstantVerifierState}: A utility state for verifying constant
70+
* values, primarily for internal engine debugging.</li>
71+
* </ul>
5872
*
59-
* Esc - Close the application.<br>
60-
* C - Display the camera position and rotation in the console.<br>
61-
* M - Display memory usage in the console.<br>
73+
* <p><b>Default Key Bindings:</b></p>
74+
* <ul>
75+
* <li><b>Esc:</b> Closes and exits the application.</li>
76+
* <li><b>F5:</b> Toggles the visibility of the statistics view (FPS and debug stats).</li>
77+
* <li><b>C:</b> Prints the current camera position and rotation to the console.</li>
78+
* <li><b>M:</b> Prints memory usage statistics to the console.</li>
79+
* </ul>
6280
*
63-
* A {@link com.jme3.app.FlyCamAppState} is by default attached as well and can
64-
* be removed by calling <code>stateManager.detach(stateManager.getState(FlyCamAppState.class));</code>
81+
* <p>Applications extending `SimpleApplication` should implement the
82+
* {@link #simpleInitApp()} method to set up their initial scene and game logic.
6583
*/
6684
public abstract class SimpleApplication extends LegacyApplication {
6785

86+
protected static final Logger logger = Logger.getLogger(SimpleApplication.class.getName());
87+
6888
public static final String INPUT_MAPPING_EXIT = "SIMPLEAPP_Exit";
6989
public static final String INPUT_MAPPING_CAMERA_POS = DebugKeysAppState.INPUT_MAPPING_CAMERA_POS;
7090
public static final String INPUT_MAPPING_MEMORY = DebugKeysAppState.INPUT_MAPPING_MEMORY;
@@ -81,26 +101,43 @@ public abstract class SimpleApplication extends LegacyApplication {
81101
private class AppActionListener implements ActionListener {
82102

83103
@Override
84-
public void onAction(String name, boolean value, float tpf) {
85-
if (!value) {
104+
public void onAction(String name, boolean isPressed, float tpf) {
105+
if (!isPressed) {
86106
return;
87107
}
88108

89109
if (name.equals(INPUT_MAPPING_EXIT)) {
90110
stop();
91111
} else if (name.equals(INPUT_MAPPING_HIDE_STATS)) {
92-
if (stateManager.getState(StatsAppState.class) != null) {
93-
stateManager.getState(StatsAppState.class).toggleStats();
112+
StatsAppState statsState = stateManager.getState(StatsAppState.class);
113+
if (statsState != null) {
114+
statsState.toggleStats();
94115
}
95116
}
96117
}
97118
}
98119

120+
/**
121+
* Constructs a `SimpleApplication` with a predefined set of default
122+
* {@link com.jme3.app.state.AppState} instances.
123+
* These states provide common functionalities like statistics display,
124+
* fly camera control, audio listener, debug keys, and constant verification.
125+
*/
99126
public SimpleApplication() {
100-
this(new StatsAppState(), new FlyCamAppState(), new AudioListenerState(), new DebugKeysAppState(),
127+
this(new StatsAppState(),
128+
new FlyCamAppState(),
129+
new AudioListenerState(),
130+
new DebugKeysAppState(),
101131
new ConstantVerifierState());
102132
}
103133

134+
/**
135+
* Constructs a `SimpleApplication` with a custom array of initial
136+
* {@link com.jme3.app.state.AppState} instances.
137+
*
138+
* @param initialStates An array of `AppState` instances to be attached
139+
* to the `stateManager` upon initialization.
140+
*/
104141
public SimpleApplication(AppState... initialStates) {
105142
super(initialStates);
106143
}
@@ -111,6 +148,7 @@ public void start() {
111148
// settings dialog is not shown
112149
boolean loadSettings = false;
113150
if (settings == null) {
151+
logger.log(Level.INFO, "AppSettings not set, creating default settings.");
114152
setSettings(new AppSettings(true));
115153
loadSettings = true;
116154
}
@@ -127,57 +165,73 @@ public void start() {
127165
}
128166

129167
/**
130-
* Returns the application's speed.
168+
* Returns the current speed multiplier of the application.
169+
* This value affects how quickly the game world updates relative to real time.
170+
* A value of 1.0f means normal speed, 0.5f means half speed, 2.0f means double speed.
131171
*
132-
* @return The speed of the application.
172+
* @return The current speed of the application.
133173
*/
134174
public float getSpeed() {
135175
return speed;
136176
}
137177

138178
/**
139-
* Changes the application's speed. 0.0f prevents the application from updating.
140-
* @param speed The speed to set.
179+
* Changes the application's speed multiplier.
180+
* A `speed` of 0.0f effectively pauses the application's update cycle.
181+
*
182+
* @param speed The desired speed multiplier. A value of 1.0f is normal speed.
183+
* Must be non-negative.
141184
*/
142185
public void setSpeed(float speed) {
143186
this.speed = speed;
144187
}
145188

146189
/**
147-
* Retrieves flyCam
148-
* @return flyCam Camera object
190+
* Retrieves the `FlyByCamera` instance associated with this application.
191+
* This camera allows free-form navigation within the 3D scene.
149192
*
193+
* @return The `FlyByCamera` object, or `null` if `FlyCamAppState` is not attached
194+
* or has not yet initialized the camera.
150195
*/
151196
public FlyByCamera getFlyByCamera() {
152197
return flyCam;
153198
}
154199

155200
/**
156-
* Retrieves guiNode
157-
* @return guiNode Node object
201+
* Retrieves the `Node` dedicated to 2D graphical user interface (GUI) elements.
202+
* Objects attached to this node are rendered on top of the 3D scene,
203+
* typically without perspective effects, suitable for HUDs and UI.
158204
*
205+
* @return The `Node` object representing the GUI root.
159206
*/
160207
public Node getGuiNode() {
161208
return guiNode;
162209
}
163210

164211
/**
165-
* Retrieves rootNode
166-
* @return rootNode Node object
212+
* Retrieves the root `Node` of the 3D scene graph.
213+
* All main 3D spatial objects and models should be attached to this node
214+
* to be part of the rendered scene.
167215
*
216+
* @return The `Node` object representing the 3D scene root.
168217
*/
169218
public Node getRootNode() {
170219
return rootNode;
171220
}
172221

222+
/**
223+
* Checks whether the settings dialog is configured to be shown at application startup.
224+
*
225+
* @return `true` if the settings dialog will be displayed, `false` otherwise.
226+
*/
173227
public boolean isShowSettings() {
174228
return showSettings;
175229
}
176230

177231
/**
178-
* Toggles settings window to display at start-up
179-
* @param showSettings Sets true/false
232+
* Sets whether the jME3 settings dialog should be displayed before the application starts.
180233
*
234+
* @param showSettings `true` to show the settings dialog, `false` to suppress it.
181235
*/
182236
public void setShowSettings(boolean showSettings) {
183237
this.showSettings = showSettings;
@@ -197,46 +251,53 @@ protected BitmapFont loadGuiFont() {
197251
public void initialize() {
198252
super.initialize();
199253

200-
// Several things rely on having this
254+
// Load the default GUI font. This is essential for rendering text like FPS.
201255
guiFont = loadGuiFont();
202256

203257
guiNode.setQueueBucket(Bucket.Gui);
204258
guiNode.setCullHint(CullHint.Never);
259+
205260
viewPort.attachScene(rootNode);
206261
guiViewPort.attachScene(guiNode);
207262

208263
if (inputManager != null) {
209-
210-
// We have to special-case the FlyCamAppState because too
211-
// many SimpleApplication subclasses expect it to exist in
212-
// simpleInit(). But at least it only gets initialized if
213-
// the app state is added.
214-
if (stateManager.getState(FlyCamAppState.class) != null) {
264+
// Special handling for FlyCamAppState:
265+
// Although FlyCamAppState manages the FlyByCamera, SimpleApplication
266+
// historically initializes and configures a default FlyByCamera instance
267+
// and sets its initial speed. This allows subclasses to directly access
268+
// 'flyCam' early in simpleInitApp().
269+
270+
FlyCamAppState flyCamState = stateManager.getState(FlyCamAppState.class);
271+
if (flyCamState != null) {
215272
flyCam = new FlyByCamera(cam);
216-
flyCam.setMoveSpeed(1f); // odd to set this here but it did it before
217-
stateManager.getState(FlyCamAppState.class).setCamera(flyCam);
273+
flyCam.setMoveSpeed(1f); // Set a default movement speed for the camera
274+
flyCamState.setCamera(flyCam); // Link the FlyCamAppState to this camera instance
218275
}
219276

277+
// Register the "Exit" input mapping for the Escape key, but only for Display contexts.
220278
if (context.getType() == Type.Display) {
221279
inputManager.addMapping(INPUT_MAPPING_EXIT, new KeyTrigger(KeyInput.KEY_ESCAPE));
222280
}
223281

224-
if (stateManager.getState(StatsAppState.class) != null) {
282+
// Register the "Hide Stats" input mapping for the F5 key, if StatsAppState is active.
283+
StatsAppState statsState = stateManager.getState(StatsAppState.class);
284+
if (statsState != null) {
225285
inputManager.addMapping(INPUT_MAPPING_HIDE_STATS, new KeyTrigger(KeyInput.KEY_F5));
226286
inputManager.addListener(actionListener, INPUT_MAPPING_HIDE_STATS);
227287
}
228288

289+
// Attach the action listener to the "Exit" mapping.
229290
inputManager.addListener(actionListener, INPUT_MAPPING_EXIT);
230291
}
231292

232-
if (stateManager.getState(StatsAppState.class) != null) {
233-
// Some tests rely on having access to fpsText
234-
// for quick display. Maybe a different way would be better.
235-
stateManager.getState(StatsAppState.class).setFont(guiFont);
236-
fpsText = stateManager.getState(StatsAppState.class).getFpsText();
293+
// Configure the StatsAppState if it exists.
294+
StatsAppState statsState = stateManager.getState(StatsAppState.class);
295+
if (statsState != null) {
296+
statsState.setFont(guiFont);
297+
fpsText = statsState.getFpsText();
237298
}
238299

239-
// call user code
300+
// Call the user's application initialization code.
240301
simpleInitApp();
241302
}
242303

@@ -246,22 +307,26 @@ public void update() {
246307
prof.appStep(AppStep.BeginFrame);
247308
}
248309

249-
super.update(); // makes sure to execute AppTasks
310+
// Executes AppTasks from the main thread
311+
super.update();
312+
313+
// Skip updates if paused or speed is zero
250314
if (speed == 0 || paused) {
251315
return;
252316
}
253317

254318
float tpf = timer.getTimePerFrame() * speed;
255319

256-
// update states
320+
// Update AppStates
257321
if (prof != null) {
258322
prof.appStep(AppStep.StateManagerUpdate);
259323
}
260324
stateManager.update(tpf);
261325

262-
// simple update and root node
326+
// Call user's per-frame update method
263327
simpleUpdate(tpf);
264328

329+
// Update scene graph nodes (logical and geometric states)
265330
if (prof != null) {
266331
prof.appStep(AppStep.SpatialUpdate);
267332
}
@@ -271,7 +336,7 @@ public void update() {
271336
rootNode.updateGeometricState();
272337
guiNode.updateGeometricState();
273338

274-
// render states
339+
// Render AppStates and the scene
275340
if (prof != null) {
276341
prof.appStep(AppStep.StateManagerRender);
277342
}
@@ -281,6 +346,7 @@ public void update() {
281346
prof.appStep(AppStep.RenderFrame);
282347
}
283348
renderManager.render(tpf, context.isRenderable());
349+
// Call user's custom render method
284350
simpleRender(renderManager);
285351
stateManager.postRender();
286352

@@ -289,23 +355,53 @@ public void update() {
289355
}
290356
}
291357

358+
/**
359+
* Controls the visibility of the frames-per-second (FPS) display on the screen.
360+
*
361+
* @param show `true` to display the FPS, `false` to hide it.
362+
*/
292363
public void setDisplayFps(boolean show) {
293-
if (stateManager.getState(StatsAppState.class) != null) {
294-
stateManager.getState(StatsAppState.class).setDisplayFps(show);
364+
StatsAppState statsState = stateManager.getState(StatsAppState.class);
365+
if (statsState != null) {
366+
statsState.setDisplayFps(show);
295367
}
296368
}
297369

370+
/**
371+
* Controls the visibility of the comprehensive statistics view on the screen.
372+
* This view typically includes details about memory, triangles, and other performance metrics.
373+
*
374+
* @param show `true` to display the statistics view, `false` to hide it.
375+
*/
298376
public void setDisplayStatView(boolean show) {
299-
if (stateManager.getState(StatsAppState.class) != null) {
300-
stateManager.getState(StatsAppState.class).setDisplayStatView(show);
377+
StatsAppState statsState = stateManager.getState(StatsAppState.class);
378+
if (statsState != null) {
379+
statsState.setDisplayStatView(show);
301380
}
302381
}
303382

304383
public abstract void simpleInitApp();
305384

385+
/**
386+
* An optional method that can be overridden by subclasses for per-frame update logic.
387+
* This method is called during the application's update loop, after AppStates are updated
388+
* and before the scene graph's logical state is updated.
389+
*
390+
* @param tpf The time per frame (in seconds), adjusted by the application's speed.
391+
*/
306392
public void simpleUpdate(float tpf) {
393+
// Default empty implementation; subclasses can override
307394
}
308395

396+
/**
397+
* An optional method that can be overridden by subclasses for custom rendering logic.
398+
* This method is called during the application's render loop, after the main scene
399+
* has been rendered and before post-rendering for states.
400+
* Useful for drawing overlays or specific rendering tasks outside the main scene graph.
401+
*
402+
* @param rm The `RenderManager` instance, which provides access to rendering functionalities.
403+
*/
309404
public void simpleRender(RenderManager rm) {
405+
// Default empty implementation; subclasses can override
310406
}
311407
}

0 commit comments

Comments
 (0)