summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNao Pross <naopross@thearcway.org>2018-11-20 17:07:35 +0100
committerNao Pross <naopross@thearcway.org>2018-11-20 17:07:35 +0100
commit235262919e0863215c2f58a4ab3b977f59442d05 (patch)
tree792b383ce57bf1e191d7cc21f2eb85d371ee5530
parentUpdate gitignore (diff)
downloadSubconscious-java-235262919e0863215c2f58a4ab3b977f59442d05.tar.gz
Subconscious-java-235262919e0863215c2f58a4ab3b977f59442d05.zip
Add Game class, fix GameWindow's scene loading / management
The new Game class contains the state of the game and the main logic to manage maps, actors and scores.
Diffstat (limited to '')
-rw-r--r--src/subconscious/Game.java66
-rw-r--r--src/subconscious/Subconscious.java4
-rw-r--r--src/subconscious/graphics/BattleScene.java5
-rw-r--r--src/subconscious/graphics/GameWindow.java125
-rw-r--r--src/subconscious/graphics/MapScene.java10
-rw-r--r--src/subconscious/graphics/Scene.java80
6 files changed, 229 insertions, 61 deletions
diff --git a/src/subconscious/Game.java b/src/subconscious/Game.java
new file mode 100644
index 0000000..dc7d109
--- /dev/null
+++ b/src/subconscious/Game.java
@@ -0,0 +1,66 @@
+package subconscious;
+
+import java.util.ArrayList;
+
+
+/* Game
+ * Contains informations about the current state of the game used in an
+ * Obervable Object pattern. The Game Loop is managed in the graphics.
+ */
+public class Game {
+ public enum State {
+ MENU, PAUSE, LOADING,
+ // state in which the player is awake (real world)
+ REALITY,
+ // state in which the player is dreaming (imaginary world)
+ DREAM
+ }
+
+ private State state;
+ private boolean stateChanged;
+
+ private boolean running;
+ private boolean gameOver;
+
+ private ArrayList<Actor> actors;
+ private ArrayList<Map> maps;
+
+ public Game() {
+ this.setState(State.MENU);
+
+ this.running = true;
+ this.gameOver = false;
+
+ this.actors = new ArrayList<>();
+ this.maps = new ArrayList<>();
+ }
+
+ public void start() {
+ this.setState(State.DREAM);
+ }
+
+ /* methods to manage the state */
+ public void setState(State state) {
+ if (this.state == state)
+ return;
+
+ this.stateChanged = true;
+ this.state = state;
+ }
+
+ public State getState() {
+ return this.state;
+ }
+
+ public void waitStateChange() {
+ while (!this.stateChanged);
+ this.stateChanged = false;
+ }
+
+ /* accessors */
+ public boolean isRunning() { return running; }
+ public void quit() { this.running = false; }
+
+ public boolean isGameOver() { return gameOver; }
+ public void gameOver() { this.gameOver = true; }
+} \ No newline at end of file
diff --git a/src/subconscious/Subconscious.java b/src/subconscious/Subconscious.java
index 384393a..88d9f70 100644
--- a/src/subconscious/Subconscious.java
+++ b/src/subconscious/Subconscious.java
@@ -4,6 +4,8 @@ import subconscious.graphics.GameWindow;
public class Subconscious {
public static void main(String[] args) {
- GameWindow w = new GameWindow();
+ // TODO: in the future this will be loaded from a save file
+ Game g = new Game();
+ GameWindow w = new GameWindow(g);
}
} \ No newline at end of file
diff --git a/src/subconscious/graphics/BattleScene.java b/src/subconscious/graphics/BattleScene.java
index 6d41c2d..1b5e990 100644
--- a/src/subconscious/graphics/BattleScene.java
+++ b/src/subconscious/graphics/BattleScene.java
@@ -1,5 +1,6 @@
package subconscious.graphics;
+import subconscious.Game;
import subconscious.Actor;
import subconscious.MapLoader;
import subconscious.Tile;
@@ -50,8 +51,8 @@ public class BattleScene extends MapScene {
private int realX = 0;
private int realY = 0;
- public BattleScene() {
- super();
+ public BattleScene(Game g) {
+ super(g);
// TODO: this should be handled in MapScene
MapLoader mapLoader = new MapLoader("../testmap.json");
diff --git a/src/subconscious/graphics/GameWindow.java b/src/subconscious/graphics/GameWindow.java
index f7459fd..ce99df5 100644
--- a/src/subconscious/graphics/GameWindow.java
+++ b/src/subconscious/graphics/GameWindow.java
@@ -1,5 +1,7 @@
package subconscious.graphics;
+import subconscious.Game;
+
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.CardLayout;
@@ -12,6 +14,11 @@ import javax.swing.JButton;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+
+/* GameWindow
+ * This class manages the graphical part of the game, such as loading and
+ * unloading scenes, and the window itself
+ */
@SuppressWarnings("serial")
public class GameWindow extends JFrame implements ActionListener {
public static final Dimension WINDOW_SIZE = new Dimension(600, 400);
@@ -24,26 +31,36 @@ public class GameWindow extends JFrame implements ActionListener {
private Scene scene = null;
private Thread sceneThread = null;
+ private Game game;
+
// TODO: remove map editor, start directly on Battle mode
- public GameWindow() {
+ public GameWindow(Game g) {
super("Subconscious");
+ this.game = g;
+
+ // set up JFrame
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(WINDOW_SIZE);
this.setPreferredSize(WINDOW_SIZE);
this.setLocationRelativeTo(null);
- this.initComponents();
+ // set up components
+ this.buildUi();
this.add(this.root, BorderLayout.CENTER);
this.pack();
this.setVisible(true);
+
+ // start Window Loop
+ this.loop();
}
- private void initComponents() {
+ private void buildUi() {
// set up a cardlayout
this.root = new JPanel(new CardLayout());
+ // TODO: menu should be a Scene
// build Menu card
JPanel menu = new JPanel();
menu.setLayout(new GridLayout(3, 1));
@@ -68,43 +85,82 @@ public class GameWindow extends JFrame implements ActionListener {
this.root.add(menu, MENU_CARD);
}
- public void showScene(Scene scene) {
- // the current scene is already open
- if (scene == this.scene) {
- ((CardLayout)this.root.getLayout()).show(this.root, SCENE_CARD);
- scene.updateCanvasSize();
- scene.resume();
+ // ovserver of this.game
+ private void loop() {
+ while (this.game.isRunning()) {
+ this.game.waitStateChange();
- return;
- }
+ switch (this.game.getState()) {
+ case MENU:
+ this.loadMenu();
+ break;
- // if there is an old scene
- if (this.scene != null) {
- // close old scene
- this.scene.stop();
- try {
- this.sceneThread.join();
- } catch (InterruptedException ex) {
- ex.printStackTrace();
+ case DREAM:
+ this.loadScene(this.scene);
+ break;
}
+
+ }
+ }
+
+ private void loadScene(Scene scene) {
+ // check to not reload the same scene
+ // TODO: this may not be necessary, ex reaload scene?
+ if (this.scene == scene)
+ return;
+
+ if (this.scene != null){
+ this.unloadScene();
}
- // build new thread
+ // build thread
this.scene = scene;
this.sceneThread = new Thread(this.scene);
+ // for debugging
+ this.sceneThread.setName("Scene rendering Thread");
- // add to layout
- this.root.add(scene, SCENE_CARD);
- ((CardLayout)this.root.getLayout()).show(this.root, SCENE_CARD);
+ // add to UI
+ this.root.add(this.scene, SCENE_CARD);
+ ((CardLayout) this.root.getLayout()).show(this.root, SCENE_CARD);
+ // start scene
this.sceneThread.start();
- scene.updateCanvasSize();
- scene.resume();
+ this.scene.updateCanvasSize(this.getSize());
}
- public void showMenu() {
- this.scene.pause();
- ((CardLayout)this.root.getLayout()).show(this, MENU_CARD);
+ private void unloadScene() {
+ // close old scene
+ this.scene.stop();
+ try {
+ // wait for thread to die
+ this.sceneThread.join();
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+
+ // remove card from UI
+ this.root.remove(scene);
+
+ this.scene = null;
+ this.sceneThread = null;
+ }
+
+ // TODO: menu should become a Scene
+ private void loadMenu() {
+ if (this.scene != null)
+ this.scene.pause();
+
+ ((CardLayout) this.root.getLayout()).show(this.root, MENU_CARD);
+ }
+
+ public void quit() {
+ this.setVisible(false);
+
+ if (this.scene != null) {
+ this.unloadScene();
+ }
+
+ this.dispose();
}
// Action Listener for menu
@@ -112,22 +168,17 @@ public class GameWindow extends JFrame implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().startsWith("btn")) {
if (e.getActionCommand().equals("btn-exit")) {
- this.setVisible(false);
- this.dispose();
+ this.quit();
return;
}
- Scene scene = null;
-
if (e.getActionCommand().equals("btn-editor")) {
- scene = new MapEditorScene();
+ this.loadScene(new MapEditorScene());
} else if (e.getActionCommand().equals("btn-battle")) {
- scene = new BattleScene();
+ this.loadScene(new BattleScene(this.game));
+ this.game.start();
}
- if (scene != null) {
- showScene(scene);
- }
}
}
}
diff --git a/src/subconscious/graphics/MapScene.java b/src/subconscious/graphics/MapScene.java
index 6978e1f..0fade19 100644
--- a/src/subconscious/graphics/MapScene.java
+++ b/src/subconscious/graphics/MapScene.java
@@ -1,5 +1,6 @@
package subconscious.graphics;
+import subconscious.Game;
import subconscious.Map;
import subconscious.Tile;
import subconscious.Actor;
@@ -32,7 +33,9 @@ import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;
-
+/* MapScene
+ * Specialization of Scene to render a Map.
+ */
@SuppressWarnings("serial")
public abstract class MapScene extends Scene implements ActionListener {
protected Map map;
@@ -54,6 +57,11 @@ public abstract class MapScene extends Scene implements ActionListener {
protected AffineTransform tx = new AffineTransform();
+
+ public MapScene(Game g) {
+ super(g);
+ }
+
public MapScene() {
super();
}
diff --git a/src/subconscious/graphics/Scene.java b/src/subconscious/graphics/Scene.java
index c49246b..0f678ef 100644
--- a/src/subconscious/graphics/Scene.java
+++ b/src/subconscious/graphics/Scene.java
@@ -1,5 +1,7 @@
package subconscious.graphics;
+import subconscious.Game;
+
import java.awt.Canvas;
import java.awt.Graphics2D;
import java.awt.Dimension;
@@ -20,6 +22,15 @@ import javax.swing.JPanel;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
+
+/* Scene
+ * Abstract model class for all scenes of the game. Each scene is in a different
+ * thread and renders with a double buffered. As threads, scenes can be paused
+ * from rendering.
+ *
+ * It is also responsable for all inputs, which are transferred to the Game
+ * object which controls the internal state of the game.
+ */
@SuppressWarnings("serial")
public abstract class Scene extends JPanel
implements Runnable, KeyListener, MouseListener, MouseMotionListener,
@@ -28,6 +39,8 @@ public abstract class Scene extends JPanel
protected final long DESIRED_FPS = 60;
protected final long DESIRED_DELTA_LOOP = (1000*1000*1000)/DESIRED_FPS;
+ protected Game game;
+
protected boolean running;
protected boolean paused;
protected Object pauseLock;
@@ -40,9 +53,17 @@ public abstract class Scene extends JPanel
protected int guiSize = 2;
protected Scene() {
+ this.game = null;
+
+ // thread control
+ this.running = false;
+ this.paused = false;
+ this.pauseLock = new Object();
+ // watch for resize
this.addComponentListener(this);
+ // set up canvas
this.canvasSize = GameWindow.WINDOW_SIZE;
this.canvas = new Canvas();
@@ -55,6 +76,13 @@ public abstract class Scene extends JPanel
this.canvas.addMouseWheelListener(this);
}
+ protected Scene(Game g) {
+ // call default contructor
+ this();
+
+ this.game = g;
+ }
+
public void run() {
long beginLoopTime;
long endLoopTime;
@@ -69,7 +97,6 @@ public abstract class Scene extends JPanel
this.running = true;
this.paused = false;
- pauseLock = new Object();
while (running) {
beginLoopTime = System.nanoTime();
@@ -121,45 +148,58 @@ public abstract class Scene extends JPanel
public synchronized void resume() {
if (!paused)
return;
-
+
this.paused = false;
- this.pauseLock.notifyAll();
+
+ synchronized (this.pauseLock) {
+ this.pauseLock.notifyAll();
+ }
}
+
// automagically set the canvas size to the parent's size
+ // WARNING: does not always work
+ // TODO: why doesn't this always work?
public synchronized void updateCanvasSize() {
this.canvasSize = this.getParent().getSize();
}
- public synchronized void setCanvasSize(Dimension newSize) {
+ // this one always works
+ public synchronized void updateCanvasSize(Dimension newSize) {
this.canvasSize = newSize;
}
+ /* abstract methods */
protected abstract void render();
protected abstract void update(long deltaNanoTime);
+ /* key listener */
+ @Override public void keyTyped(KeyEvent e) {}
+ @Override public void keyPressed(KeyEvent e) {}
+
@Override
- public void keyTyped(KeyEvent e) {}
- public void keyPressed(KeyEvent e) {}
- public void keyReleased(KeyEvent e) {}
-
- @Override
- public void mouseClicked(MouseEvent e) {}
- public void mouseEntered(MouseEvent e) {}
- public void mouseExited(MouseEvent e) {}
- public void mousePressed(MouseEvent e) {}
- public void mouseReleased(MouseEvent e) {}
- public void mouseDragged(MouseEvent e) {}
- public void mouseMoved(MouseEvent e) {}
- public void mouseWheelMoved(MouseWheelEvent e) {}
+ public void keyReleased(KeyEvent e) {
+ // TODO: Escape pauses the game
+ }
+ /* mouse listener */
+ @Override public void mouseClicked(MouseEvent e) {}
+ @Override public void mouseEntered(MouseEvent e) {}
+ @Override public void mouseExited(MouseEvent e) {}
+ @Override public void mousePressed(MouseEvent e) {}
+ @Override public void mouseReleased(MouseEvent e) {}
+ @Override public void mouseDragged(MouseEvent e) {}
+ @Override public void mouseMoved(MouseEvent e) {}
+ @Override public void mouseWheelMoved(MouseWheelEvent e) {}
+
+ /* component listener */
@Override
public void componentResized(ComponentEvent e) {
this.updateCanvasSize();
}
- public void componentMoved(ComponentEvent e) {}
- public void componentShown(ComponentEvent e) {}
- public void componentHidden(ComponentEvent e) {}
+ @Override public void componentMoved(ComponentEvent e) {}
+ @Override public void componentShown(ComponentEvent e) {}
+ @Override public void componentHidden(ComponentEvent e) {}
}