I am trying to create a 2D platform game, by following this tutorial. I have created an Images()-class which looks like this:
package Resources;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Objects;
public class Images {
public static BufferedImage[] tileBlocks;
public Images(){
tileBlocks = new BufferedImage[1];
try{
tileBlocks[0] = ImageIO.read(
// TODO: Fix the error caused by this try/catch
(Objects.requireNonNull(getClass().getResourceAsStream("TileBlocks/block_brick.png")))
);
} catch (IOException e) { e.printStackTrace(); }
}
}
And I created an instantation of it in my GamePanel()-class, which looks like this
package Main;
import GameState.GameStateManager;
import Resources.Images;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.Serial;
#SuppressWarnings("BusyWait")
public class GamePanel extends JPanel implements Runnable, KeyListener {
#Serial
private static final long serialVersionUID = 1L;
// Game thread
private boolean isRunning = false;
int FPS = 60; // Frames per second
long targetTime = 1000 / FPS; // Set target time
// Set the dimension of the game-window
public static final int WIDTH = 800;
public static final int HEIGHT = 600;
private GameStateManager gsm = new GameStateManager(); // Game state manager object
public GamePanel(){
setPreferredSize(new Dimension(WIDTH, HEIGHT));
addKeyListener(this);
setFocusable(true);
// TODO: Fix error (Reports instantiation of utility classes using the new keyword)
new Images();
start();
}
// Start the game (initialize)
private void start() {
isRunning = true;
Thread thread = new Thread(this); // Referring to Runnable
thread.start(); // Call to our game-loop
}
// Game-loop
public void run() {
long start, elapsed, wait; // Keep track of time
gsm = new GameStateManager(); // Initialize game state manager
while (isRunning) {
start = System.nanoTime(); // Start timer
update(); // Update the game-logic
repaint(); // Re-paint the board (built-in method from java.awt)
elapsed = System.nanoTime() - start;
// If everything runs in under the target time we wait (so it runs equally in all computers)
wait = targetTime - elapsed / 1000000; // Divide by 1 000 000 to get milli-seconds
if (wait <= 0) { wait = 5; } // Keep wait a positive value
try {
Thread.sleep(wait);
} catch (Exception e) { e.printStackTrace(); }
}
}
// Updating all game-logic
public void update() { gsm.update(); }// Call methods from game state manager in the game panel class
// Where we draw the graphics
public void paintComponent(Graphics g){
super.paintComponent(g); // Built-in method from java.awt
g.clearRect(0,0,WIDTH,HEIGHT); // Clear the screen before drawing
gsm.draw(g); // Call method from game state manager in the game panel class
}
public void keyPressed(KeyEvent e) {
gsm.keyPressed(e.getKeyCode()); // getKeyCode() turns KeyEvent into an integer
}
public void keyReleased(KeyEvent e) {
gsm.keyReleased(e.getKeyCode()); // getKeyCode() turns KeyEvent into an integer
}
public void keyTyped(KeyEvent e) { }
}
I get an error saying
Instantiation of utility class 'Images'
Inspection info: Reports instantiation of utility classes using the new keyword.
In utility classes, all fields and methods are static.
Instantiation of such classes is most likely unnecessary and indicates a mistake.
And when I try to run the game it doesn't launch, I only get a NullPointerException
Exception in thread "main" java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:208)
at inf112.skeleton.app/Resources.Images.<init>(Images.java:17)
at inf112.skeleton.app/Main.GamePanel.<init>(GamePanel.java:35)
at inf112.skeleton.app/Main.Game.game(Game.java:10)
at inf112.skeleton.app/Main.Game.main(Game.java:20)
But if I remove the the instantation, the game launches, but then it crashes with even more errors
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException:
Cannot load from object array because "Resources.Images.tileBlocks" is null
etc...
If I remove the Objects.requireNonNull from
tileBlocks[0] = ImageIO.read((getClass().getResourceAsStream("TileBlocks/block_brick.png"))
The game launches, but it crashes and I get another error saying
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException:
Cannot load from object array because "Resources.Images.tileBlocks" is null
What am I doing wrong here? Why is my list of tileBlocks null? How can I read the image and display it on the tileBlocks?
Put file "block_brick.png" in directory "TileBlocks" in classpath.
Related
I'm a beginner in java so sorry if I'm asking a stupid question , but how do I make a new thread in my gui class that would create a progress bar. I have a class named progress and made a new thread in my gui class using the constructor that I have created. But for some reason, I am getting a strange error:
"constructor progress in class NewJFrame.progress cannot be applied to given types;
required: no arguments
found: JProgressBar
reason: actual and formal argument lists differ in length
NewJframe.java
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
if (jRadioButton1.isSelected()){
App m = new App();
Thread t1 = new Thread(new progress(jProgressBar1));
m.sendPingRequest2("104.160.142.3",jTextPane1,jTextPane2,jTextField1);
}
}
progress.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* #author User
*/
import javax.swing.JProgressBar;
public class progress implements Runnable {
private static int DELAY = 500;
JProgressBar progressBar;
public progress (JProgressBar bar) {
progressBar = bar;
}
public void run() {
int minimum = progressBar.getMinimum();
int maximum = progressBar.getMaximum();
for (int i = minimum; i < maximum; i++) {
try {
int value = progressBar.getValue();
progressBar.setValue(value + 1);
Thread.sleep(DELAY);
} catch (InterruptedException ignoredException) {
}
}
}
}
This:
Thread t1= new progress ( jProgressBar1);
Should be:
Thread t1 = new Thread(new progress(jProgressBar1));
since your progress class implements Runnable and does not extend Thread.
Also your error message is strange:
constructor progress in class NewJFrame.progress cannot be applied to given types
suggesting that the problem resides within the constructor of the NewJFrame.progress class, a class that looks to be nested within the NewJFrame class. If this is so, get rid of the nested class and only deal with the free-standing progress (re-name it "Progress" please) class.
But having said that, your code has potential problems as you're changing the state of the JProgressBar, a Swing component, directly from within a background thread, and this is not Swing thread-safe. Much better to use a SwingWorker and link it to the JProgressBar's state as per the JProgressBar standard tutorial (check the link please).
For example:
import java.awt.event.KeyEvent;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
#SuppressWarnings("serial")
public class ProgressFun extends JPanel {
private JProgressBar progressBar = new JProgressBar(0, 100);
public ProgressFun() {
progressBar.setStringPainted(true);
final JButton startProgress = new JButton("Start Progress");
startProgress.setMnemonic(KeyEvent.VK_S);
startProgress.addActionListener(l -> {
startProgress.setEnabled(false);
progressBar.setValue(0);
final MyWorker myWorker = new MyWorker();
myWorker.execute();
myWorker.addPropertyChangeListener(pcEvent -> {
if (pcEvent.getPropertyName().equals("progress")) {
int value = (int) pcEvent.getNewValue();
progressBar.setValue(value);
} else if (pcEvent.getNewValue() == SwingWorker.StateValue.DONE) {
startProgress.setEnabled(true);
try {
myWorker.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
});
});
add(progressBar);
add(startProgress);
}
private static void createAndShowGui() {
ProgressFun mainPanel = new ProgressFun();
JFrame frame = new JFrame("Progress Fun");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class MyWorker extends SwingWorker<Void, Integer> {
#Override
protected Void doInBackground() throws Exception {
int progress = 0;
setProgress(progress);
while (progress < 100) {
progress += (int)(5 * Math.random());
progress = Math.min(progress, 100);
TimeUnit.MILLISECONDS.sleep((int) (500 * Math.random()));
setProgress(progress);
}
return null;
}
}
As an aside, you will want to learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others.
I'm using an image to draw a background for a Java app, a big one without repeat property. To do so, I used bufferedImage to read the resource, the image, and draw it in a Canvas. Well, it works almost fine. Except one thing, the app still opens even though the image are fallen behind.
I mean, I expected to see both the app and image were loaded at the same time. But it seems like the image has only been loaded after the app started, long enough to see the window frame background (gray color cause I'm using Windows 10).
So I tried my solution by setVisible(true); after the Canvas class has been initialized. The result is still the same.
One more exception that I don't know if it's relevant to the image. But after I closed the app, the console has thrown out an exception called 'Closed'. It like after I close my app, there is still a part of my coding running and try to read the image.
Here are my code:
App.class (main class in here)
import engine.MouseAction;
import graphic.Game;
import graphic.Window;
public class App {
MouseAction mouse;
Game game;
Window window;
App() {
init();
setup();
window.launch();//It's just window.setVisible(true);
}
private void init() {
mouse = new MouseAction();
game = new Game();
window = new Window(game, mouse);
}
private void setup() {
mouse.setWindow(window);
game.start();
}
public static void main(String[] args) {
new App();
}
}
Game.class
package graphic;
import java.awt.Canvas;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import engine.Config;
public class Game extends Canvas implements Runnable {
private static final long serialVersionUID = -545327428912119369L;
private Thread thread;
private boolean running = false;
public Game() {}
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();
}
}
private void update() {
}
private void render() {
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
Graphics2D g = (Graphics2D) bs.getDrawGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING , RenderingHints.VALUE_ANTIALIAS_ON);
try {
BufferedImage bg = ImageIO.read(getClass().getResource("/assets/img/bg.jpg"));
g.drawImage(bg, 0, 0, Config.SOL_16x10_small.width, Config.SOL_16x10_small.height, this);
bg.flush();
} catch (IOException e) {
e.printStackTrace();
}
g.dispose();
bs.show();
}
public void run() {
long lastTime = System.nanoTime();
final double amountOfTick = 60.0;
double ns = 1000000000 / amountOfTick; //It's nano second
double delta = 0;
while (running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while (delta >= 1) {
update();
delta--;
}
render();
}
stop();
}
}
That is 2 classes I think it's relevant to my problem. And this code does load images but it loads very slow.
The problem is in your gameloop. You try to load your image file into memory on every iteration of your gameloop, just before you get your graphics context to actually draw it.
Try to load the image into memory beforehand, then simply call the drawing method of Graphics g to render it on every iteration.
You could load the image anywhere before you start your gameloop.
You could also create the bufferstrategy beforehand and then simply get the graphics context in your render() method, the way you do it already.
Ultimately saving the if(bs == null), this will not make any noticeable difference though.
I have no idea how to fix this problem. What I am doing is trying to break up a monster class I have into different things, like one class for player, one for fireball, etc. I had everything working before I tried to break up the class but now I am getting an error. I was wondering if anyone could help me solve it and explain to me how not to repeat this error again. Thank you in advance.
EDIT: The error is on: animationTimer = new Timer(animationDelay, this);
EDIT:1 error found:
File: C:\Users\jozef\Java\Dragon Ball Z\Player.java [line: 45]
Error: incompatible types: Player cannot be converted to java.awt.event.ActionListener
Also, I do format properly but when i try to copy and paste my code into the box to post here it doesn't count it as code so i have to indent every line to get it to appear as code and no normal text.
import java.awt.Graphics;
import java.awt.MediaTracker;
import javax.swing.ImageIcon;
import java.awt.Image;
import java.awt.event.ActionEvent;
import javax.swing.Timer;
public class Player {
int x;
int y;
ImageIcon pictures[];
int total;
int current;
boolean sideMove;
int move;
Timer animationTimer;
int animationDelay = 80;
public Player(int startX, int startY, ImageIcon image[], boolean sideMove, int move) {
x = startX;
y = startY;
pictures = image;
total = pictures.length;
this.sideMove = sideMove;
this.move = move;
startAnimation();
}
public void draw(Graphics g) {
if (pictures[current].getImageLoadStatus() == MediaTracker.COMPLETE) {
Image img = pictures[current].getImage();
g.drawImage(img, x, y, null);
current = (current + 1) % total;
}
update();
}
public void update() {
if (sideMove == true) {
x += move;
} else {
y += move;
}
}
public void startAnimation() {
if (animationTimer == null) {
current = 0;
animationTimer = new Timer(animationDelay, this); // *** error ***
animationTimer.start();
} else if (!animationTimer.isRunning())
animationTimer.restart();
}
public void stopAnimation() {
animationTimer.stop();
}
}
Here:
animationTimer = new Timer(animationDelay, this);
Since the Player class does not implement ActionListener this can not be passed in to the Timer constructor as a valid parameter. A possible solution is to have your Player class implement ActionListener giving it an appropriate actionPerformed method:
public class Player implements ActionListener {
#Override
protected void actionPerformed(ActionEvent e) {
// your coded here
}
// .... rest of your code
or better still, use a different ActionListener such as an anonymous inner class.
e.g.,
public void startAnimation() {
if (animationTimer == null) {
current = 0;
animationTimer = new Timer(animationDelay, e -> timerActionPerformed(e));
animationTimer.start();
} else if (!animationTimer.isRunning()) {
animationTimer.restart();
}
}
private void timerActionPerformed(ActionEvent e) {
// TODO repeated code goes here
}
Side recommendations:
You've got code within your painting method that changes your Player object's state, something that you'll want to avoid. Understand that you can only partially control when or even if an object will be painted, and so it would be best to keep these separate.
Myself, I'd get the Timer out of the Player class and instead use a Timer as your game loop or animation controller in a more general overall control class, perhaps the Game class (or whatever your "universe" class is called), the class that holds and controls all of your logical entities such as your Player objects.
This is the signature of javax.swing.Timer constructor:
public Timer(int delay, ActionListener listener)
You are providing an int and a Player..
You should create an ActionListener as well and provide it to the constructor or you could pass this but Player class should implements ActionListener inteface (you should write actionPerformed method in the Player class).
Read more info about Timer Here (official java doc).
java: I'm stuck on the error The type Pong.Move1 must implement the inherited abstract method
KeyListener.keyTyped(KeyEvent) when I used keyListener. I don't get what it means? Help?Here's the Thread I'm having trouble on...
private class Move1 extends Thread implements KeyListener{
public void run(){
addKeyListener(this);
while(true){
//hitRight makes you lose.
//point is how many times it ricochets.
if(ball.intersects(borderRight)){
hitRight = true;
}
if(ball.intersects(borderLeft)){
point++;
}
}
}
public void keyPressed(KeyEvent event){
while(event.getKeyCode()==40||event.getKeyCode()=='s'){
direction = DOWN;
Thread.sleep(500);
}
}
public void KeyReleased(KeyEvent event){
}
public void KeyTyped(KeyEvent event){
}
}
I'm also stuck on the Thread.sleep(500); line I have. It says Unhandled exception type InterruptedException. Any help? Thx.
Oh, I forgot something.
1:When I try to run it, the only error I get is:
Exception in thread "main" java.lang.Error: Unresolved compilation problem: at Pong.main(Pong.java:50). That's the public static void main(String[] args) line. My complete code is at the end so you can look at it (plz)
2:I'm using eclipse.
3:I am basically a beginner (not really)
My complete code:
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.*;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.event.EventListenerList;
public class Pong extends JFrame{
public final int WIDTH = 1000, HEIGHT = 1000;
final int UP = 1, DOWN = 2;
boolean hitRight;
int point = 0;
int direction;
Rectangle bg = new Rectangle(0,0,WIDTH,HEIGHT);
Rectangle borderLeft = new Rectangle(0,0,WIDTH/320,HEIGHT);
Rectangle borderRight = new Rectangle(WIDTH-WIDTH/320,0,WIDTH/320,HEIGHT);
Rectangle borderTop = new Rectangle(borderLeft.x,borderLeft.y,WIDTH,HEIGHT/35);
Rectangle borderBottom = new Rectangle(0,HEIGHT-HEIGHT/320,WIDTH,HEIGHT/320);
Rectangle ball = new Rectangle(WIDTH/2,HEIGHT/2,WIDTH/64,HEIGHT/64);
Rectangle board = new Rectangle(WIDTH-WIDTH/160,0,WIDTH/128,HEIGHT/10);
public void paint(Graphics graphics){
super.paint(graphics);
graphics.setColor(Color.BLACK);
graphics.fillRect(bg.x,bg.y,bg.width,bg.height);
graphics.setColor(Color.RED);
graphics.fillRect(borderLeft.x, borderLeft.y, borderLeft.width, borderLeft.height);
graphics.fillRect(borderRight.x, borderRight.y, borderRight.width, borderRight.height);
graphics.fillRect(borderTop.x, borderTop.y, borderTop.width, borderTop.height);
graphics.fillRect(borderBottom.x, borderBottom.y, borderBottom.width, borderBottom.height);
graphics.setColor(Color.WHITE);
graphics.fillRect(ball.x,ball.y,ball.width,ball.height);
graphics.fillRect(board.x,board.y,board.width,board.height);
}
/**
* This Pong game made by me.
* This has no copied code.
* Any similarities are coincidences.
* #param args
*/
/*
* The constructor.
*/
public Pong(){
super("Pong");
setSize(WIDTH, HEIGHT);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Pong p = new Pong();
}
/*
* The move thread.
*/
private class Move1 extends Thread implements KeyListener{
public void run(){
addKeyListener(this);
while(true){
//hitRight makes you lose.
//point is how many times it ricochets.
if(ball.intersects(borderRight)){
hitRight = true;
}
if(ball.intersects(borderLeft)){
point++;
}
}
}
public void keyPressed(KeyEvent event){
while(event.getKeyCode()==40||event.getKeyCode()=='s'){
direction = DOWN;
Thread.sleep(500);
}
}
public void KeyReleased(KeyEvent event){
}
public void KeyTyped(KeyEvent event){
}
}
}
/*
* End of move thread...
*/
Your first error, related to KeyListener, is simply a case error. In Java, methods and variables are named following camelCase, and the language is case-sensitive, so you must define your method as keyTyped instead of KeyTyped. The same applies for keyReleased.
As for your second error, you must make changes necessary to handle the InterruptedException declared to be thrown by Thread#sleep. You can surround that method call in a try-catch block. (However, note that the KeyEvent's keycode will never change, so you've got a potential infinite while loop there.)
I recommend reading a tutorial or two on implementing a key listener.
In order to implement KeyListener, you need to implement all of the methods that that individual interface contains.
These are:
keyPressed
keyDown
keyTyped
In order to use interfaces you must implement all of their methods.
Also, Thread.sleep(500) might generate an exception. Java is basically requiring you to handle an error just in case something goes wrong. To do this, you need a try...catch, like so:
try
{
Thread.sleep(500);
}
catch(InterruptedException e)
{
System.out.println("Error!");
}
I've just starting delving into the wonders of Java ME but have become frustrated when trying to create a thread...
Below is the code which compiles absolutely fine. However, as soon as I install it on my G600 and run it, 'Java Game Error' pops up.
My method of putting it in a jar file and installing it works, as I have created a game with no threads and that works fine.
import java.util.Random;
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
import javax.microedition.midlet.*;
public class CanvasTest extends MIDlet {
Display display;
public CanvasTest() {
}
public void startApp() {
TestCanvas thecanvas = new TestCanvas();
display = Display.getDisplay(this);
display.setCurrent(thecanvas);
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
}
class TestCanvas extends GameCanvas implements Runnable {
Font font;
int width;
int height;
boolean running = true;
public TestCanvas() {
super(false);
setFullScreenMode(true);
width = getWidth();
height = getHeight();
Thread thisThread = new Thread(this);
thisThread.start();
}
public void paint(Graphics g) {
Random rand = new Random();
g.setColor(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255));
g.fillRect(0, 0, width, height);
}
public void run() {
while(running) {
paint(getGraphics());
flushGraphics();
try {
Thread.sleep(50);
}
catch(InterruptedException ex) {}
}
}
};
Note: yes, this is not the game, it merely demonstrates the problem I am facing.
Thanks in advance!
Just a wild guess, but a general rule in Java is that you can't "touch" the UI out of the main thread. Well, this a little bit roughly explained, but there are many articles about the topic.
I suggest you to avoid calling UI methods like paint() or flushGraphics() from a separate Thread.
I hope it helps.
did you test it at emulator prior to phone? if not - why? if yes - how did it go?
regarding the code it looks OK to me except for the slippery two lines where you create and start thread from constructor. I'd rather move these two lines at the end of startApp
public void startApp() {
TestCanvas theCanvas= new TestCanvas();
display = Display.getDisplay(this);
display.setCurrent(theCanvas);
new Thread(theCanvas).start(); // add here and...
}
//...
public TestCanvas() {
super(false);
setFullScreenMode(true);
width = getWidth();
height = getHeight();
// ...and remove here
// Thread thisThread = new Thread(this);
// thisThread.start();
}