I looked and the codes seems fine to me. Got an error but hopefully it's the source code, not something wrong with the cpu I have nor JDK.
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.*;
import javax.swing.JFrame;
public class Game extends Canvas implements Runnable {
public static int width = 300;
public static int height = width / 16*9;
public static int scale = 3;
private Thread thread;
private boolean running = false;
private JFrame frame;
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){
tick();
render();
}
}
public void tick() {
}
public void render() {
BufferStrategy bs = getBufferStrategy();
if(bs==null){
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
bs.dispose();
bs.show();
}
public Game() {
Dimension size = new Dimension(width * scale, height * scale);
setPreferredSize(size);
frame = new JFrame();
}
public static void main(String[] args) {
Game game = new Game();
game.frame.setResizable(false);
game.frame.setTitle("Title");
game.frame.add(game);
game.frame.pack();
game.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.frame.setLocationRelativeTo(null);
game.frame.setVisible(true);
game.start();
}
}
Then I got this error, even when I countlessly modified the source code I had.
Exception in thread "Display" java.lang.NullPointerException
at java.awt.Component$BltBufferStrategy.showSubRegion(Component.java:4307)
at java.awt.Component$BltBufferStrategy.show(Component.java:4255)
at com.thecherno.Rain.Game.render(Game.java:58)
at com.thecherno.Rain.Game.run(Game.java:39)
at java.lang.Thread.run(Thread.java:695)
Im starting to seem if it because of an outdated JDK. Current Version I have is JDK 6.
You state:
What Im trying to do is change color as seen in the render method. The background to be black.
Use Swing components such as a JComponent or JPanel.
Simply call setBackground(Color.BLACK) on the component will do.
You appear to be creating a game loop of some type. Consider using a Swing Timer for this.
e.g.,
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Game2 extends JPanel {
private static final int PREF_W = 300;
private static final int PREF_H = PREF_W / 16 * 9;
private static final int SCALE = 3;
private static final Color BACKGROUND = Color.BLACK;
private static final int TIMER_DELAY = 20;
private Timer swingTimer;
public Game2() {
setBackground(BACKGROUND);
swingTimer = new Timer(TIMER_DELAY, new TimerListener());
swingTimer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// TODO: add any custom painting here
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W * SCALE, PREF_H * SCALE);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// TODO add code that gets called in game loop
}
}
private static void createAndShowGui() {
Game2 mainPanel = new Game2();
JFrame frame = new JFrame("Game2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Note that this code is based on your stated requirements and what I'm guessing are other requirements based on your code. If there are further requirements not mentioned, please elaborate them for us.
Try using g.dispose(); followed by bs.show(); and then
g = (Graphics2D)bs.getDrawGraphics();. I know it looks weird, but you are emptying the canvas and then refilling it using your strategy. You may also need to do an initial check for g being null and initialize it before the first display loop.
Related
I was trying to copy a repaint() and a paintComponent() method from a tutorial. After I copied the two methods my paintComponent did not get called and so the rectangle is not being showed. Here is my code:
public class Main {
GameWindow gw;
Main() {
gw = new GameWindow();
}
void start() {
gw.setWindow();
}
public static void main(String[] args) {
new Main().start();
}
}
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameWindow extends JPanel implements Runnable {
final int ORIGINAL_TILE_SIZE = 16;
final int SCALE = 3;
final int TILE_SIZE = ORIGINAL_TILE_SIZE * SCALE;
final int MAX_SCREEN_COLUMNS = 16;
final int MAX_SCREEN_ROWS = 12;
final int SCREEN_WIDTH = TILE_SIZE * MAX_SCREEN_COLUMNS;
final int SCREEN_HEIGHT = TILE_SIZE * MAX_SCREEN_ROWS;
Thread animation;
void setWindow() {
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
window.setTitle("Avontuur");
window.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
window.getContentPane().setBackground(Color.black);
ImageIcon icon = new ImageIcon("C:\\Users\\Rick\\Desktop\\Star.png");
window.setIconImage(icon.getImage());
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
startAnimation();
}
void startAnimation() {
animation = new Thread(this);
animation.start();
}
#Override
public void run() {
while (animation != null) {
update();
repaint();
}
}
public void update() {
}
public void paintComponent(final Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setColor(Color.white);
g2.fillRect(100, 100, TILE_SIZE, TILE_SIZE);
g2.dispose();
}
}
I already tried some solutions from stackOverflow, but they did not work or they were not relevant to my problem. Now the code above is what I tried myself using the video, but after using a println in the method I saw it was not getting called. I expected it to work after watching the tutorial, but it didn't. Does anyone know how I can fix this? Thanks in advance!
Swing is single threaded - never block the Event Dispatching Thread with long running or blocking operations
Swing is NOT thread safe - never update the UI or any state the UI relies on from outside the context of the Event Dispatching Thread.
See Concurrency in Swing for more details.
Swing makes use of a passive rendering engine - you don't control the painting process and you need to work within in it's intended design/workflow, see Painting in AWT and Swing and Performing Custom Painting for more details.
So, what's the solution? In it's simplest form, use a Swing Timer, for example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
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 GamePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class GamePane extends JPanel {
final int ORIGINAL_TILE_SIZE = 16;
final int SCALE = 3;
final int TILE_SIZE = ORIGINAL_TILE_SIZE * SCALE;
final int MAX_SCREEN_COLUMNS = 16;
final int MAX_SCREEN_ROWS = 12;
final int SCREEN_WIDTH = TILE_SIZE * MAX_SCREEN_COLUMNS;
final int SCREEN_HEIGHT = TILE_SIZE * MAX_SCREEN_ROWS;
private Timer timer;
#Override
public Dimension getPreferredSize() {
return new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT);
}
#Override
public void addNotify() {
super.addNotify();
if (timer != null) {
timer.stop();
}
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
update();
}
});
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
if (timer != null) {
timer.stop();
}
}
public void update() {
System.out.println("Updatey update");
repaint();
}
public void paintComponent(final Graphics g) {
super.paintComponent(g);
System.out.println("Painty paint paint");
Graphics2D g2 = (Graphics2D) g.create();
g2.setColor(Color.white);
g2.fillRect(100, 100, TILE_SIZE, TILE_SIZE);
g2.dispose();
}
}
}
You should now see a bunch of text been printed to the console.
Also - you might not, I've made some structural changes to your code, there is not reason for a JPanel based class to create it's own window (or if you really wanted to do this, I'd create a static method to do it, but then I'd be questioning why).
Oh, and also what DontKnowMuchBut Getting Better said in the comments!
Here is the specific rectangle problem with your code, I will not mention other things because is not in the question, you are not adding GameWindow itself as component of your JFrame, and you are not calling your paintComponent method anywhere, I put it on update and it worked fine, but I'm afraid that your code will block and you will not able to move the window and interact with it since you are always calling update/repaint over and over again inside your while statement.
edit.: As the guy in the comment said, we must not call paintcomponent directly, so I fixed.
void setWindow() {
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
window.setTitle("Avontuur");
window.add(GameWindow.this);
window.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
window.getContentPane().setBackground(Color.black);
ImageIcon icon = new ImageIcon("C:\\Users\\Rick\\Desktop\\Star.png");
window.setIconImage(icon.getImage());
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
startAnimation();
}
Attempting to draw my rectangle across the screen horizontally in realtime. When I run this I get nothing but the JFrame. I'm not sure what I am missing aside from maybe some type of threading freeze to redraw the shape maybe?
public class ScreenTest extends JFrame {
int rectY = 50;
public ScreenTest()
{
setSize(300,200);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
private class DrawPanel extends JPanel {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillRect(80, rectY, 50, 50);
}
}
public void Draw()
{
DrawPanel test = new DrawPanel();
add(test);
while (rectY < 200)
{
rectY = rectY + 10;
test.repaint();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ScreenTest myWindow = new ScreenTest();
myWindow.Draw();
}
}
Swing is single threaded and not thread safe.
What this means is, you should not perform any kind of long running or blocking operations within the "Event Dispatching Thread", as this will stop the UI from been painted or responding to new events.
It also means you should not update the UI, or any state the UI relies on, from outside the context of the Event Dispatching Thread.
Your code "is" working, but because the while-loop can run so fast, it's completing before the window is realised on the screen (visible and updatable). Swing is also optimised, so all the repaint calls are likely been consolidated into a single repaint pass.
A better solution might to start with Swing `Timer, which acts as a pseudo repeating loop, but which is called on within the context of the Event Dispatching Thread.
Start by taking a look at Concurrency in Swing and How to Use Swing Timers for more details.
Runnable Example
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class ScreenTest extends JFrame {
public ScreenTest() {
setSize(300, 200);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
private class DrawPanel extends JPanel {
int rectY = 50;
private Timer timer;
// This is just convince
#Override
public void addNotify() {
super.addNotify();
timer = new Timer(25, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
rectY += 1;
repaint();
}
});
// Otherwise it disappears to fast
timer.setInitialDelay(1000);
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
timer.stop();
timer = null;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillRect(80, rectY, 50, 50);
}
}
public void Draw() {
DrawPanel test = new DrawPanel();
add(test);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
ScreenTest myWindow = new ScreenTest();
myWindow.Draw();
}
});
}
}
it is working but is so fast that you can't see it, you need to make the loop which changes the Y coordinate slower with a delay. to solve it i used Thread.sleep() in the while loop:
package paquete;
import javax.swing.*;
import java.awt.*;
public class ScreenTest extends JFrame {
int rectY = 50;
public ScreenTest()
{
setSize(300,200);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
private class DrawPanel extends JPanel {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillRect(80, rectY, 50, 50);
}
}
public void Draw() throws InterruptedException {
DrawPanel test = new DrawPanel();
add(test);
while (rectY < 200)
{
rectY = rectY + 10;
Thread.sleep(100);
test.repaint();
}
}
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
ScreenTest myWindow = new ScreenTest();
myWindow.Draw();
}
}
i hope this helps you, you can change the duration changing the number inside the argument of Thread.sleep()
While using Swing in java, I am trying to move a circle slowly from a starting position to an end position when clicking a button. However, I can't see the circle moving. It just moves from start to end in an instant.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MyApp {
private int x = 10;
private int y = 10;
private JFrame f;
private MyDraw m;
private JButton b;
public void go() {
f = new JFrame("Moving circle");
b = new JButton("click me to move circle");
m = new MyDraw();
f.add(BorderLayout.SOUTH, b);
f.add(BorderLayout.CENTER, m);
f.setSize(500, 500);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
b.addActionListener(new Bute());
}
public static void main(String[] args) {
MyApp m = new MyApp();
m.go();
}
private class Bute implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < 150; i++) {
++x;
++y;
m.repaint();
Thread.sleep(50);
}
}
}
private class MyDraw extends JPanel {
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.white);
g.fillRect(0, 0, 500, 500);
g.setColor(Color.red);
g.fillOval(x, y, 40, 40);
}
}
}
I think the problem is with the action listener because when I'm doing it without using button it is working. Any suggestions?
As Andrew Thompson said, calling Thread.sleep() without defining a second thread freezes everything, so the solution is to define and run another thread like so:
class Bute implements ActionListener, Runnable {
//let class implement Runnable interface
Thread t; // define 2nd thread
public void actionPerformed(ActionEvent e) {
t = new Thread(this); //start a new thread
t.start();
}
#Override //override our thread's run() method to do what we want
public void run() { //this is after some java-internal init stuff called by start()
//b.setEnabled(false);
for (int i = 0; i < 150; i++) {
x++;
y++;
m.repaint();
try {
Thread.sleep(50); //let the 2nd thread sleep
} catch (InterruptedException iEx) {
iEx.printStackTrace();
}
}
//b.setEnabled(true);
}
}
The only problem with this solution is that pressing the button multiple times will speed up the circle, but this can be fixed by making the button unclickable during the animation via b.setEnabled(true/false). Not the best solution but it works.
As said in the comments and another answer, don't block the EDT. Thead.sleep(...) will block it, so you have two options:
Create and manage your own (new) thread.
Use a Swing Timer
In this answer I'll be using a Swing Timer, since it's easier to use. I also changed the paintComponent method to use the Shape API and change the button text to start and stop accordingly as well as reusing the same ActionListener for the button and the timer:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MovingCircle {
private JFrame frame;
private CustomCircle circle;
private Timer timer;
private JButton button;
public static void main(String[] args) {
SwingUtilities.invokeLater(new MovingCircle()::createAndShowGui);
}
private void createAndShowGui() {
frame = new JFrame(this.getClass().getSimpleName());
circle = new CustomCircle(Color.RED);
timer = new Timer(100, listener);
button = new JButton("Start");
button.addActionListener(listener);
circle.setBackground(Color.WHITE);
frame.add(circle);
frame.add(button, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private ActionListener listener = (e -> {
if (!timer.isRunning()) {
timer.start();
button.setText("Stop");
} else {
if (e.getSource().equals(button)) {
timer.stop();
button.setText("Start");
}
}
circle.move(1, 1);
});
#SuppressWarnings("serial")
class CustomCircle extends JPanel {
private Color color;
private int circleX;
private int circleY;
public CustomCircle(Color color) {
this.color = color;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(color);
g2d.fill(new Ellipse2D.Double(circleX, circleY, 50, 50));
}
#Override
public Dimension preferredSize() {
return new Dimension(100, 100);
}
public void move(int xGap, int yGap) {
circleX += xGap;
circleY += yGap;
revalidate();
repaint();
}
public int getCircleX() {
return circleX;
}
public void setCircleX(int circleX) {
this.circleX = circleX;
}
public int getCircleY() {
return circleY;
}
public void setCircleY(int circleY) {
this.circleY = circleY;
}
}
}
I'm sorry, I can't post a GIF as I wanted but this example runs as expected.
I working on a small project and came across something that I didn't know how to fix. I tried looking into threads to see if this was the problem, but couldn't find anything to help me. The problem is that whenever I click on the frame, the key input, animations, etc, all freeze.
Here's the code for the main class: (let me know if you need code from any of the other classes, but I think the problem lies here)
package me.runeglaive.main;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
import me.runeglaive.gamestate.GameStateManager;
public class Game extends Canvas implements Runnable{
private static final long serialVersionUID = 1L;
public static int height = 360;
public static int width = height * 16 / 9;
public static int scale = 2;
private JFrame frame;
private Graphics g;
private boolean running = true;
GameStateManager gsm;
public Game(){
frame = new JFrame("Game");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this, BorderLayout.CENTER);
frame.pack();
frame.setSize(width * scale, height * scale);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
gsm = new GameStateManager();
init();
MouseListener ml = new MouseListener(){
public void mousePressed(MouseEvent e) {
gsm.mousePressed(e);
}
public void mouseReleased(MouseEvent e) {
gsm.mouseReleased(e);
}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
};
KeyListener kl = new KeyListener(){
public void keyPressed(KeyEvent k) {
gsm.keyPressed(k);
}
public void keyReleased(KeyEvent k) {
gsm.keyReleased(k);
}
public void keyTyped(KeyEvent k) {
}
};
frame.addKeyListener(kl);
frame.addMouseListener(ml);
frame.setFocusable(true);
}
public void init(){
}
public void update(double delta){
gsm.update(delta);
}
public void render(){
//double buffering
BufferStrategy bs = getBufferStrategy();
if (bs == null){
createBufferStrategy(2);
return;
}
g = bs.getDrawGraphics();
//clear screen
g.clearRect(0, 0, getWidth(), getHeight());
//Draw gameState
gsm.render(g);
g.dispose();
bs.show();
}
public synchronized void start(){
running = true;
new Thread(this).start();
}
public synchronized void stop(){
running = false;
}
public void run() {
//initialize time loop variables
long lastLoopTime = System.nanoTime();
final int TARGET_FPS = 60;
final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
double lastFpsTime = 0;
while(running)
{
//Calculate since last update
long now = System.nanoTime();
long updateLength = now - lastLoopTime;
lastLoopTime = now;
double delta = updateLength / ((double)OPTIMAL_TIME);
//update frame counter
lastFpsTime += updateLength;
//update FPS counter
if(lastFpsTime >= 1000000000)
{
lastFpsTime = 0;
}
//game updates
update(delta);
//graphics (gameState)
render();
try{
Thread.sleep((Math.abs(lastLoopTime - System.nanoTime() + OPTIMAL_TIME)/1000000));
}catch(InterruptedException e){
System.out.println("Error in sleep");
}
}
}
public static void main(String args[]){
new Game().start();
}
}
Heres the Game State Manager:
package me.runeglaive.gamestate;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
public class GameStateManager {
private ArrayList<GameState> states;
private int currentState;
public static final int MENUSTATE = 0;
public static final int MAINSTATE = 1;
public GameStateManager(){
states = new ArrayList<GameState>();
states.add(new MenuState(this));
states.add(new MainState(this));
setState(MAINSTATE);
}
public void setState(int state){
currentState = state;
states.get(state).init();
}
public int getCurrentState(){
return currentState;
}
public void update(double delta){
states.get(currentState).update(delta);
}
public void render(Graphics g){
states.get(currentState).render(g);
}
public void keyPressed(KeyEvent k){
states.get(currentState).keyPressed(k);
}
public void keyReleased(KeyEvent k){
states.get(currentState).keyReleased(k);
}
public void mousePressed(MouseEvent e){
states.get(currentState).mousePressed(e);
}
public void mouseReleased(MouseEvent e){
states.get(currentState).mouseReleased(e);
}
}
What is the code for gsm.mousePressed(e); and gsm.mouseReleased(e) please? :)
You're misusing Java Swing. You shall not create an own thread and redraw the scene programmatically, since this interferes with the threading model of the input queue.
If you're using swing, then instead of starting an own thread, start up a swing timer. Call the update from the event listener of the timer. Instead of calling render, issue a repaint() on the frame and override the painting code of the frame if you want to play nice.
http://www.oracle.com/technetwork/java/painting-140037.html
In case of AWT, there is both system triggered painting and application triggered paiting.
The code you're doing is a perfectly valid brute force game coder thinking - and perfectly valid if you're having a full control on the screen. In case of Swing, this is not the case, as Swing does take care of a lot more. If you're messing with Mr.Swing, it will bite you sooner or later ;-)
If you prefer taking this game development more seriously, look for a Java 2D game engine, or a Java 3D game engine (such as jMonkey) which takes care of all the details and offer working vertical retrace sync - which is unavoidable for smooth movements. Note that your BufferStrategy will not take care of that, this topic is a way more complex.
My Java program won't display a black screen when I tell it to. I can just see a greyish window with 0 FPS.
Here is my code:
package com.none.rain;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
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 boolean running = false;
private JFrame frame;
private Thread thread;
public Game() {
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() {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void run() {
while (running) {
}
}
public void update() {
}
public void render() {
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.setColor(Color.BLACK);
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();
}
}
Making the display black is something you do in your render() method, but you never call it.
If I take your code and add a call to render() in the run() method I get a frame with a black background:
public void run() {
while (running) {
render();
}
}