BaseApplication vs SimpleApplication & App States

Hello, I’ve been tinkering around with JME for a while, and I’ve only just recently started interacting on the forums. I’ve been coding a simple rock-paper-scissors game to get familiar with the engine and although I think it’s pretty “okay” as is, I am well aware that it could use improvement. the question that I want to ask is how can I update my code to use the AppState class as described in the tutorials? Should I go back and re-read them? if so, which page goes over using appstates in projects?

My code is down below. I used simple MS Paint drawings for all of the textures.

RPS_Game.java:

package rock_paper_scissors;

import com.jme3.app.SimpleApplication;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.material.Material;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.texture.Texture;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

public class RPS_Game extends SimpleApplication
{    
    private RPS_Game.Choice playerChoice;
    private RPS_Game.Choice computerChoice; 
    
    private final Player player = new Player("Player");
    private final Player computer = new Player("Computer");
    
    private Geometry playerCube;
    private Geometry computerCube;
    
    Random rng = new Random();
    private int computerChoiceSelection;
    
    public static void main(String[] args)
    {
        RPS_Game game = new RPS_Game();
        game.start();
    }
    
    @Override
    public void simpleInitApp()
    {
        playerCube = initDisplayCube(player.getName());
        playerCube.move(-2f, 0f, 0f);
        
        computerCube = initDisplayCube(computer.getName());
        computerCube.move(2f, 0f, 0f);
        
        initHUD(player, computer);
        
        initKeys();
        
        rootNode.attachChild(playerCube);
        rootNode.attachChild(computerCube);
        
       
    }
    
    @Override
    public void simpleUpdate(float tpf)
    {   
        
        if(playerChoice != null)
        {
            
            computerChoiceSelection = (rng.nextInt(3) + 1);
        
            switch(computerChoiceSelection)
            {
                case 1:
                    computerChoice = Choice.ROCK;
                    computerCube.getMaterial().getTextureParam("ColorMap").setTextureValue(assetManager.loadTexture("Textures/Rock.png"));
                    break;
                case 2:
                    computerChoice = Choice.PAPER;
                    computerCube.getMaterial().getTextureParam("ColorMap").setTextureValue(assetManager.loadTexture("Textures/Paper.png"));
                    break;
                case 3:
                    computerChoice = Choice.SCISSORS;
                    computerCube.getMaterial().getTextureParam("ColorMap").setTextureValue(assetManager.loadTexture("Textures/Scissors.png"));
                    break;
            }
        
        
            switch(RPS_Game.CompareChoices(playerChoice, computerChoice))
            {
                case WON:
                    player.incrementScore();
                    System.out.println("player's score +1");
                    System.out.println("Player: " + player.getScore());
                    System.out.println("Computer: " + computer.getScore());
                    break;
                case LOST:
                    computer.incrementScore();
                    System.out.println("computer's score +1");
                    System.out.println("Player: " + player.getScore());
                    System.out.println("Computer: " + computer.getScore());
                    break;
            }
        
            initHUD(player, computer);
        
            computerChoice = null;
            playerChoice = null;
        }
        
        else
        {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException ex) {
                Logger.getLogger(RPS_Game.class.getName()).log(Level.SEVERE, null, ex);
            }
            computerCube.getMaterial().getTextureParam("ColorMap").setTextureValue(assetManager.loadTexture("Textures/modern.jpg"));
            playerCube.getMaterial().getTextureParam("ColorMap").setTextureValue(assetManager.loadTexture("Textures/modern.jpg"));
        }
        
    }
    
    @Override
    public void simpleRender(RenderManager rm)
    {
           
    }
    //This method initializes the heads-up display
    public void initHUD(Player thePlayer, Player theComputer)
    {
        /** Write text on the screen (HUD) */
        guiNode.detachAllChildren();
        guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
       
        BitmapText playerData = new BitmapText(guiFont, false);
        playerData.setSize(guiFont.getCharSet().getRenderedSize());
        playerData.setText(thePlayer.getName() + ": " + thePlayer.getScore());
        playerData.setLocalTranslation(10, settings.getHeight() - 10, 0);
        guiNode.attachChild(playerData);
        
         
        BitmapText computerData = new BitmapText(guiFont, false);
        computerData.setSize(guiFont.getCharSet().getRenderedSize());
        computerData.setText(theComputer.getName() + ": " + theComputer.getScore());
        computerData.setLocalTranslation(settings.getWidth() - (computerData.getLineWidth()+ 10), settings.getHeight() - 10, 0);
        guiNode.attachChild(computerData);
    }

    
    //This method maps the A, S, and D keys to set the player's choice to
    //Rock, Paper, or Scissors
    private void initKeys()
    {
        flyCam.setEnabled(false);
        
        inputManager.addMapping("Rock", new KeyTrigger(KeyInput.KEY_A));
        inputManager.addMapping("Paper", new KeyTrigger(KeyInput.KEY_S));
        inputManager.addMapping("Scissors", new KeyTrigger(KeyInput.KEY_D));
    
        inputManager.addListener(actionListener,"Rock", "Paper", "Scissors");
    }
    
    private final ActionListener actionListener = new ActionListener()
    {
        @Override
        public void onAction(String name, boolean keyPressed, float tpf)
        {
            if (name.equals("Rock") && !keyPressed)
            {
                playerChoice = Choice.ROCK;
                playerCube.getMaterial().getTextureParam("ColorMap").setTextureValue(assetManager.loadTexture("Textures/Rock.png"));
            }
            
            if (name.equals("Paper") && !keyPressed)
            {
                playerChoice = Choice.PAPER;
                playerCube.getMaterial().getTextureParam("ColorMap").setTextureValue(assetManager.loadTexture("Textures/Paper.png"));
            }
            
            if (name.equals("Scissors") && !keyPressed)
            {
                playerChoice = Choice.SCISSORS;
                playerCube.getMaterial().getTextureParam("ColorMap").setTextureValue(assetManager.loadTexture("Textures/Scissors.png"));
            }
        }
    };    

    public Geometry initDisplayCube(String playerName)
    {
        Box playerMesh = new Box(1, 1, 1);
        Geometry tempCube = new Geometry("playerName" + " Box", playerMesh);
        Texture defaultTex = assetManager.loadTexture("Textures/modern.jpg");
        Material playerMat = new Material(assetManager,  "Common/MatDefs/Misc/Unshaded.j3md");
        playerMat.setTexture("ColorMap", defaultTex);
        tempCube.setMaterial(playerMat);
        
        return tempCube;
    }
     
    /**
     * 
     * @param player
     * @param computer
     * @return 
     */
    public static RPS_Game.Result CompareChoices(RPS_Game.Choice player, RPS_Game.Choice computer)
    {
        RPS_Game.Result result;
        
        if(player == RPS_Game.Choice.ROCK && computer == RPS_Game.Choice.SCISSORS)
            result = RPS_Game.Result.WON;
        else if(player == RPS_Game.Choice.PAPER && computer == RPS_Game.Choice.ROCK)
            result = RPS_Game.Result.WON;
        else if (player == RPS_Game.Choice.SCISSORS && computer == RPS_Game.Choice.PAPER)
            result = RPS_Game.Result.WON;
        else if (player == RPS_Game.Choice.ROCK && computer == RPS_Game.Choice.PAPER)
            result = RPS_Game.Result.LOST;
        else if(player == RPS_Game.Choice.PAPER && computer == RPS_Game.Choice.SCISSORS)
            result = RPS_Game.Result.LOST;
        else if(player == RPS_Game.Choice.SCISSORS && computer == RPS_Game.Choice.ROCK)
            result = RPS_Game.Result.LOST;
        else
            result = RPS_Game.Result.TIED;
        
        return result;
    }
    
    public enum Choice
    {
        ROCK, PAPER, SCISSORS
    }
    
    public enum Result
    {
        WON, LOST, TIED
    }
}

