I'm trying to learn to program my first game and I would like to understand correctly every step I make. I'll face double buffering and other things later.
I'm just trying to load an image in the game loop.
I have two classes. The first it's just a jframe calling the start method.
I wonder if there is some ugly code, in here (I think so).
So, why are my images not showing up?
public class myPanel extends JPanel implements Runnable{
//FIELDS
public static int WIDTH = 1024;
public static int HEIGHT = WIDTH / 16 * 9;
private BufferedImage bg;
private BufferedImage charac;
private boolean running;
private Thread t1;
private int startposX = WIDTH / 2;
private int startposY = HEIGHT / 2;
private int cordX = startposX;
private int cordY = startposY;
int speed = 50;
//METHODS
public synchronized void start (){
running = true;
t1 = new Thread (this);
t1.start();
}
public synchronized void stop (){
running = false;
try {
t1.join();
System.out.println("The game stopped");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//INIT
public myPanel(){
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setFocusable(true);
requestFocus();
addKeyListener(this);
}
//MAIN RUN METHOD
public void run(){
while (running){
load();
System.out.println("The game runs");
repaint();
}
}
//PAINT WITH GRAPHICS METHOD
public void paint (Graphics g){
super.paint(g);
g.drawImage(bg, 0, 0, this);
g.drawImage(charac, 110, 280, this);
}
//LOAD IMAGES IN MEMORY
public void load (){
try {
String path1 = "res/bg.png";
bg = ImageIO.read(new File (path1));
String path2 = "res/charac.png";
charac = ImageIO.read(new File (path2));
} catch (IOException e) {
e.printStackTrace();
}
}
So, I did some testing about paint vs paintcomponent and I just saw that the first draws on top of the jpanel background. Anyway, I can't still see anything changing the lines to
public void run(){
while (running){
load();
System.out.println("The game runs");
}
}
//PAINT WITH GRAPHICS METHOD
public void paintComponent (Graphics g){
super.paint(g);
g.drawImage(bg, 0, 0, null);
g.drawImage(charac, 110, 280, null);
}
//LOAD IMAGES METHOD AS ABOVE
And yes, I've added the panel to the frame, here is the other class
public class Game {
public static void main (String [] args){
JFrame frame = new JFrame();
frame.setIgnoreRepaint(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new myPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
myPanel game = new myPanel();
game.start();
}
Related
I have a problem where my JFrame is constantly flashing white and black, but I only set the colour to black. I think it has to do with the while (running) {} bit.
It just turns white and black forever, until I close it. I really don't know what is going on.. I only just started to use JFrame so I'm sure I have just put some wrong code.
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;
public static boolean running = false;
private Thread thread;
private JFrame frame;
public Game() {
Dimension window = new Dimension(width * scale, height * scale);
setPreferredSize(window);
frame = new JFrame();
}
public synchronized void start() {
running = true;
thread = new Thread(this, "Display");
thread.start();
}
public synchronized void stop() {
try {
running = false;
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void run() {
while (running) {
render();
}
}
public void update() {
}
public void render() {
BufferStrategy buffer = getBufferStrategy();
if (buffer == null) {
createBufferStrategy(3);
return;
}
Graphics g = buffer.getDrawGraphics();
g.setColor(Color.BLACK);
g.drawRect(0, 0, getWidth(), getHeight());
g.dispose();
buffer.show();
}
public static void main(String[] args) {
Game game = new Game();
game.frame.setResizable(false);
game.frame.setTitle("Game");
game.frame.add(game);
game.frame.pack();
game.frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
game.frame.setLocationRelativeTo(null);
game.frame.setVisible(true);
game.start();
}
}
Solved it myself... I used the method drawRect() And I read the documentation page and it says it only draws the outline of the rectangle.. So I just did drawRect()
Also I changed the buffer to 2.
Sorry for wasting your time.
In more detail, I have a componentResized event attached to the JFrame (which contains the canvas, and nothing else), and in that event a call a method which sets the bounds of the canvas accordingly. This works fine, except that while I'm resizing the canvas, it doesn't show anything. I just see the back of the JFrame. Once I've stopped resizing the JFrame, the canvas paints fine again.
public class MyCanvas implements ComponentListener {
public static void main(String[] args) {
new MyCanvas("MyCanvas",new Dimension(300,300));
}
private static final int frameRate = 30;
private JFrame frame;
private JPanel panel;
private Canvas canvas;
private BufferStrategy strategy;
private int delta;
private boolean running = false;
private int frameCount = 0;
public MyCanvas(String name, Dimension size) {
frame = new JFrame(name);
panel = (JPanel) frame.getContentPane();
canvas = new Canvas();
frame.setSize(size);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setPreferredSize(size);
panel.setLayout(null);
canvas.setBounds(0,0,size.width,size.height);
panel.add(canvas);
canvas.setIgnoreRepaint(true);
frame.setResizable(true);
frame.pack();
frame.addComponentListener(this);
canvas.createBufferStrategy(2);
strategy = canvas.getBufferStrategy();
running = true;
frame.setVisible(true);
long lastLoopTime = 0;
while (running) {
frameCount++;
delta = (int) (System.currentTimeMillis() - lastLoopTime);
lastLoopTime = System.currentTimeMillis();
Graphics2D graphics = (Graphics2D) strategy.getDrawGraphics();
graphics.setColor(Color.black);
graphics.fillRect(0,0,getSize().width,getSize().height);
graphics.dispose();
strategy.show();
try {
Thread.sleep(1000/frameRate);
} catch (InterruptedException e) {}
}
}
public final Dimension getSize() {
return frame.getSize();
}
public final void setSize(Dimension size) {
frame.setSize(size);
canvas.setBounds(0,0,size.width,size.height);
}
public synchronized void componentResized(ComponentEvent e) {
setSize(frame.getSize());
}
public synchronized void componentHidden(ComponentEvent e) {
// unused
}
public synchronized void componentShown(ComponentEvent e) {
// unused
}
public synchronized void componentMoved(ComponentEvent e) {
// unused
}
}
Edit
After tweaking with the code for a while, I have come up with a solution:
public class MyCanvas {
public static void main(String[] args) {
new MyCanvas("MyCanvas",new Dimension(400,400));
}
private static final int frameRate = 1000 / 30;
private JFrame frame;
private JPanel panel;
private int delta;
private long lastLoopTime;
private boolean running = false;
private int frameCount = 0;
private BufferedImage backBuffer = null;
private int lastPaintFrame = -1;
public MyCanvas(String name, Dimension size) {
frame = new JFrame(name);
panel = new JPanel() {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
redraw(g);
}
};
frame.setContentPane(panel);
frame.setSize(size);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setPreferredSize(size);
panel.setLayout(null);
frame.setResizable(true);
running = true;
frame.setVisible(true);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
backBuffer = new BufferedImage(screenSize.width,screenSize.height,BufferedImage.TYPE_INT_ARGB);
lastLoopTime = System.nanoTime();
while (running) {
long thisLoopTime = System.nanoTime();
delta = (int) ((thisLoopTime - lastLoopTime) / 1000000);
draw(backBuffer.getGraphics());
frameCount++;
lastLoopTime = thisLoopTime;
redraw(panel.getGraphics());
try {
Thread.sleep(1000/30);
} catch (InterruptedException e) {}
}
}
private final void redraw(Graphics g) {
if (g != null && backBuffer != null) {
g.drawImage(backBuffer,0,0,null);
}
}
int x = 30;
public final void draw(Graphics g) {
g.setColor(Color.darkGray);
g.fillRect(0,0,getSize().width,getSize().height);
g.setColor(Color.gray);
g.fillRect(0,0,500,500);
g.setColor(Color.blue);
g.fillRect(x,30,300,300);
x++;
}
public final Dimension getSize() {
return frame.getSize();
}
public final void setSize(Dimension size) {
frame.setSize(size);
}
}
However, this is not quite solved yet, because it still has odd little graphical glitches which only seem to appear when redraw is called from panel's paintComponent method, though not consistently. Those glitches manifest themselves as odd rectangles of color (usually black or grey) which promptly disappear again. I'm really not sure of what it could be... maybe problems with the double buffering? BTW, if I threw a runtime exception in paintComponent, it worked perfectly.
If this should be moved to a new question, please let me know.
I found the solution: different loops for draw and update:
public class MyCanvas {
public static void main(String[] args) {
new MyCanvas("MyCanvas",new Dimension(400,400));
}
private static final int frameRate = 1000 / 30;
private JFrame frame;
private JPanel panel;
private int delta;
private long lastLoopTime;
private volatile boolean running = false;
private int frameCount = 0;
public MyCanvas(String name, Dimension size) {
frame = new JFrame(name);
panel = new JPanel() {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
if (running) repaint();
}
};
frame.setContentPane(panel);
frame.setSize(size);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setPreferredSize(size);
panel.setLayout(null);
frame.setResizable(true);
running = true;
frame.setVisible(true);
lastLoopTime = System.nanoTime();
new Thread(()->{
while (running) {
update();
frameCount++;
try {
Thread.sleep(frameRate);
} catch (InterruptedException e) {}
}
},"Game Loop").start();
}
int x = 30;
public final void update() {
x++;
}
public final void draw(Graphics g) {
g.setColor(Color.darkGray);
g.fillRect(0,0,getSize().width,getSize().height);
g.setColor(Color.gray);
g.fillRect(0,0,500,500);
g.setColor(Color.blue);
g.fillRect(x,30,300,300);
}
public final Dimension getSize() {
return frame.getSize();
}
public final void setSize(Dimension size) {
frame.setSize(size);
}
}
I have a problem with IDEA 2016.2.2
I wrote a threading demo with flying ball
import javax.swing.*;
import java.awt.*;
public class Ball extends JFrame implements Runnable{
private DrawPanel drawPanel = new DrawPanel();
private int b_x; // ball's x
private int b_y; // ball's y
private int b_d; // ball's diameter
private Thread thread = null;
private JButton button;
private boolean flag = false;
private Ball(){
initGUI();
b_x = b_y = b_d = 50;
thread = new Thread(this);
thread.start();
}
private void initGUI(){
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Ball Thread Demo");
this.setSize(500, 500);
this.setLocationRelativeTo(null);
this.setLayout(new BorderLayout());
this.add(drawPanel, BorderLayout.CENTER);
button = new JButton("Start");
this.add(button, BorderLayout.NORTH);
button.addActionListener(e -> {
button.setText(flag ? "Start" : "Stop");
flag = !flag;
});
}
#Override
public void run() {
while(true){
//System.out.println();
if(flag) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
b_x += 1;
b_y += 1;
drawPanel.repaint();
}
}
}
class DrawPanel extends JPanel{
#Override
protected void paintBorder(Graphics g) {
g.setColor(Color.white);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
}
#Override
protected void paintChildren(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(new Color(247, 123, 40));
g2d.fillOval(b_x, b_y, b_d, b_d);
}
}
public static void main(String[] args){
new Ball().setVisible(true);
}
}
So, the problem is - if I launch this code via cmd - it works good.
But in IDEA it works only with System.out.println that comment out in run method, another way nothing is happened. Is that an issue of this IDE or I am missing smth important?
b_x, b_y and flag need to be made volatile, since they are updated and read in different threads.
I am building the test application to improve later. I have a Java Graphic Element drawn on a canvas using a Game Loop (update,render). It is a red ball that changes its color when the mouse is placed on top of it.
I am trying to figure out a method to create a JPanel when the mouse is on top of the ball,to show some sort of "Hidden Information" inside the ball. My original idea is to display a histogram made with JFreeChart API as the "Hiden information, so I believe that if I create this JPanel I can later add the histogram to the JPanel created. Similar to this http://www.bitjuice.com.au/research/#hierarchicalclassificationexample. In the link, whenever you put the mouse on top of the rectangles, you display extra information.
So far I've got this code:
*Window.java * (The JFrame)
public class Window extends JFrame {
JLabel title_label = new JLabel();
public Window(int width, int height, String title, Animation animation){
setTitle(title);
setPreferredSize(new Dimension(width,height));
setMaximumSize(new Dimension(width,height));
setMinimumSize(new Dimension(width,height));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setLocationRelativeTo(null);
add(animation);
add(title_label, BorderLayout.SOUTH);
setVisible(true);
animation.start();
}
public void update(){
title_label.setText(Animation.mouseX + " " + Animation.mouseY);
}
}
Animation.java(The game Loop)
public class Animation extends Canvas implements Runnable {
public static final int WIDTH = 1024, HEIGHT = WIDTH/12*9 ;
private Thread thread;
private boolean running = false;
public static int mouseX,mouseY;
public Window window;
Button button = new Button();
public Animation(){
window = new Window(WIDTH, HEIGHT,"Test", this);
addMouseMotionListener(new Handler(window));
addMouseListener(new Handler(window));
}
public void run() {
this.requestFocus();
long lastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000/amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
int frames = 0;
while(running){
long now = System.nanoTime();
delta += (now-lastTime) / ns;
lastTime = now;
while(delta >= 1){
update();
delta--;
}
if(running)
render();
frames++;
if(System.currentTimeMillis() - timer >1000){
//System.out.println(frames);
timer += 1000;
frames = 0;
}
}
stop();
}
public synchronized void start(){
thread = new Thread(this);
thread.start();
running = true;
}
public synchronized void stop(){
try{
thread.join();
running = false;
}catch(Exception e){
e.printStackTrace();
}
}
public static int getMouseX(){
return mouseX;
}
public static int getMouseY(){
return mouseY;
}
public static void setMouseX(int x){
mouseX = x;
}
public static void setMouseY(int y){
mouseY = y;
}
private void update(){
window.update();
button.update();
}
private void render(){
BufferStrategy bs = this.getBufferStrategy();
if(bs == null){
this.createBufferStrategy(4);
return;
}
RenderingHints rh = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
Graphics g = bs.getDrawGraphics();
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHints(rh);
g2d.setColor(Color.white);
g2d.fillRect(0, 0, WIDTH, HEIGHT);
button.render(g);
g.dispose();
g2d.dispose();
bs.show();
}
public static void main(String args[]){
new Animation();
}
}
Handler.java
public class Handler extends MouseAdapter {
int x,y;
private Window window;
public Handler(Window window){
this.window = window;
}
public void mouseMoved(MouseEvent e){
Animation.setMouseX(e.getX());
Animation.setMouseY(e.getY());
}
}
Button.java
public class Button {
Ellipse2D mask;
boolean mouseIsOn = false;
public Button(){
mask = new Ellipse2D.Double(500,350,50,50);
}
public void update(){
if(mask.contains(Animation.mouseX,Animation.mouseY)){
mouseIsOn = true;
}else
mouseIsOn = false;
}
public void render(Graphics g){
if(mouseIsOn)
g.setColor(Color.green);
else
g.setColor(Color.red);
g.fillOval(500,350, 50, 50);
}
}
I appreciate the help.
Heavy weight and light weight components don't mix. JPanel is a lightweight component and Canvas is heavyweight. Heavyweight components always get drawn on top of lightweight ones.
What you may want to do instead is just draw the mouseover portion directly to your canvas. You can use FontMetrics for drawing Strings if that is what you need.
I'm having an issue instantiating a class in Java, essentially it generates a new world every tick, which is a bit frustrating when the program runs.
Whereas all I need to do is instantiate it then access a variable inside the class.
Here's the code:
Background.java
public class Background extends UserView {
private BufferedImage bg;
private static Game game;
public Background(World w, int width, int height) {
super(w, width, height);
try {
bg = ImageIO.read(new File("data/background.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void paintBackground(Graphics2D g) {
super.paintBackground(g);
game = new Game();
g.drawImage(bg, 0, 0, this);
int level = game.getLevel();
g.drawString("Level: " + level, 25, 25);
}
}
Game.java
public Game() {
// make the world
level = 1;
world = new Level1();
world.populate(this);
// make a view
view = new Background(world, 500, 500);
// uncomment this to draw a 1-metre grid over the view
// view.setGridResolution(1);
// display the view in a frame
JFrame frame = new JFrame("Save the Princess");
// quit the application when the game window is closed
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
// display the world in the window
frame.add(view);
// don't let the game window be resized
frame.setResizable(false);
// size the game window to fit the world view
frame.pack();
// make the window visible
frame.setVisible(true);
// get keyboard focus
frame.requestFocus();
// give keyboard focus to the frame whenever the mouse enters the view
view.addMouseListener(new GiveFocus(frame));
controller = new Controller(world.getPlayer());
frame.addKeyListener(controller);
// start!
world.start();
}
/** Run the game. */
public static void main(String[] args) {
new Game();
}
Any help would be appreciated! Thank you!
Well you probably need to think about the class concept nad its dependencies, but this is the easiest and fastest approach in your case to keep only one instance of Game :
public class Background extends UserView {
private BufferedImage bg;
private static Game game = new Game();
public Background(World w, int width, int height) {
super(w, width, height);
try {
bg = ImageIO.read(new File("data/background.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void paintBackground(Graphics2D g) {
super.paintBackground(g);
g.drawImage(bg, 0, 0, this);
int level = game.getLevel();
g.drawString("Level: " + level, 25, 25);
}
}
If you add more code and say what you want and what you get, we can say you more about it.