简体   繁体   English

如何在游戏中添加状态?

[英]How can I add States to my game?

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". 在我当前的游戏中,应该有不同的状态,例如,当我在游戏的主菜单中时,状态将变为“ MainMenu”;如果我在游戏(游戏)中,则状态将为“游戏” ”。

As it is right now it just starts the game GAME and not the MainMenu. 现在,它只是启动游戏而不是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. 编辑:我当然希望游戏在MainMenu-STATE中启动,以防万一:)然后按“播放”开始游戏,“退出”退出或“选项”,依此类推。

EDIT 2: The first code has the non-working Statefunction. 编辑2:第一个代码具有无效的Statefunction。 The code at the bottom has a Statefunction that does work. 底部的代码具有有效的Statefunction。 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: 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. 在tick和render方法中,我知道我将只需要勾选并渲染特定的STATE,所以如果我在MainMenu中,它将只勾选并渲染它。

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: 选项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 future Finite State machine with a pre-defined workflow :如果您事先知道游戏的所有状态并且状态机几乎是固定的,并且以后没有更改,则建议使用

Option 2: 选项2:

Behavioural tress : Recommended if there are frequent changes to game behaviour. Behavioural tress ::如果游戏行为频繁变化,则建议使用。 You can dynamically add behaviour to existing tree without impacting current behaviour. 您可以在不影响当前行为的情况下将行为动态添加到现有树中。

Refer to below related SE question for more details. 有关更多详细信息,请参阅以下相关的SE问题。

How to implement a FSM - Finite State Machine in Java 如何在Java中实现FSM-有限状态机

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM