In my current game there are supposed to be different states, for example when I am in the Main Menu of the game, the state would turn "MainMenu", if I am in the game (The game GAME) the state would be "Game".
As it is right now it just starts the game GAME and not the MainMenu. So my question is how can I add it so that it works the way it should?
EDIT: I want the game to start in the MainMenu-STATE of course, in case that wasn't clear :) And then press "play" to start the Game GAME, "Exit" to exit or "options" and so on.
EDIT 2: The first code has the non-working Statefunction. The code at the bottom has a Statefunction that does work. Why can't I seem to make it work for the first code?
Code:
package Game.Window;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.util.Random;
import Game.Framework.AudioPlayer;
import Game.Framework.KeyInput;
import Game.Framework.ObjectId;
import Game.Framework.Texture;
import Game.Interfaces.MainMenu;
import Game.Objects.Block;
import Game.Objects.Player;
public class Game extends Canvas implements Runnable{
private static final long serialVersionUID = -3258890003398871268L;
private boolean running = false;
private Thread thread;
public static int WIDTH, HEIGHT;
// Buffers the "Level" and "Background"
private BufferedImage Level = null, Background = null;
Handler handler;
Camera cam;
static Texture tex;
Random rand = new Random();
public enum STATE{
MainMenu,
Game,
};
public static STATE gameState = STATE.MainMenu;
public void init(){
WIDTH = getWidth();
HEIGHT = getHeight();
AudioPlayer.load();
AudioPlayer.getMusic("music").loop();
tex = new Texture();
BufferedImageLoader loader = new BufferedImageLoader();
Level = loader.loadImage("/Level.png"); // Loads the image "Level"
Background = loader.loadImage("/BackGround.png"); // Loads the image "Background"
handler = new Handler();
cam = new Camera(0, 0);
LoadImageLevel(Level);
this.addKeyListener(new KeyInput(handler, null));
}
public synchronized void start(){
if(running)
return;
running = true;
thread = new Thread(this);
thread.start();
}
// The Game_Loop
public void run(){
init();
this.requestFocus();
long lastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
int updates = 0;
int frames = 0;
while(running){
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while(delta >= 1){
tick();
updates++;
delta--;
}
render();
frames++;
if(System.currentTimeMillis() - timer > 1000){
timer += 1000;
System.out.println("FPS: " + frames + " - TICKS: " + updates);
frames = 0;
updates = 0;
}
}
}
private void tick(){
if(Game.gameState == STATE.Game){
handler.tick();
}else if(Game.gameState == STATE.MainMenu){
//MainMenu.tick();
}
for(int i = 0; i < handler.object.size(); i++){
if(handler.object.get(i).getId() == ObjectId.Player){
cam.tick(handler.object.get(i));
}
}
}
private void render(){
BufferStrategy bs = this.getBufferStrategy();
if(bs == null){
this.createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
Graphics2D g2d = (Graphics2D) g;
// The background
g.setColor(new Color(25, 191, 224));
g.fillRect(0, 0, getWidth(), getHeight());
// Beginning of the Camera
g2d.translate(cam.getX(), cam.getY());
for(int xx = 0; xx < Background.getWidth() * 10; xx += Background.getWidth())
g.drawImage(Background, xx, 50, this);
handler.render(g);
g2d.translate(-cam.getX(), -cam.getY());
// Ending of the Camera
g.dispose();
bs.show();
}
private void LoadImageLevel(BufferedImage image){
int w = image.getWidth();
int h = image.getHeight();
System.out.println("Width, Height: " + w + " " + h);
for(int xx = 0; xx < h; xx++){
for(int yy = 0; yy < h; yy++){
int pixel = image.getRGB(xx, yy);
int red = (pixel >> 16) & 0xff;
int green = (pixel >> 8) & 0xff;
int blue = (pixel) & 0xff;
if(red == 255 && green == 255 && blue == 255) handler.addObject(new Block(xx*32, yy*32, 0, ObjectId.Block));
if(red == 0 && green == 255 && blue == 0) handler.addObject(new Block(xx*32, yy*32, 1, ObjectId.Block));
if(red == 0 && green == 38 && blue == 255) handler.addObject(new Player(xx*32, yy*32, handler, ObjectId.Player));
}
}
}
public static Texture getInstance(){
return tex;
}
}
The MainMenu:
package Game.Interfaces;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import Game.Framework.AudioPlayer;
import Game.Window.Game;
import Game.Window.Game.STATE;
import Game.Window.Handler;
public class MainMenu extends MouseAdapter{
private Game game;
private Handler handler;
public MainMenu(Game game, Handler handler){
this.game = game;
this.handler = handler;
}
public void mousePressed(MouseEvent e){
int mx = e.getX();
int my = e.getY();
if(Game.gameState == STATE.MainMenu){
if(mouseOver(mx, my, 10, 10, 50, 50)){
AudioPlayer.getSound("menu_sound").play();
Game.gameState = STATE.Game;
return;
}
}
}
public void mouseReleased(MouseEvent e){
}
private boolean mouseOver(int mx, int my, int x, int y, int WIDTH, int HEIGHT){
if(mx > x && mx < x + WIDTH){
if(my > y && my < y + HEIGHT){
return true;
}else return false;
}else return false;
}
public void tick(){
}
public void render(Graphics g){
if(Game.gameState == STATE.MainMenu){
Font fnt1 = new Font("Arial", 1, 50);
g.setFont(fnt1);
g.setColor(Color.WHITE);
g.drawRect(10, 10, 100, 100);
g.drawString("Main Menu", 10, 10);
}
}
}
In the tick and render methods I know that I will need to tick and render only the specific STATE, so if I were in the MainMenu it would only tick and render it.
These states works in this game:
package Game;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.util.Random;
public class Game extends Canvas implements Runnable{
private static final long serialVersionUID = 7364682855700581664L;
public static final int WIDTH = 800, HEIGHT = WIDTH / 12 * 9;
private Thread thread;
private boolean running = false;
public static boolean paused = false;
public int diff = 0;
// 0 = EASY
// 1 = NORMAL
// 2 = HARD
private Random r;
private Handler handler;
private HUD hud;
private Spawner spawner;
private Menu menu;
public enum STATE {
Menu,
Options,
Game,
Select,
Lose
};
public static STATE gameState = STATE.Menu;
public Game(){
hud = new HUD();
handler = new Handler();
menu = new Menu(this, handler, hud);
this.addKeyListener(new KeyInput(handler, this));
this.addMouseListener(menu);
AudioPlayer.load();
AudioPlayer.getMusic("music").loop();
new Window(WIDTH, HEIGHT, "Test Game", this);
spawner = new Spawner(handler, hud, this);
r = new Random();
if(gameState == STATE.Game){
handler.addObject(new Player(WIDTH/2-32, HEIGHT/2-32, ID.Player, handler));
handler.addObject(new BasicEnemy(r.nextInt(Game.WIDTH - 50), r.nextInt(Game.HEIGHT - 50), ID.BasicEnemy, handler));
}
}
public synchronized void start(){
thread = new Thread(this);
thread.start();
running = true;
}
public synchronized void stop(){
try{
thread.join();
running = false;
}catch(Exception e){
e.printStackTrace();
}
}
public void run(){
this.requestFocus();
long lastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
int frames = 0;
while(running){
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while(delta >= 1){
tick();
delta--;
}
if(running)
render();
frames++;
if(System.currentTimeMillis() - timer > 1000){
timer += 1000;
System.out.println("FPS: " + frames);
frames = 0;
}
}
stop();
}
private void tick(){
if(gameState == STATE.Game){
if(!paused){
hud.tick();
spawner.tick();
handler.tick();
if(HUD.PlayerHealth < 1){
HUD.PlayerHealth = 100;
//hud.setLevel(1);
//hud.setScore(0);
gameState = STATE.Lose;
handler.clearEnemies();
}
}
}else if(gameState == STATE.Menu || gameState == STATE.Lose || gameState == STATE.Select){
menu.tick();
handler.tick();
}
}
private void render(){
BufferStrategy bs = this.getBufferStrategy();
if(bs == null){
this.createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.setColor(Color.BLACK);
g.fillRect(0, 0, WIDTH, HEIGHT);
handler.render(g);
if(paused){
g.setColor(Color.RED);
g.drawString("PAUSED", 370, 260);
}
if(gameState == STATE.Game){
hud.render(g);
}else if(gameState == STATE.Menu || gameState == STATE.Options || gameState == STATE.Lose || gameState == STATE.Select){
menu.render(g);
}
g.dispose();
bs.show();
}
public static float clamp(float var, float min, float max){
if(var >= max)
return var = max;
else if(var <= min)
return var = min;
else
return var;
}
public static void main(String args[]){
new Game();
}
}
Inherit from a common class, then use a stack and render the item at the top of it. Send input events to that item as well. Once you change state, simply push the new one on the top of the stack and pop it when finished to return to the previous one.
Of course, when the game starts, you must put in the stack the first state to be rendered, that is your menu.
If the states are only two, you can also simply use a variable that stores the active state to render and to which to send input events and so on, then update that variable when you change your state.
In this case, when the game starts, simply set as active the scene that contains your menu.
You can re-design game in two different ways.
Option 1:
Finite State machine with a pre-defined workflow
: Recommended if you know all states of your game in advance and state machine is almost fixed without changes in futureOption 2:
Behavioural tress
: Recommended if there are frequent changes to game behaviour. You can dynamically add behaviour to existing tree without impacting current behaviour.Refer to below related SE question for more details.
How to implement a FSM - Finite State Machine in Java