How should I learn modular concepts correctly

wiki

这是MonkeyZone
I don’t think I can access it
I am further studying JME

Control Not as detailed as the appState article
Can you recommend more articles about AbstractControl
:pray: :pray: :pray: :pray:

Controls are self contained. They have to be given everything they need.

In 11 years of JME development, I’ve never needed to have two controls communicate with each other. There may be a better way to do what you are actually trying to do.

Always the best place to start: what are you actually trying to do?

Sometimes new JME users will think of a solution that is not the best solution and then wonder why it is harder. If we understand what you are actually trying to do then we can offer proper suggestions on the preferred way.

1 Like

I’ve put together some examples that I’ve written about before
Putting all the code in simpleInitApp is not a wise choice
Need to be modular

That’s correct.

AppStates for most global game logic.

Controls for spatial-specific game logic.

In some architectures like “entity component systems”, there are almost no controls at all. That is a more advanced topic, though.

For reference, this is the actual main application class of my current game Mythruna. As you can see there is essentially nothing here other than basic setup. The real work is done in app states.

/*
 * $Id$
 *
 * Copyright (c) 2020, Simsilica, LLC
 * All rights reserved.
 */

package mythruna.client;

import java.util.*;
import java.util.prefs.*;

import org.slf4j.*;

import com.jme3.app.*;
import com.jme3.app.state.ScreenshotAppState;
import com.jme3.math.ColorRGBA;
import com.jme3.system.AppSettings;

import com.jme3.input.RawInputListener;
import com.jme3.input.event.*;

import com.simsilica.lemur.*;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.*;
import com.simsilica.state.*;
import com.simsilica.thread.JobState;
import com.simsilica.util.LogAdapter;

import mythruna.GameConstants;
import mythruna.SystemInfo;
import mythruna.client.net.NetworkState;

/**
 *  Main Mythruna entry point.
 *
 *  @author    Paul Speed
 */
public class Main extends SimpleApplication {

    static Logger log = LoggerFactory.getLogger(Main.class);

    public static void main( String... args ) throws Exception {

        // Make sure JUL logging goes to our log4j configuration
        LogAdapter.initialize();

        // Make sure the confing is loaded
        MythrunaConfig.getInstance();

        Main main = new Main();
        AppSettings settings = new AppSettings(true);

        // Set some defaults that will get overwritten if
        // there were previously saved settings from the last time the user
        // ran.
        settings.setWidth(1280);
        settings.setHeight(720);
        settings.setVSync(true);
        settings.setGammaCorrection(false);

        // Note: JME uses the title to save so these two must match.
        settings.load("Mythruna");
        settings.setTitle("Mythruna");
        settings.setUseJoysticks(true);

        settings.setSettingsDialogImage("/Interface/mythruna-test-title.png");
        try {
            BufferedImage[] icons = new BufferedImage[] {
                    ImageIO.read( TreeEditor.class.getResource( "/dragon-icon-128.png" ) ),
                    ImageIO.read( TreeEditor.class.getResource( "/dragon-icon-32.png" ) ),
                    ImageIO.read( TreeEditor.class.getResource( "/dragon-icon-16.png" ) )
                };
            settings.setIcons(icons);
        } catch( IOException e ) {
            log.warn( "Error loading globe icons", e );
        }

        main.setSettings(settings);
        main.start();
    }

    public Main() {
        super(new StatsAppState(), new DebugKeysAppState(), new BasicProfilerState(false),
              new ConfigurationState(),
              new GuiState(),
              new PerspectiveGuiState(),
              new PostProcessingState(),
              new OptionPanelState(),
              new DebugHudState(),
              new MemoryDebugState(),
              new CommandConsoleState(),
              new MessageState(),
              new NetworkState(),
              new MainMenuState(),
              new GameSettingsState(),
              new JobState("regularWorkers", 4, 1),
              new JobState("priorityWorkers", 4, 1),
              new JobState("backgroundWorkers", 2, 1),
              //new mythruna.client.ui.bp.BlueprintEditorState(),
              new ProgressState(),
              new com.simsilica.mworld.view.ProgressState(),
              new ScreenshotAppState("", System.currentTimeMillis()));
    }

    @Override
    public void simpleInitApp() {

        // Get rid of the default close mapping in InputManager
        if( inputManager.hasMapping(INPUT_MAPPING_EXIT) ) {
            inputManager.deleteMapping(INPUT_MAPPING_EXIT);
        }

        SystemInfo.initialize(getContext()).write(new java.io.File("system-info.txt"));

        setPauseOnLostFocus(false);
        setDisplayFps(false);
        setDisplayStatView(false);

        if( stateManager.getState(StatsAppState.class) != null ) {
            inputManager.deleteMapping(INPUT_MAPPING_HIDE_STATS);
        }

        GuiGlobals.initialize(this);

        GuiGlobals globals = GuiGlobals.getInstance();

        MainGameFunctions.initializeDefaultMappings(globals.getInputMapper());

        // Setup the main stats keys
        globals.getInputMapper().addDelegate(MainGameFunctions.F_JME_STATS, 
                                             stateManager.getState(StatsAppState.class), 
                                             "toggleStats");        

        BaseStyles.loadGlassStyle();
        globals.getStyles().setDefaultStyle("glass");

        // Setup some custom styling for the debug HUD
        Styles styles = globals.getStyles();

        // We don't want backgrounds on those panels
        Attributes attrs = styles.getSelector(DebugHudState.CONTAINER_ID, "glass");
        attrs.set("background", null);

        attrs = styles.getSelector(DebugHudState.NAME_ID, "glass");
        attrs.set("color", ColorRGBA.White);
        attrs.set("background", new QuadBackgroundComponent(new ColorRGBA(0, 0, 0, 0.5f)));
        attrs.set("textHAlignment", HAlignment.Right);
        attrs.set("insets", new Insets3f(0, 0, 0, 0));

        attrs = styles.getSelector(DebugHudState.VALUE_ID, "glass");
        attrs.set("color", ColorRGBA.White);
        attrs.set("background", new QuadBackgroundComponent(new ColorRGBA(0, 0, 0, 0.5f)));
        attrs.set("insets", new Insets3f(0, 0, 0, 0));

        attrs = styles.getSelector(MessageState.MESSAGE_LABEL_ID, "glass");
        attrs.set("background", new QuadBackgroundComponent(new ColorRGBA(0, 0, 0, 0.5f)));
        attrs.set("shadowColor", new ColorRGBA(0, 0, 0, 1));
    }
}
2 Likes

first of all, you should use ECS.

Second, you should use AppStates (BaseAppState) for modularity

using both, you create modularity and System Component based Code that is a way to go.

for ECS you can use Zay-ES or any other ECS library.

and if you plan to make Game “moddable” you should provide Groovy or LUA or JS or any other modding language for Modders.

1 Like