Player.java:

 package rock_paper_scissors;

public class Player
{
    private String name;
    private int score;	
    
    public Player()
    {
        name = "";
        score = 0;
    
    }
    
    public Player(String name)
    {
        this.name = name;
        score = 0;
    }
    
    protected void incrementScore()
    {
        score++;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public int getScore()
    {
        return score;
    }

    public void setScore(int score)
    {
        this.score = score;
    }
    
}

AppStates are different states or parts of the app which can be attached or detached during execution. In your code you may want to create an InGameAppState and a MainMenuAppState (and, obviously, create a main menu :stuck_out_tongue: ).

For reference, I have almost nothing in my main SimpleApplication subclass for any of my games.

Here is my simpleInitApp() for spacebugs:

 public void simpleInitApp() {        
        
        setPauseOnLostFocus(false);
        setDisplayFps(false);
        setDisplayStatView(false);
        
        GuiGlobals.initialize(this);
        GuiGlobals.getInstance().setCursorEventsEnabled(false);
 
        GuiGlobals globals = GuiGlobals.getInstance();
        BaseStyles.loadStyleResources("style/scifi.groovy");
        globals.getStyles().setDefaultStyle("scifi");
 
        MainGameFunctions.initializeDefaultMappings(globals.getInputMapper());
        PlayerMovementFunctions.initializeDefaultMappings(globals.getInputMapper());
 
        // Get rid of the default close mapping in InputManager
        //if( inputManager.hasMapping(INPUT_MAPPING_EXIT) ) {
        //    inputManager.deleteMapping(INPUT_MAPPING_EXIT);
        //}
     
        setupPostProcessing();                  
}

…and the stuff setupPostProcessing() is doing should probably be an app state.

I have no simpleUpdate() at all. All of my starting app states are registered by the constructor:

public Main() {
        super(new StatsAppState(), new DebugKeysAppState(), new BasicProfilerState(false),
              new OptionPanelState(), // from Lemur
              new DebugHudState(), // SiO2 utility class
              new StartupLogosState(),
              new MainMenuState(),
              new MessageState(),
              new CommandConsoleState(),
              new CameraState(45, 0.1f, 1000), // should be a utility class, probably
              new ScreenshotAppState("", System.currentTimeMillis()));
}

That’s almost all of my main application class.

Essentially, anything you would ‘extends application’ for can probably be done with an app state… and that gives you flexibility to move things around later. For example, today maybe you jump right into your game, tomorrow maybe you have a main menu first. No big deal because it only depends on when you attach your main game app state.

6 Likes

Thanks. I’ll work on updating the game and come back to share my results.