When i try to set value to BufferedImage called dinoImage in Dino.java in a constructor i just get a blank screen every time (second picture) because repaint() is not being called, but if i set it to null it is working just fine but without this image (first picture).
No exceptions, everything seems fine in this code, this problem appears when i try to set value to this field using static method getImage of Resource.java which uses this line of code ImageIO.read(new File(path)) and it causes that repaint() is not being called, i guess this line causes such weird behavior but i dont know how to solve it.
Main.java
public class Main {
public static void main(String[] args) {
GameWindow gameWindow = new GameWindow();
gameWindow.startGame();
}
}
GameWindow.java
public class GameWindow extends JFrame {
private GameScreen gameScreen;
public GameWindow() {
super("Runner");
setSize(1000, 500);
setVisible(true);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gameScreen = new GameScreen();
add(gameScreen);
}
public void startGame() {
gameScreen.startThread();
}
}
GameScreen.java
public class GameScreen extends JPanel implements Runnable, KeyListener {
private Thread thread;
public static final double GRAVITY = 0.1;
public static final int GROUND_Y = 300;
private Dino dino;
public GameScreen() {
thread = new Thread(this);
dino = new Dino();
}
public void startThread() {
thread.start();
}
#Override
public void run() {
while(true) {
try {
Thread.sleep(20);
dino.updatePosition();
repaint();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// g.clearRect(0, 0, getWidth(), getHeight());
g.setColor(Color.RED);
g.drawLine(0, GROUND_Y, getWidth(), GROUND_Y);
dino.draw(g);
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
System.out.println("Key Pressed");
dino.jump();
}
#Override
public void keyReleased(KeyEvent e) {
System.out.println("Key Released");
}
}
Dino.java
public class Dino {
private double x = 100;
private double y = 100;
private double speedY = 0;
private BufferedImage dinoImage;
public Dino() {
dinoImage = getImage("data/dino.png");
}
public void updatePosition() {
if(y + speedY >= GROUND_Y - 100) {
speedY = 0;
y = GROUND_Y - 100;
} else {
speedY += GRAVITY;
y += speedY;
}
}
public void jump() {
if(y == GROUND_Y - 100) {
speedY = -5;
y += speedY;
}
}
public void draw(Graphics g) {
g.setColor(Color.BLACK);
g.drawRect((int)x, (int)y, 100, 100);
g.drawImage(dinoImage, (int)x, (int)y, null);
}
}
Resource.java
public class Resource {
public static BufferedImage getImage(String path) {
BufferedImage image = null;
try {
image = ImageIO.read(new File(path));
} catch (IOException e) {
e.printStackTrace();
}
return image;
}
}
setSize(1000, 500);
setVisible(true);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gameScreen = new GameScreen();
add(gameScreen);
Swing components need to be added to the frame BEFORE the frame is made visible. Otherwise the panel has a size of (0, 0) and there is nothing to paint.
The code should be something like:
gameScreen = new GameScreen();
add(gameScreen);
setSize(1000, 500);
setVisible(true);
Related
I have made simple JFrame that works fine except when I am trying to access it from other classes it returns null. So I made getter for it (public static JFrame getWindow()) it returns null. If I set JFrame to public and try to access it that way it returns null. When I create it becomes null right after the game engine starts.
Main:
public class Main {
private static String title = "2D SquareWorld 0.";
private static String version = "";
private static JFrame window;
private static Container container;
public static void main(String[] args) {
GameEngine game = new GameEngine();
window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setTitle(title + Version.newVersion());
window.setResizable(false);
window.add(game);
window.setSize(1000, 720);
window.setLocationRelativeTo(null);
window.setVisible(true);
game.start();
}
public static Container getContainer() {
return container;
}
public static JFrame getWindow() {
return window;
}
}
GameEngine:
package GameEngine;
public class GameEngine extends Canvas implements Runnable, KeyListener, MouseListener, MouseMotionListener {
private static final long serialVersionUID = 1L;
private Thread thread;
private boolean running;
private GameStateManager gsm;
public GameEngine() {
gsm = new GameStateManager();
}
public void start() {
thread = new Thread(this, "Game");
thread.start();
running = true;
init();
}
public void init() {
Data.setValue("ScreenWidth", getWidth());
Data.setValue("ScreenHeight", getHeight());
addKeyListener(this);
}
public void update(double delta) {
}
public void render() {
BufferStrategy bs = getBufferStrategy();
if(bs == null) {
createBufferStrategy(2);
return;
}
Graphics g = bs.getDrawGraphics();
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.white);
g2.fillRect(0, 0, getWidth(), getHeight());
gsm.draw(g2);
g2.dispose();
bs.show();
}
public void run() {
long lastLoopTime = System.nanoTime();
long lastFpsTime = 0;
final int TARGET_FPS = Fps.TargetFPS;
final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
int CurrentFps = 0;
while(running) {
long now = System.nanoTime();
long updateLength = now - lastLoopTime;
lastLoopTime = now;
double delta = updateLength / ((double) OPTIMAL_TIME);
lastFpsTime += updateLength;
CurrentFps++;
if (lastFpsTime >= 1000000000) {
Fps.FPS = CurrentFps;
lastFpsTime = 0;
CurrentFps = 0;
}
update(delta);
render();
try {
if(((lastLoopTime-System.nanoTime() + OPTIMAL_TIME) / 1000000) >= 1) {
Thread.sleep((lastLoopTime-System.nanoTime() + OPTIMAL_TIME) / 1000000);
}
}catch(InterruptedException IE) {
}
}
}
public void mouseDragged(MouseEvent me) {
}
public void mouseMoved(MouseEvent me) {
}
public void mouseClicked(MouseEvent me) {
}
public void mouseEntered(MouseEvent me) {
}
public void mouseExited(MouseEvent me) {
}
public void mousePressed(MouseEvent me) {
}
public void mouseReleased(MouseEvent me) {
}
public void keyPressed(KeyEvent ke) {
gsm.keyPressed(ke.getKeyCode());
}
public void keyReleased(KeyEvent ke) {
}
public void keyTyped(KeyEvent ke) {
}
}
I called getWindow from this class:
public class LoginState extends GameState {
private GameStateManager gsm;
public LoginState(GameStateManager gsm) {
this.gsm = gsm;
}
private JTextField username;
private JTextField password;
public void init() {
username = new JTextField(25);
username.setVisible(true);
username.setBounds(20, 20, 50, 50);
Main.getWindow().add(username);
}
public void draw(Graphics2D g) {
g.setColor(Color.gray);
//g.fillRect(0, 0, Data.getIntegerValue("ScreenWidth"), Data.getIntegerValue("ScreenHeight"));
}
GameStateManager:
public class GameStateManager {
private ArrayList<GameState> gameStates;
public static final int LOGINSTATE = 0;
public static final int MENUSTATE = 1;
public static final int PLAYSTATE = 2;
private static int currentState;
public GameStateManager() {
gameStates = new ArrayList<GameState>();
currentState = LOGINSTATE;
gameStates.add(new LoginState(this));
gameStates.add(new MenuState(this));
gameStates.add(new PlayState(this));
gameStates.get(currentState).init();
}
Please help.
Thanks for the update, but... you still haven't shown where LoginWindow is being initialized.
A guess -- you're program is starting from a different main method from the one you're showing, and so the main method which creates and assigns your JFrame to the static window field is never called. I suggest that you avoid using static methods and fields in this way, that depend on a main method to initialize. Java is an OOP language -- so make your code OOP compliant and create needed objects within code guaranteed to be called, and then assign them to non-static fields.
edit:
simply swap the two lines:
GameEngine game = new GameEngine();
window = new JFrame();
to
window = new JFrame();
GameEngine game = new GameEngine();
Your JFrame gets initialized in the main method of Main. You are better off using a static initialization block instead.
static {
window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setTitle(title + Version.newVersion());
window.setResizable(false);
window.add(game);
window.setSize(1000, 720);
window.setLocationRelativeTo(null);
window.setVisible(true);
}
I am a complete noob to Java Graphics.
I wrote a simple "game" in which you control a box with the arrow keys
Here is the source code:
package com.thundercrust.graphics;
public class Drawings extends Canvas implements KeyListener, Runnable {
public static Thread thread;
public static Drawings draw;
private static final long serialVersionUID = 1L;
public static boolean running = false;
public static int x = 640;
public static int y = 320;
public static int bulletX = 0;
public static int bulletY = 0;
public static int direction = 2;
public static boolean fired = false;
public static boolean show = false;
public static String colorCode;
public static final int WIDTH = 1366;
public static final int HEIGHT = WIDTH / 16 * 9;
public static final String title = "A Moving Box!";
JFrame frame = new JFrame();
public void paint(Graphics g) {
Graphics2D g2D = (Graphics2D) g;
g2D.setColor(Color.black);
g2D.fillRect(0, 0, 1366, 768);
g2D.setColor(Color.pink);
g2D.fillRect(50, 50, 1266, 668);
if (colorCode.equals("red")) g2D.setColor(Color.red);
if (colorCode.equals("orange")) g2D.setColor(Color.orange);
if (colorCode.equals("yellow")) g2D.setColor(Color.yellow);
if (colorCode.equals("green")) g2D.setColor(Color.green);
if (colorCode.equals("blue")) g2D.setColor(Color.blue);
if (colorCode.equals("cyan")) g2D.setColor(Color.cyan);
if (colorCode.equals("gray")) g2D.setColor(Color.gray);
g2D.fillRect(x, y, 50, 50);
}
public Drawings() {
frame.addKeyListener(this);
frame.setTitle(title);
frame.setSize(WIDTH, HEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
frame.add(this);
}
public void display() {
while (running = true) {
repaint();
try {
Thread.sleep(30);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String args[]) {
colorCode = JOptionPane.showInputDialog("Enter the color of the box: ");
running = true;
draw = new Drawings();
draw.start();
}
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_UP) { y-= 5; direction = 0; }
if (keyCode == KeyEvent.VK_DOWN) { y+= 5; direction = 2; }
if (keyCode == KeyEvent.VK_LEFT) {x-= 5; direction = 3;}
if (keyCode == KeyEvent.VK_RIGHT) {x+= 5; direction = 1;}
if (keyCode == KeyEvent.VK_Z) System.out.println("You pressed z");
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
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 = true) {
System.out.println("The Game is Running!");
repaint();
try {
Thread.sleep(60);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
The reason why I am asking for help is because the application always flickers and it gets really annoying.
Is there any way to fix this?
Any help would be appreciated.
Canvas is not double buffered and I'd recommend not using it this way. Instead, consider using a JPanel and overriding it's paintComponent method, this will give you double buffering for free.
See Painting in AWT and Swing and Performing Custom Painting for some deatils
Alternatively, you could use BufferStrategy, which would allow you to define you own double buffering strategy as well as take complete control over the painting process, letting you control when the canvas was painted (AKA active painting)
I am trying to get an animation using a spritesheet in JFrame. The problem is the once loaded, the image doesn't change. It only shows the first image.
I have searched a lot and tried almost all the advice but can't get it to work.
By the way, getSprite has a return type of Image.
When I do everything in JFrame, only one image is shown. When I do it in separate class extending JPanel, I get only a white Window.
Update 1: Thanks to andrew, got JFrame working. Fixed my old part too. Here's the working code at ideone
Update 2:
Here's my version with timer.
class TestSpriteSheet extends JFrame{
//same old variables
public TestSpriteSheet(){
//same old stuff goes here before this
add(new PanelSprite(this, ss));
this.setVisible(true);
}
}
class PanelSprite extends JPanel{
private long runningTime = 0;
private int fps = 3;
private boolean stop = false;
private SpriteSheetManager ss;
private TestSpriteSheet temp;
public PanelSprite(TestSpriteSheet test, SpriteSheetManager sm){
ss = sm;
temp = test;
setSize(180,180);
setLayout(new BorderLayout()); init();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
long time = 5000;
animate_with_gfx(g, time);
}
public void init(){
Timer t = new Timer((int)(1000/fps), new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(!stop) {
repaint();
} else {
((Timer)e.getSource()).stop();
}
}
});
t.setRepeats(true);
t.setDelay((int)(1000/fps));
t.start();
}
public void animate_with_gfx(Graphics g, long time){
if(runningTime <= time){
try {
System.out.println(runningTime); //Checking if this part works
int x = 0; int y = 0;
g.drawImage(ss.getSprite(x, y), 40, 40, null);
x++; y++; runningTime+=(1000/fps);
}catch (Exception ex) {
ex.printStackTrace();
}
}
else{
stop = true;
}
}
}
public void paintComponent(Graphics g){
super.paintComponent(g);
long time = 5000; int fps = 3;
boolean stop = false;
Timer t = new Timer((int)(1000/fps), new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(!stop) {
animate_with_gfx(g, time, fps, stop);
repaint();
} else {
((Timer)e.getSource()).stop();
}
}
});
t.setRepeats(true);
t.setDelay((int)(1000/fps));
t.start();
System.exit(0);
}
This part is entirely wrong, since the paintComponent() method is called whenever the JRE thinks the view needs repainting. So definitely remove the System.exit(0);!
Then the Timer needs a single instance, to be started once. That would best be done in the constructor or an init() type method.
It might look something like this:
private int fps = 3;
private boolean stop = false;
public void paintComponent(Graphics g){
super.paintComponent(g);
long time = 5000;
animate_with_gfx(g, time, fps, stop);
}
/** Called from constructor.. */
public void init(){
Timer t = new Timer((int)(1000/fps), new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(!stop) {
repaint();
} else {
((Timer)e.getSource()).stop();
}
}
});
t.setRepeats(true);
t.setDelay((int)(1000/fps));
t.start();
}
Update
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.event.*;
import javax.swing.*;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
class SpriteSheetManager {
private BufferedImage spriteSheet;
int cols;
int rows;
public SpriteSheetManager() {
setSpriteSheet();
}
public void setSpriteSheet() {
try {
spriteSheet = ImageIO.read(
new URL("http://s8.postimg.org/vso6oed91/spritesheet.png"));
setColsAndRows(3, 3);
} catch (IOException e) {
e.printStackTrace();
}
}
public BufferedImage getSpriteSheet() {
return spriteSheet;
}
public void setColsAndRows(int cols, int rows) {
this.cols = cols;
this.rows = rows;
}
public Image getSprite(int x, int y) {
Image sprite = null;
try {
sprite = spriteSheet.getSubimage(
x * spriteSheet.getWidth() / cols,
y * spriteSheet.getHeight() / rows,
spriteSheet.getWidth() / cols,
spriteSheet.getHeight() / rows);
} catch (Exception e) {
e.printStackTrace();
}
return sprite;
}
}
class Ideone {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestSpriteSheet();
}
});
}
}
class TestSpriteSheet extends JFrame {
private static final long serialVersionUID = 1L;
private SpriteSheetManager ss;
public TestSpriteSheet() {
super("Testing SpriteSheets");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setLayout(new BorderLayout());
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent windowEvent) {
System.exit(0);
}
});
ss = new SpriteSheetManager();
add(new PanelSprite(this, ss));
pack();
setSize(200, 200);
this.setVisible(true);
}
}
class PanelSprite extends JPanel {
private long runningTime = 0;
private int fps = 10;
private boolean stop = false;
private SpriteSheetManager ss;
private TestSpriteSheet temp;
private Timer t;
int count = 0;
long time = 50000;
public PanelSprite(TestSpriteSheet test, SpriteSheetManager sm) {
ss = sm;
temp = test;
init();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
animate_with_gfx(g);
}
public void init() {
t = new Timer((int) (1000 / fps), new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!stop) {
repaint();
} else {
((Timer) e.getSource()).stop();
}
}
});
t.setRepeats(true);
t.setDelay((int) (1000 / fps));
t.start();
}
public void animate_with_gfx(Graphics g) {
if (runningTime <= time) {
Image img = ss.getSprite((count % 9) % 3, (count % 9) / 3);
g.drawImage(img, 40, 40, this);
count++;
runningTime += (1000 / fps);
} else {
stop = true;
}
}
}
I want to, when i click the "d" button, start a timer. This is to animate a player walking. The timer doesn't start when i press the key, how do i do that?
The code I have is this:
public class Game extends JPanel implements KeyListener {
//Player variables
private BufferedImage playerStanding;
private BufferedImage playerWalking;
private BufferedImage playerFrame;
private boolean walking = false;
private final int PLAYER_HEIGHT = 100;
private final int PLAYER_WIDTH = 100;
private final int INITIAL_X = 0;
private final int INITIAL_Y = 500;
private int x = INITIAL_X;
private int y = INITIAL_Y;
//The timer I want to start on keypress-> "d"
private Timer playerAnimationTimer;
public Game() {
setPreferredSize(new Dimension(800, 800));
setBackground(Color.CYAN);
//Player
try {
playerStanding = ImageIO.read(getClass().getResource("player1.gif"));
playerWalking = ImageIO.read(getClass().getResource("player2.gif"));
playerFrame = playerStanding;
}
catch (IOException ex) {
ex.printStackTrace();
}
playerAnimationTimer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
walking = !walking;
playerFrame = walking ? playerWalking : playerStanding;
x += 10;
if (x > getWidth() - PLAYER_WIDTH) {
x = INITIAL_X;
}
repaint();
}
});
playerAnimationTimer.setRepeats(true);
}
public Dimension setPreferredSize() {
return new Dimension(800, 800);
}
#Override
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
Graphics2D graphics2D = (Graphics2D) graphics;
if (playerFrame != null) {
graphics2D.drawImage(playerFrame, x, y, PLAYER_WIDTH, PLAYER_HEIGHT, this);
}
graphics2D.dispose();
}
#Override
public void keyTyped(KeyEvent e) {
//This doesn't work
playerAnimationTimer.start();
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
}
//The class to hold the gamepanel
public class StartGame extends JFrame implements ActionListener {
private static JButton startGame = new JButton();
StartGame() {
setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
setSize(200, 100);
setVisible(true);
setBackground(Color.BLUE);
setLocationRelativeTo(null);
startGame.setText("Play!");
startGame.setSize(100, 25);
startGame.addActionListener(this);
add(startGame);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == startGame) {
this.setVisible(false);
new GameWindow();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new StartGame();
}
});
}
}
How could I make the timer start when I click the "d" button?
Your KeyListener doesn't work because you never add the KeyListener to anything much less to a component that has focus, which is needed for a KeyListener to work.
I suggest that you instead use Key Bindings as a cleaner safer way to capture the desired key press.
As an aside, never dispose of a Graphics object that is given to you from the JVM.
For a better answer, please edit your code to make it comply with the mcve standard. You should use no images files, and it should compile and run for us unaltered.
You could set the private Timer like you did and start it like this...
public void startTimer(){
timer.start();
timer.setRepeats(true);
}
I've followed these tutorials and produced the following.
http://www.youtube.com/playlist?list=PL54DB126285ED0420
Main.java:
public class Main extends JFrame {
GamePanel gp;
public Main() {
gp = new GamePanel();
setSize(500, 400);
setVisible(true);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(gp);
}
public static void main(String[] args) {
Main m = new Main();
}
}
GamePanel.java:
public class GamePanel extends JPanel implements Runnable {
// Double Buffering Variables
private Image dbImage;
private Graphics dbg;
// JPanel Variables
static final int GWIDTH = 500, GHEIGHT = 400;
static final Dimension gameDim = new Dimension(GWIDTH, GHEIGHT);
// Game Variables
private Thread game;
private volatile boolean running = false;
public GamePanel() {
setPreferredSize(gameDim);
setBackground(Color.WHITE);
setFocusable(true);
requestFocus();
// Handle all key inputs from the user
addKeyListener(new KeyAdapter() {
#Override public void keyPressed(KeyEvent e) {}
#Override public void keyReleased(KeyEvent e) {}
#Override public void keyTyped(KeyEvent e) {}
});
}
public void run() {
while (running) {
gameUpdate();
gameRender();
paintScreen();
}
}// END run
private void gameUpdate() {
if (running && game != null) {
// update the game state
}
}
private void gameRender() {
// create the buffer
if (dbImage == null) {
dbImage = createImage(GWIDTH, GHEIGHT);
if (dbImage == null) {
System.err.println("dbImage is still null!!!");
return;
} else {
dbg = dbImage.getGraphics();
}
}
// Clear the screen
dbg.setColor(Color.WHITE);
dbg.fillRect(0, 0, GWIDTH, GHEIGHT);
// Draw the game elements
draw(dbg);
}
// draw all game content
public void draw(Graphics g) {}
private void paintScreen() {
Graphics g;
try {
g = this.getGraphics();
if (dbImage != null && g != null)
g.drawImage(dbImage, 0, 0, null);
// For Linux
Toolkit.getDefaultToolkit().sync();
g.dispose();
} catch (Exception e) {
System.err.println(e);
}
}
public void addNotify() {
super.addNotify();
startGame();
}
private void startGame() {
if (game == null || !running) {
game = new Thread(this);
game.start();
running = true;
}
}
public void stopGame() {
if (running)
running = false;
}
private void log(String s) {
System.out.println(s);
}
}
It should just print a "Hello World" string on the screen but it's not performing. I've gone over the code couple of times but couldn't see what was wrong.
So what's absent that causes it not to display the string.
Thanks.
All right. Finally just found it.
In my Main.java I'd to place the add(gp); code to the top. Because basically it was falling under.
P.S. Just mentioning again. Accidentally I erased the contents of the draw method. Silly of me. Sorry for that. It should've g.drawString("Hello World!", 100, 100); in it.
Thanks.
So what's absent that causes it not to display the string.
For starters, the "Hello World" string itself is absent.
I suggest you go over the tutorials that you referenced again as you appear to be new to Java and require more practice.