Please read the latest update down below
So I am trying to make a brick breaker game and while everything works in terms of gameplay I am having trouble adding a menu system. Basically this is how my code works
public class Game extends Canvas implements Runnable{
public Graphics g;
private Menu menu;
protected Ball ball;
protected Player player;
protected BufferedImage image;
protected BufferStrategy bufferStrategy;
protected Thread thread;
protected JFrame frame;
protected volatile boolean running, gameOver;
private enum STATE{
MENU,
GAME,
ABOUT,
OPTIONS,
};
private STATE State = STATE.MENU;
public Game(){
//Set the Jframe
}
private void init()
{
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
requestFocus();
menu = new Menu();
//init everything else aswell
}
public void update()
{
//Update every moving object
}
#Override
public void run()
{
init();
long initialTime = System.nanoTime();
double timePerFrame = 1000000000/FPS;
double delta = 0;
int ticks = 0;
long timer = 0;
while (running)
{
long currentTime = System.nanoTime();
long elapsedTime = currentTime - initialTime;
delta += elapsedTime/timePerFrame;
timer += elapsedTime;
if (delta >= 1)
{
update();
delta--;
ticks++;
}
render();
initialTime = currentTime;
if (timer >= 1000000000)
{
currentFPS = ticks;
ticks = 0;
timer = 0;
}
}
stop();
}
And when I render everything that is in the STATE GAME it works just fine but when I try to add an else if statement that does menu.draw(g) it all falls apart and I just get a blank frame
Here is how I render
public void render()
{
bufferStrategy = getBufferStrategy();
if (bufferStrategy == null)
{
createBufferStrategy(3);
return;
}
g = bufferStrategy.getDrawGraphics();
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
g.setColor(BG_COLOR);
g.fillRect(0, 0, WIDTH, HEIGHT);
if(State == STATE.GAME){
player.draw(g);
ball.draw(g);
blockController.draw(g); **THESE WORK JUST FINE**
}
else if(State == STATE.MENU){
menu.draw(g); **DOES NOT WORK**
}
bufferStrategy.show();
g.dispose();
}
And my Menu class has no difference in terms of the draw method
public class Menu implements GUI
{
#Override
public void draw(Graphics g) {
g.setFont(new Font("arial", Font.BOLD, 50));
g.setColor(Color.black);
g.drawString("MENU", Game.WIDTH / 2, 100);
}
}
Any idea why this might be happening I am doing the same render litteraly but keep getting
Exception in thread "Thread-0" java.lang.ClassCastException: class sun.java2d.NullSurfaceData cannot be cast to class sun.java2d.d3d.D3DSurfaceData error or g is null error
How can I fix this?
UPDATE ----------------------------------
The menu.draw() in the render works when I remove the lines
g.setFont(new Font("arial", Font.BOLD, 50));
g.setColor(Color.black);
g.drawString("MENU", Game.WIDTH / 2, 100);
And instead add something like
g.setColor(Color.CYAN);
g.fillRect(5, 5, 200, 200);
This does work but why the setfont, setColor and drawString don't work I don't understand I wanted to add buttons aswell but they don't work either. Is it because I set the entire frame with a rectangle in the render with the line g.fillRect(0, 0, WIDTH, HEIGHT); but then can I add objects like paddle,ball,bricks but not a string or a button?
The following example seems to work fine.
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
});
}
public class TestPane extends Canvas {
private Thread thread;
private volatile boolean render = false;
public TestPane() {
}
#Override
public void addNotify() {
super.addNotify();
start();
}
#Override
public void removeNotify() {
super.removeNotify();
stop();
}
protected void start() {
if (thread != null) {
render = false;
try {
thread.join();
} catch (InterruptedException ex) {
}
}
render = true;
thread = new Thread(new Runnable() {
#Override
public void run() {
while (render) {
render();
try {
Thread.sleep(16);
} catch (InterruptedException ex) {
}
}
}
});
thread.start();
}
protected void stop() {
render = false;
if (thread == null) {
return;
}
try {
thread.join();
} catch (InterruptedException ex) {
}
thread = null;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
private Menu menu = new Menu();
protected void render() {
BufferStrategy strategy = getBufferStrategy();
if (strategy == null) {
createBufferStrategy(3);
strategy = getBufferStrategy();
}
if (strategy == null) {
return;
}
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
// Get a new graphics context every time through the loop
// to make sure the strategy is validated
Graphics graphics = strategy.getDrawGraphics();
graphics.setColor(Color.BLACK);
graphics.fillRect(0, 0, getWidth(), getHeight());
menu.render(graphics, getSize());
graphics.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
}
public class Menu {
public void render(Graphics g, Dimension bounds) {
// This is probably going to cost you a lot of
// performance and it might be better to
// pre-create the font instead
g.setFont(new Font("Arial", Font.BOLD, 50));
g.setColor(Color.WHITE);
String text = "MENU";
FontMetrics fm = g.getFontMetrics();
g.drawString("MENU", (bounds.width - fm.stringWidth(text)) / 2, (bounds.height / 2) - fm.getAscent());
}
}
}
If you continue to have issues, continue providing a runnable example which demonstrates your issue
Related
I started remaking my graphics engine and i have a problem with repainting the VolatileImage on the JFrame, i added an FPS counter and when it is in full-screen it only gets to around 250 or 280 FPS even though i have the loop in a new thread and I'm only drawing 2 dots on the screen.
Here is the code for the init and render functions in the Window class, might be a bit unorganized:
`
public int render() {
frame.getGraphics().drawImage(vImg, 0, 0, frame.getWidth(), frame.getHeight(), null);
totalFrames++;
if (System.nanoTime() > lastFPScheck + 1000000000) {
lastFPScheck = System.nanoTime();
currentFPS = totalFrames;
totalFrames = 0;
}
if (vImg.validate(gc) == VolatileImage.IMAGE_OK) {
vImg = gc.createCompatibleVolatileImage(frame.getWidth(), frame.getHeight());
}
if (debugging()) {
frame.setTitle(title + "[FPS: " + currentFPS + "]");
}
graphics = (Graphics2D)vImg.getGraphics();
graphics.setColor(bg_color);
graphics.fillRect(0, 0, frame.getWidth(), frame.getHeight());
return currentFPS;
}
public void repaint_buffer() {
frame.getContentPane().getGraphics().drawImage(vImg, 0, 0, canvasWidth, canvasHeight, null);
}
public void init(int width,
int height,
String title,
Color bg_color,
Consumer<Void> onDestroy) {
this.canvasWidth = width;
this.canvasHeight = height;
this.is_open = true;
this.title = title;
this.bg_color = bg_color;
this.frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
onDestroy.accept(null);
}
});
this.frame.addWindowListener(new WindowAdapter() {
public void windowResized(WindowEvent e) {
vImg = gc.createCompatibleVolatileImage(frame.getWidth(), frame.getHeight());
}
});
this.frame.setLayout(null);
this.frame.setSize(canvasWidth, canvasHeight);
this.frame.addKeyListener(keyInput);
this.frame.setResizable(true);
this.frame.setLocation(-7, 0);
this.frame.setTitle(title);
this.frame.setVisible(true);
gc = this.frame.getGraphicsConfiguration();
vImg = gc.createCompatibleVolatileImage(this.frame.getWidth(), this.frame.getHeight());
this.graphics = (Graphics2D) vImg.getGraphics();
}
`
and here is the code from the Main class, the GraphicsManager object only contains the set_pixel function which sets the color and draws a rectangle at the given position with a size of 1x1 pixels:
package obsidian.core;
import obsidian.core.Window;
import java.awt.*;
public class Main {
private static void on_destroy() {
System.exit(0);
}
public static void main(String[] args) {
Window window = new Window();
window.init(Window.get_max_size().width, Window.get_max_size().height, "title", new Color(0, 0, 0), destroy -> on_destroy());
GraphicsManager gm = new GraphicsManager(window);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
while(window.is_open()) {
window.render();
gm.draw_pixel(0.5, 0.5, new Color(255, 0, 0));
gm.draw_pixel(0, 0, new Color(255, 255, 255));
System.out.println(window.get_current_fps());
}
}
});
t.start();
}
}
I tried removing the VolatileImage and directly drawing on the frame but the filling of the image makes it glitchy but it got to about 2000 FPS and IntelliJ IDEA couldn't keep up. The only solution I could find to make it run faster is removing the VolatileImage and draw directly to the frame. Didn't work ;-;
Can anyone help me with this? Thank you
Swing "passive" painting
This makes use of a Swing Timer
~170fps
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.Instant;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Timer timer;
private Instant lastTick;
private int fps = 0;
public TestPane() {
}
#Override
public void addNotify() {
super.addNotify();
if (timer != null) {
timer.stop();
}
timer = new Timer(5, new ActionListener() {
private int frameCount = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (lastTick != null) {
Duration duration = Duration.between(lastTick, Instant.now());
if (duration.toMillis() >= 1000) {
lastTick = Instant.now();
fps = frameCount;
frameCount = 0;
} else {
frameCount++;
}
} else {
lastTick = Instant.now();
}
repaint();
}
});
timer.start();
}
#Override
public void removeNotify() {
if (timer != null) {
timer.stop();
}
timer = null;
super.removeNotify();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fillOval(20, 20, 20, 20);
g2d.fillOval(40, 40, 20, 20);
FontMetrics fm = g2d.getFontMetrics();
String text = Integer.toString(fps);
g2d.drawString(text, 10, fm.getAscent());
g2d.dispose();
}
}
}
"Active" painting via a BufferStrategy
~680fps
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.atomic.AtomicBoolean;
public class Other {
public static void main(String[] args) {
new Other();
}
public Other() {
Frame frame = new Frame();
MainCanvas canvas = new MainCanvas();
frame.setBackground(Color.BLUE);
frame.add(canvas);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
public class MainCanvas extends Canvas {
private Thread thread;
private AtomicBoolean running = new AtomicBoolean(false);
private int fps;
public MainCanvas() {
}
#Override
public void addNotify() {
super.addNotify();
createBufferStrategy(3);
start();
}
#Override
public void removeNotify() {
stop();
super.removeNotify();
}
public void start() {
stop();
run();
}
protected void stop() {
if (running.get()) {
running.set(false);
}
thread = null;
}
protected void run() {
stop();
running.set(true);
thread = new Thread(new Runnable() {
private Instant lastTick;
private int frameCount;
#Override
public void run() {
while (running.get()) {
if (lastTick != null) {
Duration duration = Duration.between(lastTick, Instant.now());
if (duration.toMillis() >= 1000) {
lastTick = Instant.now();
fps = frameCount;
frameCount = 0;
} else {
frameCount++;
}
} else {
lastTick = Instant.now();
}
render();
// Comment out these lines and see where it takes you
try {
Thread.sleep(1);
} catch (InterruptedException ex) {
java.util.logging.Logger.getLogger(Other.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
}
}
});
thread.start();
}
protected void render() {
BufferStrategy bs = getBufferStrategy();
while (bs == null) {
bs = getBufferStrategy();
}
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
// Get a new graphics context every time through the loop
// to make sure the strategy is validated
Graphics2D g2d = (Graphics2D) bs.getDrawGraphics();
g2d.setColor(Color.GREEN);
g2d.fillRect(0, 0, getWidth(), getHeight());
// Render to graphics
// ...
g2d.setColor(Color.RED);
g2d.fillOval(20, 20, 20, 20);
g2d.fillOval(40, 40, 20, 20);
FontMetrics fm = g2d.getFontMetrics();
String text = Integer.toString(fps);
g2d.drawString(text, 10, fm.getAscent());
// Dispose the graphics
g2d.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (bs.contentsRestored());
// Display the buffer
bs.show();
// Repeat the rendering if the drawing buffer was lost
} while (bs.contentsLost());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
}
Disclaimer
This is by far a "scientific" example and is intended simply as a demonstration between passive and active rendering workflows
I'm trying to make a 2D tile based game. I have been encountering an issue when attempting to draw new graphics and the old ones don't seem to have been removed.
If anyone knows why g.dispose isn't clearing the graphics then please help.
Here's my "Main" class:
package Main;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
public class Main extends Canvas implements Runnable {
private static final long serialVersionUID = 1L;
private JFrame frame;
static int size = 40;
static int tilesX = 20;
static int tilesY = 20;
static int width = size * 10;
static int height = size * 10;
private boolean running = false;
public static STATE state = STATE.MENU;
public static PLAYER type = PLAYER.ARCHER;
private Thread thread;
static tileMap grid = new tileMap();
tile[][] map = tileMap.map;
public Main() {
addKeyListener(new controls());
addMouseListener(new mouse());
Dimension wSize = new Dimension(width, height);
setPreferredSize(wSize);
frame = new JFrame();
}
public synchronized void start() {
running = true;
thread = new Thread(this, "game");
thread.start();
}
public synchronized void stop() {
running = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public enum STATE {
MENU, GAME
}
public enum PLAYER {
ARCHER, KNIGHT
}
public static void main(String[] args) {
Main game = new Main();
game.frame = new JFrame("Game");
game.frame.add(game);
game.frame.setResizable(false);
game.frame.pack();
game.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.frame.setVisible(true);
game.start();
}
public void run() {
while (running) {
if (state == STATE.MENU) {
menu();
} else if (state == STATE.GAME) {
tick();
render();
}
try {
Thread.sleep(16);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void menu() {
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(2);
return;
}
Graphics g = bs.getDrawGraphics();
g.drawRect(Main.width / 8, 20, Main.width / 3, 200);
g.drawString("Archer", Main.width / 8, 20);
g.drawRect((Main.width - Main.width / 3) - Main.width / 8, 20,
Main.width / 3, 200);
g.drawString("Knight", (Main.width - Main.width / 3) - Main.width / 8,
20);
g.dispose();
bs.show();
}
private void render() {
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
Graphics2D d = (Graphics2D) g;
camera.setCam();
g.translate(-camera.camX, -camera.camY);
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map[i].length; j++) {
d.setColor(map[i][j].getC());
d.fillRect(map[i][j].getX(), map[i][j].getY(), Main.size, Main.size);
d.setColor(Color.BLACK);
d.drawRect(map[i][j].getX(), map[i][j].getY(), Main.size, Main.size);
}
}
map[player.p.getX()][player.p.getY()].setC(player.p.getC());
if (type == PLAYER.ARCHER) {
d.drawString("Archer", 5, 15);
} else if (type == PLAYER.KNIGHT) {
d.drawString("Knight", 5, 15);
}
g.dispose();
bs.show();
}
static public void moved() {
tileMap.map[player.p.getX()][player.p.getY()].setC(Color.GREEN);
}
private void tick() {
if (player.p == null) {
player.createP();
}
}
}
Graphics#dispose releases any internal resources that the Graphics context may be holding, reducing the memory overhead, it does not "clear" the context.
From the JavaDocs
Disposes of this graphics context and releases any system resources that it is using. A Graphics object cannot be used after dispose has been called.
What it doesn't do is effect the underlying content, that would be, well, annoying, as using a copy of a Graphics object is a good and easy way to make complex changes without effecting the original context.
To "clear" the context you could use fillRect to paint a color/background before you performing you next cycle of painting. Do this just after Graphics g = bs.getDrawGraphics();
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.
I believe my code will describe what I wrote, But the problem is, is that When I run it, I get that infamous "Exception in thread "Animator" java.lang.IllegalArgumentException: Width (0) and height (0) cannot be <= 0" Error. I have searched for answers but have yet to find why this is happening.
package com.shparki.TowerDefense;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import javax.swing.JPanel;
public class GamePanel extends JPanel implements Runnable{
//==================== PROGRAM VARIABLES ====================//
private static final int WIDTH = 300;
private static final int HEIGHT = WIDTH / 16 * 9;
private static final int SCALE = 4;
private static final int TARGET_FPS = 45;
private static final int PERIOD = 1000 / TARGET_FPS;
private boolean debug = true;
private volatile boolean running = false;
private volatile boolean gameOver = false;
private volatile boolean paused = false;
private Thread animator;
private TowerDefense frame;
private double currentFPS;
//==================== PROGRAM METHODS ====================//
public GamePanel(TowerDefense td){
frame = td;
setMinimumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setMaximumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setVisible(true);
}
public void addNotify(){
super.addNotify();
startGame();
}
public void startGame(){
if (!running && animator == null){
animator = new Thread(this, "Animator");
animator.start();
}
}
public void stopGame(){
running = false;
}
public void run(){
running = true;
Long beforeTime, timeDiff, sleepTime;
while(running){
beforeTime = System.currentTimeMillis();
update();
render();
paintScreen();
timeDiff = System.currentTimeMillis() - beforeTime;
sleepTime = PERIOD - timeDiff;
if (sleepTime <= 0){
sleepTime = 5L;
}
currentFPS = (1000 / ( timeDiff + sleepTime ));
try{
Thread.sleep(sleepTime);
} catch (InterruptedException ex) { ex.printStackTrace(); }
}
System.exit(0);
}
//==================== DRAWING METHODS ====================//
private Graphics dbg;
private Image dbImage;
public void paintComponenet(Graphics g){
super.paintComponent(g);
if (dbImage != null){
g.drawImage(dbImage, 0, 0, null);
}
}
public void paintScreen(){
Graphics g;
try{
g = this.getGraphics();
if (g != null & dbImage != null){
g.drawImage(dbImage, 0, 0, null);
}
Toolkit.getDefaultToolkit().sync();
g.dispose();
} catch (Exception ex) { System.out.println("Graphics context error: " + ex); }
}
public void doubleBuffer(){
if (dbImage == null){
dbImage = createImage(getWidth(), getHeight());
if (dbImage == null){
System.out.println("dbImage is null");
return;
} else {
dbg = dbImage.getGraphics();
}
}
}
//==================== UPDATE METHODS ====================//
public void update(){
}
//==================== RENDER METHODS ====================//
public void render(){
doubleBuffer();
Graphics2D g2d = (Graphics2D) dbg;
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, getWidth(), getHeight());
}
}
And this is my frame class:
package com.shparki.TowerDefense;
import java.awt.BorderLayout;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.JFrame;
public class TowerDefense extends JFrame implements WindowListener{
public TowerDefense(String name){
super(name);
setResizable(false);
setLayout(new BorderLayout());
add(new GamePanel(this), BorderLayout.CENTER);
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
public void windowActivated(WindowEvent arg0) { }
public void windowClosed(WindowEvent arg0) { }
public void windowClosing(WindowEvent arg0) { }
public void windowDeactivated(WindowEvent arg0) { }
public void windowDeiconified(WindowEvent arg0) { }
public void windowIconified(WindowEvent arg0) { }
public void windowOpened(WindowEvent arg0) { }
public static void main(String[] args){
new TowerDefense("Tower Defense Game");
}
}
The JFrame has not been packed then the height and widths are requested here in GamePanel
dbImage = createImage(getWidth(), getHeight());
resulting in 0x0 dimension for the JPanel.
The reason is that Swing invokes addNotify (where startGame resides) almost immediately once the parent component has been set for the component.
You could invoke startGame explicitly after pack has been called.
add(gamePanel, BorderLayout.CENTER);
pack();
gamePanel.startGame();
What I'm trying to do is make it so when I run my application, it starts the thread and the image is shown for 3 seconds (3000ms), then the thread stops running.
The path for the image is correct, the image file exists, and the thread itself runs; however, the image doesn't seem to show. What could be wrong? Here is my code:
package org.main;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
public class Splasher extends JPanel implements Runnable {
private static final long serialVersionUID = 1L;
Image image;
ImageIcon splash = new ImageIcon("res/splash.png");
public static Thread DrawSplash = new Thread(new Splasher());
public Splasher() {
setFocusable(true);
image = splash.getImage();
repaint();
}
boolean hasRan = false;
public void run() {
try {
System.out.println("Drawing Splash Screen");
repaint();
Thread.sleep(3000);
System.out.println("Repainted");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void paint(Graphics g) {
g.drawImage(image, 0, 0, null);
}
public Image getImage() {
return image;
}
}
There's not enough to go from you question.
You don't show how you use the splash screen, if it's attached to anything or how you start/use the Thread.
So the problem could be anything...
In addition to everything else VishalK has already pointed out, I would add public static Thread DrawSplash = new Thread(new Splasher()) is a bad idea. You shouldn't use static Threads are threads are non-reentrant, that is, you can run the same thread twice.
This is a little example demonstrating a "fading" splash screen, using a number of Swing Timers
This assumes you're using Java 7, there is away to make it work for Java 6, I've not posted that code.
public class TestSplashScreen01 {
public static void main(String[] args) {
new TestSplashScreen01();
}
public TestSplashScreen01() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
SplashScreen splash = new SplashScreen();
splash.start();
}
});
}
public class SplashScreen extends JWindow {
private SplashPane splash;
public SplashScreen() {
setBackground(new Color(0, 0, 0, 0));
splash = new SplashPane();
add(splash);
pack();
setLocationRelativeTo(null);
}
public void start() {
splash.start();
}
public class SplashPane extends JPanel {
private BufferedImage splash;
private Timer timer;
private float alpha = 0f;
private int duration = 1000;
private long startTime = -1;
public SplashPane() {
try {
splash = ImageIO.read(getClass().getResource("/res/SokahsScreen.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
timer = new Timer(3000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
fadeOut();
}
});
timer.setRepeats(false);
}
protected void fadeOut() {
Timer fadeInTimer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long now = System.currentTimeMillis();
long runTime = now - startTime;
alpha = 1f - ((float) runTime / (float) duration);
if (alpha <= 0.01f) {
alpha = 0f;
((Timer) (e.getSource())).stop();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
dispose();
}
});
}
repaint();
}
});
startTime = System.currentTimeMillis();
fadeInTimer.setRepeats(true);
fadeInTimer.setCoalesce(true);
fadeInTimer.start();
}
protected void fadeIn() {
Timer fadeInTimer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long now = System.currentTimeMillis();
long runTime = now - startTime;
alpha = (float) runTime / (float) duration;
if (alpha >= 1f) {
alpha = 1f;
((Timer) (e.getSource())).stop();
timer.start();
}
repaint();
}
});
startTime = System.currentTimeMillis();
fadeInTimer.setRepeats(true);
fadeInTimer.setCoalesce(true);
fadeInTimer.start();
}
public void start() {
if (!SplashScreen.this.isVisible()) {
alpha = 0f;
SplashScreen.this.setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
fadeIn();
}
});
}
}
#Override
public Dimension getPreferredSize() {
return splash == null ? super.getPreferredSize() : new Dimension(splash.getWidth(), splash.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (splash != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(alpha));
int x = (getWidth() - splash.getWidth()) / 2;
int y = (getHeight() - splash.getHeight()) / 2;
g2d.drawImage(splash, x, y, this);
g2d.dispose();
}
}
}
}
}
NB:
The problem with this example is once you call start, the program will continue to execute, this will require some kind of listener to tell interested parties when the splash screen has completed.
Alternatively, you could use a undecorated modal JDialog
Many mistakes that you have done in your code...
You have overriden paint method instead of paintComponent to draw Image.
You have used Thread for drawing and removing image on Swing component (JPanel). You should use javax.swing.Timer instead.
Although you should use javax.swing.Timer , but still the basic mistake that you did is that You have created a static Thread and within that passed a new object of Splasher instead of current object.
Each Time you want to make changes in the Graphics of a Component You should call repaint explicitly. For example, If you want to make the image to disappear after 3 seconds you should call repaint method after 3 seconds laps , and should have the proper logic written inside paintComponent method to remove that image.
Here is the modified version of your code , which is doing exactly what you are looking for.Have a look on it.:
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentAdapter;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.Timer;
import javax.swing.SwingUtilities;
public class Splasher extends JPanel {
private static final long serialVersionUID = 1L;
Image image;
ImageIcon splash = new ImageIcon("apple.png");
MyComponentListener componentListener ;
Timer timer ;
public Splasher()
{
componentListener = new MyComponentListener();
setFocusable(true);
image = splash.getImage();
timer = new Timer(3000, new LoadAction());
addComponentListener(componentListener);
}
boolean hasRan = false;
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if (image == null && timer !=null )
{
g.clearRect(0, 0, getWidth(), getHeight()) ;
timer.stop();
removeComponentListener(componentListener);
}
else
{
g.drawImage(image, 0, 0, null);
}
}
public Image getImage()
{
return image;
}
private class MyComponentListener extends ComponentAdapter
{
#Override
public void componentResized(ComponentEvent evt)
{
System.out.println("Resized..");
timer.start();
}
}
private class LoadAction implements ActionListener
{
public void actionPerformed(ActionEvent evt)
{
System.out.println("Drawing Splash Screen");
repaint();
image = null;
repaint();
System.out.println("Repainted");
}
}
public static void main(String st[])
{
SwingUtilities.invokeLater ( new Runnable()
{
#Override
public void run()
{
JFrame frame = new JFrame("Splash:");
frame.getContentPane().add(new Splasher());
frame.setSize(300,500);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
});
}
}
Also Watch the following tutorial for Paint mechanism in java. http://docs.oracle.com/javase/tutorial/uiswing/painting/closer.html