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
5050import com .jme3 .system .JmeContext .Type ;
5151import com .jme3 .system .JmeSystem ;
5252
53+ import java .util .logging .Level ;
54+ import java .util .logging .Logger ;
55+
5356/**
54- * <code>SimpleApplication</code> is the base class for all jME3 Applications.
55- * <code>SimpleApplication</code> will display a statistics view
56- * using the {@link com.jme3.app.StatsAppState} AppState. It will display
57- * the current frames-per-second value on-screen in addition to the statistics.
58- * Several keys have special functionality in <code>SimpleApplication</code>:<br>
57+ * `SimpleApplication` is the foundational base class for all jMonkeyEngine 3 (jME3) applications.
58+ * It provides a streamlined setup for common game development tasks, including scene management,
59+ * camera controls, and performance monitoring.
60+ *
61+ * <p>By default, `SimpleApplication` attaches several essential {@link com.jme3.app.state.AppState} instances:
62+ * <ul>
63+ * <li>{@link com.jme3.app.StatsAppState}: Displays real-time frames-per-second (FPS) and
64+ * detailed performance statistics on-screen.</li>
65+ * <li>{@link com.jme3.app.FlyCamAppState}: Provides a convenient first-person fly-by camera
66+ * controller, allowing easy navigation within the scene.</li>
67+ * <li>{@link com.jme3.audio.AudioListenerState}: Manages the audio listener, essential for 3D sound.</li>
68+ * <li>{@link com.jme3.app.DebugKeysAppState}: Enables debug functionalities like displaying
69+ * camera position and memory usage in the console.</li>
70+ * <li>{@link com.jme3.app.state.ConstantVerifierState}: A utility state for verifying constant
71+ * values, primarily for internal engine debugging.</li>
72+ * </ul>
5973 *
60- * Esc - Close the application.<br>
61- * C - Display the camera position and rotation in the console.<br>
62- * M - Display memory usage in the console.<br>
74+ * <p><b>Default Key Bindings:</b></p>
75+ * <ul>
76+ * <li><b>Esc:</b> Closes and exits the application.</li>
77+ * <li><b>F5:</b> Toggles the visibility of the statistics view (FPS and debug stats).</li>
78+ * <li><b>C:</b> Prints the current camera position and rotation to the console.</li>
79+ * <li><b>M:</b> Prints memory usage statistics to the console.</li>
80+ * </ul>
6381 *
64- * A {@link com.jme3.app.FlyCamAppState} is by default attached as well and can
65- * be removed by calling <code>stateManager.detach(stateManager.getState(FlyCamAppState.class));</code>
82+ * <p>Applications extending `SimpleApplication` should implement the
83+ * {@link #simpleInitApp()} method to set up their initial scene and game logic.
6684 */
6785public abstract class SimpleApplication extends LegacyApplication {
6886
87+ protected static final Logger logger = Logger .getLogger (SimpleApplication .class .getName ());
88+
6989 public static final String INPUT_MAPPING_EXIT = "SIMPLEAPP_Exit" ;
7090 public static final String INPUT_MAPPING_CAMERA_POS = DebugKeysAppState .INPUT_MAPPING_CAMERA_POS ;
7191 public static final String INPUT_MAPPING_MEMORY = DebugKeysAppState .INPUT_MAPPING_MEMORY ;
@@ -82,26 +102,43 @@ public abstract class SimpleApplication extends LegacyApplication {
82102 private class AppActionListener implements ActionListener {
83103
84104 @ Override
85- public void onAction (String name , boolean value , float tpf ) {
86- if (!value ) {
105+ public void onAction (String name , boolean isPressed , float tpf ) {
106+ if (!isPressed ) {
87107 return ;
88108 }
89109
90110 if (name .equals (INPUT_MAPPING_EXIT )) {
91111 stop ();
92112 } else if (name .equals (INPUT_MAPPING_HIDE_STATS )) {
93- if (stateManager .getState (StatsAppState .class ) != null ) {
94- stateManager .getState (StatsAppState .class ).toggleStats ();
113+ StatsAppState statsState = stateManager .getState (StatsAppState .class );
114+ if (statsState != null ) {
115+ statsState .toggleStats ();
95116 }
96117 }
97118 }
98119 }
99120
121+ /**
122+ * Constructs a `SimpleApplication` with a predefined set of default
123+ * {@link com.jme3.app.state.AppState} instances.
124+ * These states provide common functionalities like statistics display,
125+ * fly camera control, audio listener, debug keys, and constant verification.
126+ */
100127 public SimpleApplication () {
101- this (new StatsAppState (), new FlyCamAppState (), new AudioListenerState (), new DebugKeysAppState (),
128+ this (new StatsAppState (),
129+ new FlyCamAppState (),
130+ new AudioListenerState (),
131+ new DebugKeysAppState (),
102132 new ConstantVerifierState ());
103133 }
104134
135+ /**
136+ * Constructs a `SimpleApplication` with a custom array of initial
137+ * {@link com.jme3.app.state.AppState} instances.
138+ *
139+ * @param initialStates An array of `AppState` instances to be attached
140+ * to the `stateManager` upon initialization.
141+ */
105142 public SimpleApplication (AppState ... initialStates ) {
106143 super (initialStates );
107144 }
@@ -112,6 +149,7 @@ public void start() {
112149 // settings dialog is not shown
113150 boolean loadSettings = false ;
114151 if (settings == null ) {
152+ logger .log (Level .INFO , "AppSettings not set, creating default settings." );
115153 setSettings (new AppSettings (true ));
116154 loadSettings = true ;
117155 }
@@ -128,57 +166,73 @@ public void start() {
128166 }
129167
130168 /**
131- * Returns the application's speed.
169+ * Returns the current speed multiplier of the application.
170+ * This value affects how quickly the game world updates relative to real time.
171+ * A value of 1.0f means normal speed, 0.5f means half speed, 2.0f means double speed.
132172 *
133- * @return The speed of the application.
173+ * @return The current speed of the application.
134174 */
135175 public float getSpeed () {
136176 return speed ;
137177 }
138178
139179 /**
140- * Changes the application's speed. 0.0f prevents the application from updating.
141- * @param speed The speed to set.
180+ * Changes the application's speed multiplier.
181+ * A `speed` of 0.0f effectively pauses the application's update cycle.
182+ *
183+ * @param speed The desired speed multiplier. A value of 1.0f is normal speed.
184+ * Must be non-negative.
142185 */
143186 public void setSpeed (float speed ) {
144187 this .speed = speed ;
145188 }
146189
147190 /**
148- * Retrieves flyCam
149- * @return flyCam Camera object
191+ * Retrieves the `FlyByCamera` instance associated with this application.
192+ * This camera allows free-form navigation within the 3D scene.
150193 *
194+ * @return The `FlyByCamera` object, or `null` if `FlyCamAppState` is not attached
195+ * or has not yet initialized the camera.
151196 */
152197 public FlyByCamera getFlyByCamera () {
153198 return flyCam ;
154199 }
155200
156201 /**
157- * Retrieves guiNode
158- * @return guiNode Node object
202+ * Retrieves the `Node` dedicated to 2D graphical user interface (GUI) elements.
203+ * Objects attached to this node are rendered on top of the 3D scene,
204+ * typically without perspective effects, suitable for HUDs and UI.
159205 *
206+ * @return The `Node` object representing the GUI root.
160207 */
161208 public Node getGuiNode () {
162209 return guiNode ;
163210 }
164211
165212 /**
166- * Retrieves rootNode
167- * @return rootNode Node object
213+ * Retrieves the root `Node` of the 3D scene graph.
214+ * All main 3D spatial objects and models should be attached to this node
215+ * to be part of the rendered scene.
168216 *
217+ * @return The `Node` object representing the 3D scene root.
169218 */
170219 public Node getRootNode () {
171220 return rootNode ;
172221 }
173222
223+ /**
224+ * Checks whether the settings dialog is configured to be shown at application startup.
225+ *
226+ * @return `true` if the settings dialog will be displayed, `false` otherwise.
227+ */
174228 public boolean isShowSettings () {
175229 return showSettings ;
176230 }
177231
178232 /**
179- * Toggles settings window to display at start-up
180- * @param showSettings Sets true/false
233+ * Sets whether the jME3 settings dialog should be displayed before the application starts.
181234 *
235+ * @param showSettings `true` to show the settings dialog, `false` to suppress it.
182236 */
183237 public void setShowSettings (boolean showSettings ) {
184238 this .showSettings = showSettings ;
@@ -208,41 +262,48 @@ public void initialize() {
208262
209263 guiNode .setQueueBucket (Bucket .Gui );
210264 guiNode .setCullHint (CullHint .Never );
265+
211266 viewPort .attachScene (rootNode );
212267 guiViewPort .attachScene (guiNode );
213268
214269 if (inputManager != null ) {
215-
216- // We have to special-case the FlyCamAppState because too
217- // many SimpleApplication subclasses expect it to exist in
218- // simpleInit(). But at least it only gets initialized if
219- // the app state is added.
220- if (stateManager .getState (FlyCamAppState .class ) != null ) {
270+ // Special handling for FlyCamAppState:
271+ // Although FlyCamAppState manages the FlyByCamera, SimpleApplication
272+ // historically initializes and configures a default FlyByCamera instance
273+ // and sets its initial speed. This allows subclasses to directly access
274+ // 'flyCam' early in simpleInitApp().
275+
276+ FlyCamAppState flyCamState = stateManager .getState (FlyCamAppState .class );
277+ if (flyCamState != null ) {
221278 flyCam = new FlyByCamera (cam );
222- flyCam .setMoveSpeed (1f ); // odd to set this here but it did it before
223- stateManager . getState ( FlyCamAppState . class ). setCamera (flyCam );
279+ flyCam .setMoveSpeed (1f ); // Set a default movement speed for the camera
280+ flyCamState . setCamera (flyCam ); // Link the FlyCamAppState to this camera instance
224281 }
225282
283+ // Register the "Exit" input mapping for the Escape key, but only for Display contexts.
226284 if (context .getType () == Type .Display ) {
227285 inputManager .addMapping (INPUT_MAPPING_EXIT , new KeyTrigger (KeyInput .KEY_ESCAPE ));
228286 }
229287
230- if (stateManager .getState (StatsAppState .class ) != null ) {
288+ // Register the "Hide Stats" input mapping for the F5 key, if StatsAppState is active.
289+ StatsAppState statsState = stateManager .getState (StatsAppState .class );
290+ if (statsState != null ) {
231291 inputManager .addMapping (INPUT_MAPPING_HIDE_STATS , new KeyTrigger (KeyInput .KEY_F5 ));
232292 inputManager .addListener (actionListener , INPUT_MAPPING_HIDE_STATS );
233293 }
234294
295+ // Attach the action listener to the "Exit" mapping.
235296 inputManager .addListener (actionListener , INPUT_MAPPING_EXIT );
236297 }
237298
238- if ( stateManager . getState ( StatsAppState . class ) != null ) {
239- // Some tests rely on having access to fpsText
240- // for quick display. Maybe a different way would be better.
241- stateManager . getState ( StatsAppState . class ) .setFont (guiFont );
242- fpsText = stateManager . getState ( StatsAppState . class ) .getFpsText ();
299+ // Configure the StatsAppState if it exists.
300+ StatsAppState statsState = stateManager . getState ( StatsAppState . class );
301+ if ( statsState != null ) {
302+ statsState .setFont (guiFont );
303+ fpsText = statsState .getFpsText ();
243304 }
244305
245- // call user code
306+ // Call the user's application initialization code.
246307 simpleInitApp ();
247308 }
248309
@@ -259,22 +320,26 @@ public void update() {
259320 prof .appStep (AppStep .BeginFrame );
260321 }
261322
262- super .update (); // makes sure to execute AppTasks
323+ // Executes AppTasks from the main thread
324+ super .update ();
325+
326+ // Skip updates if paused or speed is zero
263327 if (speed == 0 || paused ) {
264328 return ;
265329 }
266330
267331 float tpf = timer .getTimePerFrame () * speed ;
268332
269- // update states
333+ // Update AppStates
270334 if (prof != null ) {
271335 prof .appStep (AppStep .StateManagerUpdate );
272336 }
273337 stateManager .update (tpf );
274338
275- // simple update and root node
339+ // Call user's per-frame update method
276340 simpleUpdate (tpf );
277341
342+ // Update scene graph nodes (logical and geometric states)
278343 if (prof != null ) {
279344 prof .appStep (AppStep .SpatialUpdate );
280345 }
@@ -284,7 +349,7 @@ public void update() {
284349 rootNode .updateGeometricState ();
285350 guiNode .updateGeometricState ();
286351
287- // render states
352+ // Render AppStates and the scene
288353 if (prof != null ) {
289354 prof .appStep (AppStep .StateManagerRender );
290355 }
@@ -294,6 +359,7 @@ public void update() {
294359 prof .appStep (AppStep .RenderFrame );
295360 }
296361 renderManager .render (tpf , context .isRenderable ());
362+ // Call user's custom render method
297363 simpleRender (renderManager );
298364 stateManager .postRender ();
299365
@@ -302,23 +368,53 @@ public void update() {
302368 }
303369 }
304370
371+ /**
372+ * Controls the visibility of the frames-per-second (FPS) display on the screen.
373+ *
374+ * @param show `true` to display the FPS, `false` to hide it.
375+ */
305376 public void setDisplayFps (boolean show ) {
306- if (stateManager .getState (StatsAppState .class ) != null ) {
307- stateManager .getState (StatsAppState .class ).setDisplayFps (show );
377+ StatsAppState statsState = stateManager .getState (StatsAppState .class );
378+ if (statsState != null ) {
379+ statsState .setDisplayFps (show );
308380 }
309381 }
310382
383+ /**
384+ * Controls the visibility of the comprehensive statistics view on the screen.
385+ * This view typically includes details about memory, triangles, and other performance metrics.
386+ *
387+ * @param show `true` to display the statistics view, `false` to hide it.
388+ */
311389 public void setDisplayStatView (boolean show ) {
312- if (stateManager .getState (StatsAppState .class ) != null ) {
313- stateManager .getState (StatsAppState .class ).setDisplayStatView (show );
390+ StatsAppState statsState = stateManager .getState (StatsAppState .class );
391+ if (statsState != null ) {
392+ statsState .setDisplayStatView (show );
314393 }
315394 }
316395
317396 public abstract void simpleInitApp ();
318397
398+ /**
399+ * An optional method that can be overridden by subclasses for per-frame update logic.
400+ * This method is called during the application's update loop, after AppStates are updated
401+ * and before the scene graph's logical state is updated.
402+ *
403+ * @param tpf The time per frame (in seconds), adjusted by the application's speed.
404+ */
319405 public void simpleUpdate (float tpf ) {
406+ // Default empty implementation; subclasses can override
320407 }
321408
409+ /**
410+ * An optional method that can be overridden by subclasses for custom rendering logic.
411+ * This method is called during the application's render loop, after the main scene
412+ * has been rendered and before post-rendering for states.
413+ * Useful for drawing overlays or specific rendering tasks outside the main scene graph.
414+ *
415+ * @param rm The `RenderManager` instance, which provides access to rendering functionalities.
416+ */
322417 public void simpleRender (RenderManager rm ) {
418+ // Default empty implementation; subclasses can override
323419 }
324420}
0 commit comments