From 21b40a8dcc55b3ec365b4a33f08310957155b50d Mon Sep 17 00:00:00 2001 From: Nao Pross Date: Sat, 10 Feb 2018 19:06:21 +0100 Subject: Threaded double buffered rendering Other changes: - Tabs < Spaces - Add jar manifest configuration to gradle.build --- src/main/java/Actor.java | 107 ++++++++++++++++---------------- src/main/java/Palette.java | 18 +++--- src/main/java/Player.java | 6 +- src/main/java/Subconscious.java | 131 +++++++++++++++++++++++++++++++--------- src/main/java/WorldScene.java | 84 ++++++++++++++++---------- 5 files changed, 218 insertions(+), 128 deletions(-) (limited to 'src') diff --git a/src/main/java/Actor.java b/src/main/java/Actor.java index 3d81d7c..7ebdf7f 100644 --- a/src/main/java/Actor.java +++ b/src/main/java/Actor.java @@ -3,59 +3,56 @@ import java.awt.Point; public class Actor { - public enum Type { - PLAYER, ENEMY - } - - - public final String name; - public final Type type; - protected final int MAXHP; - protected int hp; - protected int x, y; - protected Dimension gridSize; - - public Actor(String name, int MAXHP, Type type) { - this.name = name; - this.type = type; - this.MAXHP = MAXHP; - this.hp = this.MAXHP; - } - - public void damage(int dmg) { - this.hp = this.hp - dmg; - if (this.hp < 0) { - this.hp = 0; - } - } - - public void heal(int life) { - this.hp = this.hp + life; - if (this.hp > this.MAXHP) { - this.hp = this.MAXHP; - } - } - - public boolean move(int x, int y) { - if (x < this.gridSize.width && y < this.gridSize.height && x > 0 && y > 0) { - this.x = x; - this.y = y; - return true; - } else { - return false; - } - } - - public int getHp() { - return this.hp; - } - - public int getX() { - return this.x; - } - - public int getY() { - return this.y; - } - + public enum Type { + PLAYER, ENEMY + } + + public final String name; + public final Type type; + protected final int MAXHP; + protected int hp; + protected int x, y; + protected Dimension gridSize; + + public Actor(String name, int MAXHP, Type type) { + this.name = name; + this.type = type; + this.MAXHP = MAXHP; + this.hp = this.MAXHP; + } + + public void damage(int dmg) { + this.hp = this.hp - dmg; + if (this.hp < 0) { + this.hp = 0; + } + } + + public void heal(int life) { + this.hp = this.hp + life; + if (this.hp > this.MAXHP) { + this.hp = this.MAXHP; + } + } + + public boolean move(int x, int y) { + if (x < this.gridSize.width && y < this.gridSize.height && x > 0 && y > 0) { + this.x = x; + this.y = y; + return true; + } else { + return false; + } + } + + public int getHp() { + return this.hp; + } + + public int getX() { + return this.x; } + + public int getY() { + return this.y; + } } diff --git a/src/main/java/Palette.java b/src/main/java/Palette.java index 26838d8..b9282c5 100644 --- a/src/main/java/Palette.java +++ b/src/main/java/Palette.java @@ -1,13 +1,13 @@ import java.awt.Color; public class Palette { - public final static Color BLACK = new Color(29, 31, 33); - public final static Color WHITE = new Color(197, 200, 198); - public final static Color RED = new Color(165, 66, 66); - public final static Color YELLOW = new Color(250, 198, 116); - public final static Color GREEN = new Color(181, 189, 104); - public final static Color DARKGREEN = new Color(140, 148, 64); - public final static Color BLUE = new Color(95, 129, 157); - public final static Color ORANGE = new Color(222, 147, 95); - public final static Color BROWN = new Color(51, 41, 33); + public final static Color BLACK = new Color(29, 31, 33); + public final static Color WHITE = new Color(197, 200, 198); + public final static Color RED = new Color(165, 66, 66); + public final static Color YELLOW = new Color(250, 198, 116); + public final static Color GREEN = new Color(181, 189, 104); + public final static Color DARKGREEN = new Color(140, 148, 64); + public final static Color BLUE = new Color(95, 129, 157); + public final static Color ORANGE = new Color(222, 147, 95); + public final static Color BROWN = new Color(51, 41, 33); } diff --git a/src/main/java/Player.java b/src/main/java/Player.java index 1e9d0b7..e64b765 100644 --- a/src/main/java/Player.java +++ b/src/main/java/Player.java @@ -2,7 +2,7 @@ import java.awt.Dimension; public class Player extends Actor { - public Player(String name, Dimension gridSize) { - super(name, 100, Actor.Type.PLAYER); - } + public Player(String name, Dimension gridSize) { + super(name, 100, Actor.Type.PLAYER); + } } diff --git a/src/main/java/Subconscious.java b/src/main/java/Subconscious.java index 8ce6b15..8fed909 100644 --- a/src/main/java/Subconscious.java +++ b/src/main/java/Subconscious.java @@ -7,87 +7,158 @@ import java.awt.Dimension; import java.awt.event.MouseListener; import java.awt.event.MouseEvent; +import java.awt.event.KeyListener; +import java.awt.event.KeyEvent; +import java.awt.event.ComponentListener; +import java.awt.event.ComponentEvent; +import java.lang.Runnable; +import java.lang.Thread; -public class Subconscious extends JPanel implements MouseListener { +import java.awt.Toolkit; + + +public class Subconscious + extends JPanel + implements Runnable, MouseListener, KeyListener, ComponentListener { public static final String TITLE = "Subconscious"; + public static final long RENDER_PERIOD_MS = 10; /* graphics */ - public JFrame window; - // public Graphics dbGraphics; // double buffered graphics - // public Image dbImage = null; // double buffered image - - public Scene currentScene; + private JFrame window; + private Scene currentScene; + + private Graphics dbg = null; // double buffered graphics + private Image dbImage = null; // double buffered image + + private Thread animator = null; /* game */ - public boolean gameOver = false; - public boolean running = false; + // private volatile boolean gamePaused = false; + private volatile boolean gameOver = false; + private volatile boolean running = false; public Subconscious() { createWindow(); + setFocusable(true); + requestFocus(); + // TODO remove demo currentScene = new WorldScene(new Dimension(200, 200), 50); - addMouseListener(this); + addMouseListener(this); + addKeyListener(this); + addComponentListener(this); } public void createWindow() { window = new JFrame(TITLE); window.setSize(new Dimension(800, 600)); - window.setLocationRelativeTo(null); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.add(this); + window.setMinimumSize(getSize()); + window.setLocationRelativeTo(null); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.add(this); } /* game logic */ public void gameLoop() { - + } public void gameRender() { - + if (dbImage == null) { + dbImage = createImage(getWidth(), getHeight()); + if (dbImage == null) { + System.out.println("ERROR: Cannot create image"); + return; + } + } + + dbg = dbImage.getGraphics(); + dbg.fillRect(0, 0, getWidth(), getHeight()); + + currentScene.render(dbg); } - public void start() { - running = true; - gameOver = false; + public void gameStart() { + if (animator == null || !running) { + animator = new Thread(this); + animator.start(); + } + } + + /* animator and game thread */ + public void run() { + long beforeTime, afterTime, sleepTime; window.setVisible(true); + beforeTime = System.currentTimeMillis(); + + running = true; while (running) { gameLoop(); gameRender(); repaint(); + // could be better with NanoSeconds, but this is + // probably enough for this + afterTime = System.currentTimeMillis(); + sleepTime = RENDER_PERIOD_MS - (beforeTime - afterTime); + try { // TODO replace with correct timing - Thread.sleep(20); + Thread.sleep(sleepTime); } catch (InterruptedException ex) { // } + + beforeTime = System.currentTimeMillis(); } + + System.exit(0); } /* graphics */ @Override public void paintComponent(Graphics g) { - currentScene.render(g); + if (dbImage != null) { + g.drawImage(dbImage, 0, 0, null); + } + + Toolkit.getDefaultToolkit().sync(); + g.dispose(); } - /* listener */ - public void mouseClicked(MouseEvent e) { - int x = e.getX(); - int y = e.getY(); - } - public void mousePressed(MouseEvent e) {} - public void mouseReleased(MouseEvent e) {} - public void mouseEntered(MouseEvent e) {} - public void mouseExited(MouseEvent e) {} + /* listeners */ + public void mousePressed(MouseEvent e) {} + public void mouseReleased(MouseEvent e) {} + public void mouseEntered(MouseEvent e) {} + public void mouseExited(MouseEvent e) {} + public void mouseClicked(MouseEvent e) { + int x = e.getX(); + int y = e.getY(); + } + + public void keyPressed(KeyEvent e) {} + public void keyTyped(KeyEvent e) {} + public void keyReleased(KeyEvent e) { + int keyCode = e.getKeyCode(); + + if (keyCode == KeyEvent.VK_Q && e.isControlDown()) { + running = false; + } + } + + public void componentHidden(ComponentEvent e) {} + public void componentShown(ComponentEvent e) {} + public void componentMoved(ComponentEvent e) {} + public void componentResized(ComponentEvent e) { + dbImage = null; // create new image with the correct size + } /* main */ public static void main(String args[]) { - Subconscious game = new Subconscious(); - game.start(); + Subconscious g = new Subconscious(); + g.gameStart(); } } diff --git a/src/main/java/WorldScene.java b/src/main/java/WorldScene.java index 2c11cba..99ac291 100644 --- a/src/main/java/WorldScene.java +++ b/src/main/java/WorldScene.java @@ -1,3 +1,5 @@ +import java.awt.AlphaComposite; +import java.awt.Composite; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; @@ -6,6 +8,7 @@ import java.awt.Graphics2D; import java.util.ArrayList; + public class WorldScene extends Scene { private ArrayList actors = new ArrayList(); @@ -17,45 +20,64 @@ public class WorldScene extends Scene { this.tileSize = tileSize; map = new Map(gridSize); - Player player = new Player("pipo", gridSize); - this.actors.add(player); + + // TODO remove hardcoded stuff + Player player = new Player("pipo", gridSize); + this.actors.add(player); } @Override public void render(Graphics g) { Graphics2D g2d = (Graphics2D) g; + // draw tiles for (Tile tile : this.map.grid) { - switch (tile.type) { - case GRASS: - g2d.setColor(Palette.GREEN); - break; - case WATER: - g2d.setColor(Palette.BLUE); - break; - } - g2d.fillRect( - this.tileSize * tile.x, - this.tileSize * tile.y, - this.tileSize, this.tileSize - ); + switch (tile.type) { + case GRASS: + g2d.setColor(Palette.GREEN); + break; + case WATER: + g2d.setColor(Palette.BLUE); + break; + } + + g2d.fillRect( + this.tileSize * tile.x, + this.tileSize * tile.y, + this.tileSize, this.tileSize + ); + + // draw grid + // composite (enable alpha) + Composite originalComposite = g2d.getComposite(); + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f)); + + g2d.setPaint(Color.BLACK); + g2d.drawRect( + this.tileSize * tile.x, + this.tileSize * tile.y, + this.tileSize, this.tileSize + ); + + g2d.setComposite(originalComposite); } - for (Actor actor : this.actors) { - switch (actor.type) { - case PLAYER: - g2d.setColor(Palette.ORANGE); - break; - case ENEMY: - g2d.setColor(Palette.RED); - break; - } - int gap = this.tileSize/10; - g2d.fillRect( - (this.tileSize * actor.x) + gap, - (this.tileSize * actor.y) + gap, - this.tileSize-gap, this.tileSize-gap - ); - } + // draw actors + for (Actor actor : this.actors) { + switch (actor.type) { + case PLAYER: + g2d.setColor(Palette.ORANGE); + break; + case ENEMY: + g2d.setColor(Palette.RED); + break; + } + int gap = this.tileSize/10; + g2d.fillRect( + (this.tileSize * actor.x) + gap, + (this.tileSize * actor.y) + gap, + this.tileSize - gap*2, this.tileSize - gap*2 + ); + } } } -- cgit v1.2.1