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
4949import com .jme3 .system .JmeContext .Type ;
5050import 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 */
6684public 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