I am making a java game and at it's heart, it consists of a JFrame that holds a a JLabel that holds the background image. Early in the project this was being displayed. However, after I implemented a Game render method that put healthbars and a character on screen, the background image no longer displays. Here is the Game's render() and main() methods.
public class Game extends Canvas implements Runnable{
private static final long serialVersionUID = 1L;
public static final int WIDTH = 1200, HEIGHT = 600, SCALE = 1;
public static boolean running = false;
public Thread gameThread;
private BufferedImage playerSpriteSheet;
private ImageManager im;
private static Player player;
private static HealthBar healthBars;
public void init(){
ImageLoader loader = new ImageLoader();
playerSpriteSheet = loader.load("/spriteSheet.png");
SpriteSheet pss = new SpriteSheet(playerSpriteSheet);
im = new ImageManager(pss);
player = new Player(0, 0, im);
healthBars = new HealthBar(200, 200);
this.addKeyListener(new KeyManager());
}
public synchronized void start() {
if(running)return;
running = true;
gameThread = new Thread(this);
gameThread.start();
}
public synchronized void stop() {
if(!running)return;
running = false;
try {
gameThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void run() {
init();
long lastTime = System.nanoTime();
final double amountOfTicks = 60D;
double ns = 1_000_000_000/amountOfTicks;
double delta = 0;
long now = System.nanoTime();
while(running)
{
delta += (now - lastTime)/ns;
lastTime = now;
if(delta >= 1)
{
tick();
delta--;
}
render();
}
stop();
}
public void tick() {
player.tick();
}
public void render() {
BufferStrategy bs = this.getBufferStrategy();
if(bs == null)
{
createBufferStrategy(3); //Use 5 at most
return;
}
Graphics g = bs.getDrawGraphics();
//RENDER HERE
player.render(g);
healthBars.render(g);
//END RENDER
g.dispose();
bs.show();
}
public static void main(String[] args)
{
JLabel backgroundImage;
JLabel controlKeyPanel;
JLabel statusLabel;
Game game = new Game();
game.setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
game.setMaximumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
game.setMinimumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
JFrame frame = new JFrame("Java Game");
frame.setResizable(false);
frame.setSize(WIDTH * SCALE, HEIGHT * SCALE);
frame.setLayout(new BorderLayout());
backgroundImage = new JLabel(new ImageIcon("/background.png"));
String htmlButtonGuide = "<html>← - Move Left<br>→ - Move Right<br>A - Attack<br>S - Fire Gun<br>P - Position<br>esc - Exit</html>";
controlKeyPanel = new JLabel(htmlButtonGuide);
statusLabel = new JLabel("Game Status");
frame.add(backgroundImage, BorderLayout.CENTER); //This should be displaying the background image
frame.add(controlKeyPanel, BorderLayout.EAST);
frame.add(statusLabel, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(game);
frame.setVisible(true);
game.start();
}
public static Player getPlayer() {
return player;
}
}
Here is the catch, and where I think part of the issue is visible. Inside the render() method there are these 2 lines:
player.render(g);
healthBars.render(g);
If I put player.render(g) first, then both the player and health bars appear on the screen. However, if I put healthBars.render(g) before player.render(g), then the player doesn't appear. This really confuses me, because I would expect the one that renders second causes the first one to be hidden behind, but the opposite happens. Could anyone point me in the right direction?
BufferStrategy doesn't play well with Swing, as you've taken control of the painting process
Canvas can't be transparent, so it will hide anything beneath it...
When you use frame.add(game) you are replaceing what ever use to be at BorderLayout.CENTER
Instead of mixing lightweight (Swing) and heavy weight (AWT) components, paint the background image as part of your render process
public void render() {
BufferStrategy bs = this.getBufferStrategy();
if(bs == null)
{
createBufferStrategy(3); //Use 5 at most
return;
}
Graphics g = bs.getDrawGraphics();
//RENDER HERE
// Paint background here...
player.render(g);
healthBars.render(g);
//END RENDER
g.dispose();
bs.show();
}
Related
When I run my game the JFrame is just white. Can someone explain?
I have no idea why this is happening and I'm having a really hard time finding out what could be the issue. I hope one of you can explain/know the answer. I look forward to hearing your answer and I would love to continue coding but I'm stuck atm, - Artycal.
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.IOException;
public class Game extends Canvas implements Runnable {
static GraphicsDevice device = GraphicsEnvironment
.getLocalGraphicsEnvironment().getScreenDevices()[0];
private static JFrame frame;
private static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
private BufferedImage image = new BufferedImage((int)width, (int)height, BufferedImage.TYPE_INT_ARGB);
private BufferedImage spriteSheet = null;
private static double width = screenSize.getWidth();
private static double height = screenSize.getHeight();
private boolean running;
private Thread thread;
private BufferedImage player;
public void init() {
BufferedImageLoader loader = new BufferedImageLoader();
try{
spriteSheet = loader.loadImage("/sprite_sheet.png");
}catch (IOException e){
e.printStackTrace();
}
SpriteSheet ss = new SpriteSheet(spriteSheet);
player = ss.grabImage(1, 1, 32, 32);
}
public void run() {
init();
long lastTime = System.nanoTime();
final double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
int updates = 0;
int frames = 0;
long timer = System.currentTimeMillis();
while (running){
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
if (delta >= 1){
tick();
updates++;
delta--;
}
render();
frames++;
if (System.currentTimeMillis() - timer > 1000){
timer += 1000;
System.out.println(updates + " Ticks, Fps " + frames);
updates = 0;
frames = 0;
}
}
stop();
}
private void render() {
BufferStrategy bs = this.getBufferStrategy();
if (bs == null){
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.setColor(new Color(81, 218, 221));
g.drawRect(0, 0, getWidth(), getHeight());
g.setColor(new Color(81, 218, 221));
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
g.setColor(new Color(255, 174, 80));
g.drawImage(player, 100, 100, this);
g.dispose();
bs.show();
}
private void tick() {
}
public static void main(String[] args){
Game game = new Game();
frame = new JFrame("Game");
frame.setMaximumSize(new Dimension((int)width, (int)height));
frame.setMinimumSize(new Dimension((int)width, (int)height));
frame.setPreferredSize(new Dimension((int)width, (int)height));
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.add(game);
frame.pack();
frame.setVisible(true);
//device.setFullScreenWindow(frame);
game.start();
}
private synchronized void start() {
if (running)
return;
running = true;
thread = new Thread(this);
thread.start();
}
private synchronized void stop() {
if (!running)
return;
running = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.exit(1);
}
}
some things to note:
INT_ARGB for BufferedImage has a default alpha of 0 (completely clear) so unless you change the alpha value of the pixels you wont see anything.
drawRect draws only the outline of the rectangle. use fillRect to draw the entire rectangle.
as far as I know, drawing BufferedImages does not use the current color of the Graphics object as they are mainly defined as a rectangle made of different colored pixels. try drawing to the Images own Graphics object if you want to change its color.
you are exactly overlapping two drawings (the image and the rect) so you will only ever see one.
I hope I have finally been of some help.
I'm trying to make a game with a youtube tutorial and I am currently attempting to create and fill a rectangle the size of the window. Instead of going black, it stays grey (my default colour).
Any ideas? Thanks in advance!
Code:
import javax.swing.*;
import java.awt.image.BufferStrategy;
import java.awt.*;
public class Game extends Canvas implements Runnable {
private static final long serialVersionUID = 1L;
public static int width = 300;
public static int height = width / 16 * 9;
public static int scale = 3;
private Thread thread;
private JFrame frame; // Creates a window
private boolean running = false; // Checks to see if the game is running
public Game() {
Dimension size = new Dimension(width * scale, height * scale); // The window size
setPreferredSize(size); // Choosing the size I want
frame = new JFrame(); // Calling the frame
}
public synchronized void start() {
running = true;
thread = new Thread(new Game(), "Display"); // Calling the class
thread.start(); // Starting the class
}
public synchronized void stop() { // Ending the game
running = false;
try {
thread.join();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
public void run() {
while(running) {
update();
render();
}
}
public void update() {
}
public void render() {
BufferStrategy bs = getBufferStrategy();
if(bs == null) {
createBufferStrategy(3); // Triple buffering strategy (speeds up process)
return;
}
Graphics g = bs.getDrawGraphics(); // Create a link between buffer and drawing on screen
g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
g.dispose();
bs.show();
}
public static void main(String[] args) {
Game game = new Game(); // Calling the class to be able to change things in it
game.frame.setResizable(true); // Stopping window changing in size (causes graphical errors)
game.frame.setTitle("Game Window");
game.frame.add(game); // Fills the window with the class (can be done because of 'Canvas')
game.frame.pack(); // Sets the size of the window based on what is in the component above.
game.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.frame.setLocationRelativeTo(null); // Centres the window in the screen
game.frame.setVisible(true);
game.start(); // Starts the game
}
}
Change thread = new Thread(new Game(), "Display"); to use this instead of new Game().
This is a follow up question to an earlier one of mine. I am making a java game, and it is basically a JFrame with a character image, some healthbars consisting of fillRect()s which are all on top of a background image. The problem is the healthbars and character are appearing but the background image isn't.
Here is a shortened version of the Game class which has the main() and render() methods:
public class Game extends Canvas implements Runnable{
public static boolean running = false;
public Thread gameThread;
private BufferedImage playerSpriteSheet;
private ImageManager im;
private static Player player;
private static HealthBar healthBars;
private static BackgroundImage backgroundImage;
public void init(){
ImageLoader loader = new ImageLoader();
playerSpriteSheet = loader.load("/spriteSheet.png");
SpriteSheet pss = new SpriteSheet(playerSpriteSheet);
im = new ImageManager(pss);
backgroundImage = new BackgroundImage("/background.png");
player = new Player(800, 250, im);
healthBars = new HealthBar(200, 200);
this.addKeyListener(new KeyManager());
}
public synchronized void start() {
if(running)return;
running = true;
gameThread = new Thread(this);
gameThread.start();
}
public synchronized void stop() {
if(!running)return;
running = false;
try {
gameThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void run() {
init();
long lastTime = System.nanoTime();
final double amountOfTicks = 60D;
double ns = 1_000_000_000/amountOfTicks;
double delta = 0;
long now = System.nanoTime();
while(running)
{
delta += (now - lastTime)/ns;
lastTime = now;
if(delta >= 1)
{
tick();
delta--;
}
render();
}
stop();
}
public void tick() {
player.tick();
}
public void render() {
BufferStrategy bs = this.getBufferStrategy();
if(bs == null)
{
createBufferStrategy(3); //Use 5 at most
return;
}
Graphics g = bs.getDrawGraphics();
//RENDER HERE
backgroundImage.render(g);
player.render(g);
healthBars.render(g);
//END RENDER
g.dispose();
bs.show();
}
public static void main(String[] args)
{
JLabel backgroundImage;
JLabel controlKeyPanel;
JLabel statusLabel;
Game game = new Game();
game.setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
game.setMaximumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
game.setMinimumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
JFrame frame = new JFrame("Title");
frame.setResizable(false);
frame.setSize(WIDTH * SCALE, HEIGHT * SCALE);
frame.setLayout(new BorderLayout());
backgroundImage = new JLabel(new ImageIcon("/background.png"));
String htmlButtonGuide = "words";
controlKeyPanel = new JLabel(htmlButtonGuide);
statusLabel = new JLabel("label");
frame.add(backgroundImage, BorderLayout.CENTER);
frame.add(controlKeyPanel, BorderLayout.EAST);
frame.add(statusLabel, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(game);
frame.setVisible(true);
game.start();
//Program seems to continue running after ESC
}
public static Player getPlayer() {
return player;
}
}
Here is the BackGroundImage class:
public class BackgroundImage {
private Image background = null;
public BackgroundImage(String s) {
if(s == null)
{
background = getImage(s);
}
}
public void render(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(background, 0, 0, 1200, 600, null);
}
public Image getImage(String path) {
Image tempImage = null;
File image2 = new File(path);
try {
tempImage = ImageIO.read(image2);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return tempImage;
}
}
I am concerned with the render() method reusing the "g" Graphics object for adding all 3 things to the screen. I was told to not combine lightweight Swing health bars with the heavy AWT background and character? Can anyone point me in the right direction to get the background to show? Should the render method not take care of the background? I only need the background to be put up once. It doesn't need to be constantly updated like the health bars and characters right?
Let's start with...
backgroundImage = new BackgroundImage("/background.png");
Which becomes...
File image2 = new File(path);
or
File image2 = new File("/background.png");
so you can see it...can you see a problem with this? This is requesting a file which resides at the root location of the current drive...not really what I think you want...
The images are stored in a folder called "res" in the main project folder
Which would suggest you want to use...
backgroundImage = new BackgroundImage("res/background.png");
Assuming that the images are not embedded resources....
Next...
public BackgroundImage(String s) {
if (s == null) {
background = getImage(s);
}
}
So, you only ever want to try a load the image when it's reference is null???
Side notes...
frame.setSize(WIDTH * SCALE, HEIGHT * SCALE); is a bad idea, as frames have borders which occupy space within side the frame itself.
Better to override the getPreferredSize method of Canvas and provide a default size value you want to use and then call pack on the frame instead. This will calculate the size of the frame as the preferred size of it's content PLUS the frame border requirements...
You "game loop" is running wild...
while (running) {
delta += (now - lastTime) / ns;
lastTime = now;
if (delta >= 1) {
tick();
delta--;
}
render();
}
Basically, this will run as fast as it possibly can and will reduce the opportunity for other threads to run, eventually bringing your game (and probably your PC) to it's knees
This is "simple" concept of a run loop...
public void run() {
init();
final long amountOfTicks = 60;
long ns = Math.round(1_000_000_000 / (double)amountOfTicks);
int frames = 0;
long frameStart = System.currentTimeMillis();
while (running) {
long startedAt = System.nanoTime();
tick();
render();
long completedAt = System.nanoTime();
long duration = completedAt - startedAt;
long frameEnd = System.currentTimeMillis();
if (frameEnd - frameStart >= 1000) {
System.out.println(frames);
frames = 0;
frameStart = System.currentTimeMillis();
} else {
frames++;
}
long rest = ns - duration;
if (rest > 0) {
rest = TimeUnit.MILLISECONDS.convert(rest, TimeUnit.NANOSECONDS);
try {
Thread.sleep(rest);
} catch (InterruptedException ex) {
}
}
}
stop();
}
Basically, it tries to ensure that there is enough delay between each iteration in order to maintain the 60fps you are trying to target...without starving the system...
I'm following a Java game programming series on youtube, and all have been going well until we add some code to the program. The code for the program is:
package com.fagyapong.rain;
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
public class Game extends Canvas implements Runnable{
private static final long serialVersionUID = -247215114548172830L;
public static int width = 300;
public static int height = width / 16 * 9;
public static int scale = 3;
private JFrame frame;
public Thread thread;
private boolean running = false;
public Game() {
// Setup Game window
Dimension size = new Dimension(width * scale, height * scale);
setPreferredSize(size);
frame = new JFrame();
}
public synchronized void start() {
running = true;
thread = new Thread(this, "Display");
thread.start();
}
public synchronized void stop() {
running = false;
try {
thread.join();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
public void run() {
while (running) {
update();
render();
}
}
public void update() {
}
public void render() {
// Get the canvas' BufferStragy object
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.setColor(Color.GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
g.dispose();
bs.show();
}
public static void main(String[] args) {
Game game = new Game();
game.frame.setResizable(false);
game.frame.setTitle("Rain");
game.frame.add(game);
game.frame.pack();
game.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.frame.setLocationRelativeTo(null);
game.frame.setVisible(true);
game.start();
}
}
Below is code that causes the system freeze(it has been commented out in the above code)
Graphics g = bs.getDrawGraphics();
g.setColor(Color.GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
g.dispose();
bs.show();
Okay, I figured out the problem a bit. I was having the same issue. The issue for me was the triple buffering. Instead, set the code to:
createBufferStrategy(2);
That way it's only double buffering. I don't have a fantastic computer, so I had to set it to 1 instead of 2. At that point, my guess is that it's not buffering at all. This is how I got it to work though.
Im working on a game in Java and having an issue (i believe its with the content pane) when rendering. I have a screen class which draws the background and all sprites to an Image. The frame then displays the image using a doubleBuffer. For some odd reason tho the image is rendering off the edge of the frame. You can see in the link below that the image is rendering 3 pixels to the left and 28 pixels above where it should be. Does anyone have any idea what could be causing this?
![enter image description here][1]
http://imageshack.us/photo/my-images/41/weirdg.png/
public class Game extends JFrame implements Runnable{
private static final long serialVersionUID = 1L;
//graphics
public BufferStrategy buffy;
BufferedImage image;
Screen screen;
public Boolean running = false;
public Boolean playerTurn = false;
public InputManager input;
public Level level;
//JButton b;
public static final int HEIGHT = 452;
public static final int WIDTH = 768;
public Game() {
super("GridWars");
setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel drawPanel = new JPanel();
drawPanel.setPreferredSize(new Dimension(WIDTH, HEIGHT));
drawPanel.setLayout(null);
drawPanel.setOpaque(false);
//drawPanel.setLocation(50,50);
setContentPane(drawPanel);
setResizable(false);
pack();
setLocationRelativeTo(null);
setVisible(true);
requestFocus();
createBufferStrategy(2);
//b = new JButton("this sucks");
//getContentPane().add(b);
//b.setBounds(300, 300, 100, 50);
buffy = getBufferStrategy();
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
screen = new Screen(WIDTH, HEIGHT);
input = new InputManager(this);
level = new Level(WIDTH, HEIGHT, input, this);
}
public void start() {
running = true;
new Thread(this).start();
}
public void setup(){
}
public void run() {
final double TICKS = 30.0;
final double UPDATE_INTERVAL_NS = 1000000000 / TICKS;
double pastUpdateNS = System.nanoTime();
int updateCount = 0;
int frameCount = 0;
final double FRAPS = 60.0;
final double RENDER_INTERVAL_NS = 1000000000 / FRAPS;
double pastRenderNS = System.nanoTime();
int pastSecondNS = (int) (pastUpdateNS/1000000000);
while(running) {
double nowNS = System.nanoTime();
if(nowNS - pastUpdateNS >= UPDATE_INTERVAL_NS) {
update();
pastUpdateNS += UPDATE_INTERVAL_NS;
updateCount++;
}
float interp = Math.min(1.0f, (float) ((nowNS - pastUpdateNS) / UPDATE_INTERVAL_NS) );
render(interp);
pastRenderNS += RENDER_INTERVAL_NS;
frameCount++;
int thisSecondNS = (int) (pastUpdateNS/1000000000);
if (thisSecondNS > pastSecondNS) {
//System.out.println("TICKS: "+updateCount+" | FRAPS: "+frameCount);
updateCount = 0;
frameCount = 0;
pastSecondNS = thisSecondNS;
}
while( nowNS - pastRenderNS < RENDER_INTERVAL_NS && nowNS - pastUpdateNS < UPDATE_INTERVAL_NS) {
try { Thread.sleep(1); } catch(Exception e) {};
nowNS = System.nanoTime();
}
}
}
public void update() {
input.update();
level.update();
}
public void render(float interp) {
level.render(screen, interp);
image = screen.getImage();
Graphics g = buffy.getDrawGraphics();
g.drawImage(image, 0, 0, null, null);
//b.repaint();
g.dispose();
buffy.show();
}
public static void main(String[] args) {
Game game = new Game();
game.start();
}
}
The 0,0 coordinate of Graphics object you obtain from buffy.getDrawGraphics(); is exactly at top left corner of JFrame and it is ignoring frame decorations.
UPD I forgot one obvious option. JFrame.getInsets() provides information about decorations. You could simply shift your rendering.
You would make frame undecorated (setUndecorated(true)) and render/manage window controls yourself.
Or, and i think it is easier way, you would forget about direct rendering on JFrame, place Canvas on it, and use it instead. Canvas also contains createBufferStrategy method, so you need few simple changes.
JPanel drawPanel = new JPanel();
drawPanel.setLayout(new BorderLayout());
Canvas canvas = new Canvas();
canvas.setPreferredSize(new Dimension(WIDTH, HEIGHT));
drawPanel.add(canvas, BorderLayout.CENTER);
// some code skipped
canvas.setIgnoreRepaint(true); //important
canvas.createBufferStrategy(2);
buffy = canvas.getBufferStrategy();
I've created simple demo with similar render few days ago for another answer. Maybe it will helpful.