My 2dGameEngine is very laggy - java

I originally wrote started writing this on windows, and it ran pretty smoothly, though it occasionaly would freeze up; but, after i switched to linux, the graphics are very very choppy. I expect it worked before because I had the right drivers to my video card, but after the switch, I may be getting less performance from it do to bad drivers. Although, a 2d game engine should be able to run on pretty bad computers. I was wondring why it is running so slow, and how i could make run faster. There are other classes aside from the one below, but the one below contains most of main functions of the program.
package Platformer;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import javax.sound.sampled.*;
import javax.sound.sampled.LineEvent.Type;
import javax.swing.JPanel;
import javax.swing.Timer;
public abstract class GameComponent extends JPanel{
//delete this i think
public static int ticks=0;
public Rectangle box;
private final int FPS=40;
public int xMovement=0;
public int yMovement=0;
public final int A=0,D=1,S=2,W=3,SPACE=4;
public boolean[] keyPressed=new boolean[5];
private InputController keyListener;
public ArrayList<Entity> Entities=new ArrayList<Entity>();
HashMap sounds=new HashMap();
public boolean drawLines=false;
public int xAxisLines=40;
public int yAxisLines=27;
Quadtree quad=new Quadtree(0,new Rectangle(0,0,1920,1080));
public GameComponent(){
keyListener=new InputController();
box=new Rectangle(90,90,20,20);
setFocusable(true);
requestFocusInWindow(true);
run();
}
public void run(){
addKeyListener(keyListener);
Timer timer=new Timer(1000/FPS, new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
//movement();
gameLoop();
collisions();
repaint();
ticks++;
if(ticks==360 || ticks==1000){
try{
playSound("spun");
}
catch(Exception e){
e.printStackTrace();
}
}
}
});
timer.start();
}
#Override
public void paint(Graphics g){
super.paint(g);
Graphics2D g2d=(Graphics2D)g.create();
//g2d.setColor(Color.RED);
//g2d.fill(box);
for(Entity entity: Entities){
g2d.drawImage(entity.IMAGE, entity.getX(),entity.getY(), entity.IMAGE.getWidth(), entity.IMAGE.getHeight(), null);
g2d.drawString(String.valueOf(entity.NAME), entity.getX(), entity.getY());
}
///draw lines in window if enabiled
if(drawLines){
drawLines(g2d);
}
///do collisions
g2d.dispose();
}
class InputController implements KeyListener{
#Override
public void keyPressed(KeyEvent e) {
switch(e.getKeyCode()){
case KeyEvent.VK_W:
keyPressed[W]=true;
break;
case KeyEvent.VK_S:
keyPressed[S]=true;
break;
case KeyEvent.VK_A:
keyPressed[A]=true;
break;
case KeyEvent.VK_D:
keyPressed[D]=true;
break;
case KeyEvent.VK_SPACE:
keyPressed[SPACE]=true;
}
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
switch(e.getKeyCode()){
case KeyEvent.VK_W:
keyPressed[W]=false;
break;
case KeyEvent.VK_S:
keyPressed[S]=false;
break;
case KeyEvent.VK_A:
keyPressed[A]=false;
break;
case KeyEvent.VK_D:
keyPressed[D]=false;
break;
case KeyEvent.VK_SPACE:
keyPressed[SPACE]=false;
}
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
}
public abstract void gameLoop();
public void drawLines(Graphics2D g2d){
for(int i=0;i<1920;i+=1920/xAxisLines){
g2d.drawLine(i, 0, i, 1080);
}
for(int i=0;i<1080;i+=1080/yAxisLines){
g2d.drawLine(0, i, 1920, i);
}
}
public void collisions(){
////collisions!
ArrayList<Entity> returnEntities=new ArrayList<Entity>();
quad.clear();
for(int i=0;i<Entities.size();i++){
quad.insert(Entities.get(i));
}
for(int i=0;i <Entities.size();i++){
returnEntities.clear();
returnEntities=quad.retrieve(returnEntities,Entities.get(i));
for(Entity e:returnEntities){
//System.out.println(Entities.get(i).NAME+" could collide with "+e.NAME);
if(Math.abs(Entities.get(i).getX()-e.getX())<= Entities.get(i).IMAGE.getWidth()/2+e.IMAGE.getWidth()/2 && Math.abs(Entities.get(i).getY()-e.getY())<= Entities.get(i).IMAGE.getHeight()/2+e.IMAGE.getHeight()/2 && Entities.get(i)!=e){
//System.out.println(Entities.get(i).NAME+" is colliding with "+e.NAME);
}
}
}
////collisions!
/*
System.out.println(String.valueOf(quad.getIndex(Entities.get(0))));
System.out.println(Entities.get(0).bounds.x);
System.out.println(Entities.get(0).bounds.y);
*/
}
public void playSound(String fileName) throws IOException,
UnsupportedAudioFileException, LineUnavailableException, InterruptedException {
File clipFile=new File("assets//"+fileName+".wav");
class AudioListener implements LineListener {
private boolean done = false;
#Override public synchronized void update(LineEvent event) {
Type eventType = event.getType();
if (eventType == Type.STOP || eventType == Type.CLOSE) {
done = true;
notifyAll();
}
}
public synchronized void waitUntilDone() throws InterruptedException {
while (!done) {}
}
}
class SoundThread implements Runnable {
AudioListener listener = new AudioListener();
AudioInputStream audioInputStream;
public SoundThread() throws UnsupportedAudioFileException, IOException{
audioInputStream = AudioSystem.getAudioInputStream(clipFile);
}
#Override
public void run() {
try{
Clip clip=AudioSystem.getClip();
clip.addLineListener(listener);
clip.open(audioInputStream);
try{
clip.start();
listener.waitUntilDone();
}
finally{
clip.close();
}
}
catch(Exception e){
e.printStackTrace();
}
finally{
try {
audioInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Thread thread=new Thread(new SoundThread());
thread.start();
}
}

Well you have written it very Procedurally, rather than object orientated. you have lots of lists being iterated, Maybe if you refactor it then you may be able to speed it up, Is there anywhere that we can get the code (github etc) as with just this snippet there is not much we can do to help.

Any specific reason for doing Graphics2D g2d=(Graphics2D)g.create() and g2d.dispose();?
Can't you just Graphics2D g2d = (Graphics2D) g;? It will probably improve performance.

TLDR; but managed to find 2 fundamental mistakes:
In Swing never override paint() to do graphics.
Use the override-safe paintComponent(). (In additional to #YoavAharoni's point, don't create new graphic contexts every time it repaints.)
Never make long lasting tasks inside the EDT.
In your case, you call gameLoop(), collisions(), and playSound() - which I assume as time consuming - inside the timer's event method. Instead, use java.util.Timer or threads.
If you must use the EDT, as to render long lasting graphics, use a SwingWorker. (Note: repaint() may be called outside EDT)

I don't know what IDE are you using, but I recommend you to do a profiling. Eclipse has a nice plugin for that.
Yourkit: free trial only, but pretty good https://www.yourkit.com/features/eclipse/
Eclipse java profiler, you can find it on the marketplace
Netbeans https://profiler.netbeans.org/
Once I had to wrote a game myself, and my problem was with the size of the images:
``g2d.drawImage(entity.IMAGE, entity.getX(),entity.getY(), entity.IMAGE.getWidth(), entity.IMAGE.getHeight(), null);``
I had to decrease the size under 40kb for each image to run normally.
Another thing is that you switch of some elments on your UI, and see what makes a big performance boost, then you can tweak that component.

Related

Removing object from a collection while iterating over it

I'm trying to remove objects that are outside of the JPanel.
However, when I do that I get this error
and my program crashes.
I was told by my lecturer that it's because two Threads are accessing the ArrayList that stores my objects.
I did to synchronize the functions but it didn't work.
Bullet
public void move(){
if(y< -height){
synchronized (this) {
bullet.remove(this);
}
}
y-=5;
}
Relevant Classes:
Application
import javax.swing.*;
public class Application {
public static String path ="C:\\Users\\jarek\\OneDrive\\NUIG Private\\(2) Semester 2 2019\\Next Generation Technologies II CT255\\Assignment 3\\";
private Application(){
JFrame frame = new JFrame("Ihsan The Defender");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GamePanel gamePanel= new GamePanel();
frame.add(gamePanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
new Thread(gamePanel).start();
}
public static void main (String args[]){
new Application();
}
}
GamePanel
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.security.Key;
import java.util.ArrayList;
public class GamePanel extends JPanel implements Runnable, KeyListener{
private String path = Application.path;
Image gameOverImg = new ImageIcon(path+"//images//gameover1.png").getImage();
private Ihsan ihsan;
private ArrayList <David> david = new ArrayList<>();
private int enemies=5;
private boolean pause=false;
private boolean gameOver=false;
GamePanel(){
ihsan = new Ihsan(this);
for(int i=0; i<enemies; i++){
david.add(new David(this));
}
setFocusable(true);
requestFocusInWindow();
addKeyListener(this);
}
#Override
public void run() {
while (!pause){
repaint();
for(David david:david){
david.move();
}
for(Bullet bullet:Bullet.bullet){
bullet.move();
}
try{Thread.sleep(30);}
catch (InterruptedException e){}
}
}
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.GRAY);
g2d.fillRect(0,0 ,getWidth(), getHeight());
for(David david : david){
g2d.drawImage(david.getImg(), david.getX(), david.getY(), null);
}
g2d.drawImage(ihsan.getImg(), ihsan.getX(), ihsan.getY(), null);
for (Bullet bullet:Bullet.bullet){
g2d.drawImage(bullet.getImg(), bullet.getX(), bullet.getY(), null);
}
if(gameOver){
g2d.drawImage(gameOverImg,0,getHeight()/4,null);
}
}
private static final Dimension DESIRED_SIZE = new Dimension(600,700);
#Override
public Dimension getPreferredSize(){
return DESIRED_SIZE;
}
public void setGameOver(boolean gameOver) {
this.gameOver = gameOver;
}
#Override
public void keyPressed(KeyEvent e) {
int key=e.getKeyCode();
if (key==KeyEvent.VK_D || key==KeyEvent.VK_RIGHT){
ihsan.move(4,0);
System.out.println("Right Key");
}
if (key==KeyEvent.VK_A || key== KeyEvent.VK_LEFT){
ihsan.move(-4,0);
System.out.println("Left Key");
}
if(key==KeyEvent.VK_SPACE){
Bullet.bullet.add(new Bullet(this,ihsan.getX()+(ihsan.getWidth()/2), ihsan.getY()));
}
}
#Override
public void keyTyped(KeyEvent e) { }
#Override
public void keyReleased(KeyEvent e) { }
public boolean getGameOver(){
return gameOver;
}
}
Bullet
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
public class Bullet {
//Environment
public static ArrayList<Bullet> bullet = new ArrayList<>();
private String path = Application.path;
private GamePanel gp;
//properties
private int x,y;
private int width,height;
private int yVector;
private Image image;
Bullet(GamePanel gp, int x, int y){
image = new ImageIcon(path+"\\images\\javaicon.png").getImage();
width=image.getWidth(null);
height=image.getHeight(null);
this.gp=gp;
this.x=x;
this.y=y;
yVector=5;
}
public void move(){
if(y< -height){
bullet.remove(this);
}
y-=5;
}
public Image getImg(){
return image;
}
public int getX(){
return x;
}
public int getY(){
return y;
}
}
Your current problem is not with synchronization, but that you modify the bullet list while iterating over it:
// GamePanel.java#run():
for (Bullet bullet:Bullet.bullet) { //your code is iterating over Bullet.bullet here
bullet.move(); //you call Bullet#move here
}
// Bullet.java#move():
public void move(){
if(y< -height){
bullet.remove(this); //this will remove the current bullet from Bullet.bullet
// ultimately causing the ConcurrrentModificationException in GamePanel.run()
}
y-=5;
}
Synchronization won't help, since both actions occur within the same thread.
To solve this issue the Bullet.move() method needs to return a boolean indicating whether it should be removed from the list. And GamePanel.run() must not use an enhanced for loop but an iterator (removing an element from a list using Iterator.remove() is safe if this is the only active Iterator):
// Bullet.java#move():
public boolean move(){
if(y< -height){
return true; // instruct GamePanel.run() to remove this bullet
}
y-=5;
return false; // keep this bullet
}
// GamePanel.java#run():
Iterator<Bullet> it = Bullet.bullet.iterator();
while (it.hasNext()) {
Bullet bullet = it.next();
if (bullet.move()) { // if bullet should be removed
it.remove(); // remove it from the list
}
}
There are other issues too:
you call #repaint() from your own thread instead of the Swing EDT
repainting iterates over the same Bullet.bullet list without synchronization (which may lead to a ConcurrentModificationException within GamePanel.paint())
The synchronized block must be around every piece of code that accessed or modifies the ArrayList. The object put in the parenthesis must be the same: It’s the lock.
Create a field of type Object named bulletLock for example, and use it as a lock, every time you access bullet.
The error occurs because you’re removing a bullet while another thread is in a for-loop on the list. As there is a concurrent modification, it can’t continue safely.
Another solution would be to make a copy of the ArrayList before your for-loop.
A simple solution to the problem described in Thomas Kläger answer could be:
for(Bullet bullet: new ArrayList(Bullet.bullet) ){ //iterate over a copy
bullet.move();
}
Alternatively:
Iterator<Bullet> it = Bullet.bullet.iterator();
while (it.hasNext()) {
Bullet bullet = it.next();
bullet.move();
}
without changing other parts of the code.

How do I make a loop start and end with a key press and release?

Here is my code... How can I make it work so that it runs the loop while the user is holding a button and stops when the user releases the button?
public void nextPrimeNum()
{
x = false;
int b = 2;
ArrayList<Integer> next = new ArrayList<Integer>();
while(x)
{
next = factors(b);
if(next.size()==2)
{
System.out.println(b);
}
b++;
}
System.out.println("End");
}
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode() == 401)
{
x = true;
}
}
public void keyRealesed(KeyEvent e)
{
if(e.getKeyCode() == 402)
{
x = false;
}
}
GUI and multi-thread programming is inherently difficult.
So, this is as simple as it could be, without violating best practices too much.
You need several things:
A separate Thread for printing primes:
Its run method loops for ever, but pauses when the Space key is not pressed.
(see Defining and Starting a Thread for more info)
A KeyListener which will be called from AWT's event dispatch thread:
The event handling methods are designed to finish fast, so that other events
(like moving, resizing and closing the frame) still are handled fast.
(see How to Write a Key Listener
and The Event Dispatch Thread for more info)
A visible GUI component (JFrame) for adding the KeyListener
Some synchronization between the 2 threads (via synchronized, notify and wait)
so that the prime-printing starts/continues on keyPressed
and suspends on keyReleased
(see Guarded Blocks for more info)
Initialize and start the whole GUI by invoking initGUI.
(see Initial Threads for more info)
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class Main implements Runnable, KeyListener {
public static void main(String[] args) {
SwingUtilities.invokeLater(Main::initGUI);
}
private static void initGUI() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JLabel("Press SPACE key for printing primes"));
frame.pack();
frame.setLocationRelativeTo(null); // center on screen
frame.setVisible(true);
Main main = new Main();
frame.addKeyListener(main);
Thread thread = new Thread(main);
thread.start();
}
private boolean spaceKeyPressed;
private boolean isPrime(int n) {
for (int i = 2; i < n; i++) {
if (n % i == 0)
return false;
}
return true;
}
#Override
public void run() {
for (int n = 2; /**/; n++) {
while (!spaceKeyPressed) {
synchronized (this) {
try {
wait(); // waits until notify()
} catch (InterruptedException e) {
// do nothing
}
}
}
if (isPrime(n)) {
System.out.println(n);
}
}
}
#Override
public void keyTyped(KeyEvent e) {
// do nothing
}
#Override
public synchronized void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
spaceKeyPressed = true;
notifyAll(); // cause wait() to finish
}
}
#Override
public synchronized void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
spaceKeyPressed = false;
notifyAll(); // cause wait() to finish
}
}
}
So, the answer is - it's complicated. It covers broad topics such as concurrency (in general), GUI development, best practices with the specific API (Swing) which are better covered in more detail by reading through the various tutorials (and experimenting)
Concurrency
Creating a GUI With JFC/Swing
Concurrency in Swing
Worker Threads and SwingWorker
How to Use Actions
How to Use Key Bindings
The example presents two ways to execute the "loop" (which is presented in the doInBackground method of the CalculateWorker class).
You can press and hold the JButton or press and hold the [kbd]Space[kbd] bar, both will cause the "main loop" to run, updating the JTextArea with the results...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTextArea ta;
private CalculateWorker worker;
public TestPane() {
setLayout(new BorderLayout());
ta = new JTextArea(20, 20);
ta.setEditable(false);
add(new JScrollPane(ta));
worker = new CalculateWorker(ta);
JButton btn = new JButton("Press");
btn.getModel().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
System.out.println("...isRunning = " + worker.isRunning());
if (!worker.isRunning()) {
return;
}
System.out.println("...isPressed = " + btn.getModel().isPressed());
System.out.println("...isPaused = " + worker.isPaused());
if (btn.getModel().isPressed()) {
worker.pause(false);
} else {
worker.pause(true);
}
}
});
add(btn, BorderLayout.SOUTH);
worker.execute();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), "Space.released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true), "Space.pressed");
am.put("Space.released", new CalculateAction(false, worker));
am.put("Space.pressed", new CalculateAction(true, worker));
}
public class CalculateWorker extends SwingWorker<List<String>, String> {
private AtomicBoolean run = new AtomicBoolean(true);
private AtomicBoolean paused = new AtomicBoolean(false);
private ReentrantLock pausedLocked = new ReentrantLock();
private Condition pausedCondition = pausedLocked.newCondition();
private JTextArea ta;
public CalculateWorker(JTextArea ta) {
this.ta = ta;
pause(true);
}
public void stop() {
run.set(false);
pausedLocked.lock();
pausedCondition.signalAll();
pausedLocked.unlock();
}
public void pause(boolean pause) {
paused.set(pause);
pausedLocked.lock();
pausedCondition.signalAll();
pausedLocked.unlock();
}
public boolean isPaused() {
return paused.get();
}
public boolean isRunning() {
return run.get();
}
#Override
protected List<String> doInBackground() throws Exception {
List<String> values = new ArrayList<>(256);
long value = 0;
System.out.println("!! Start running");
while (run.get()) {
while (paused.get()) {
System.out.println("!! I'm paused");
pausedLocked.lock();
try {
pausedCondition.await();
} finally {
pausedLocked.unlock();
}
}
System.out.println("!! Start loop");
while (!paused.get() && run.get()) {
value++;
values.add(Long.toString(value));
publish(Long.toString(value));
Thread.sleep(5);
}
System.out.println("!! Main loop over");
}
System.out.println("!! Run is over");
return values;
}
#Override
protected void process(List<String> chunks) {
for (String value : chunks) {
ta.append(value);
ta.append("\n");
}
ta.setCaretPosition(ta.getText().length());
}
}
public class CalculateAction extends AbstractAction {
private boolean start;
private CalculateWorker worker;
public CalculateAction(boolean start, CalculateWorker worker) {
putValue(NAME, "Calculate");
this.start = start;
this.worker = worker;
}
#Override
public void actionPerformed(ActionEvent e) {
worker.pause(start);
}
}
}
}
Is there a simpler solution?
Of course, I always go for the most difficult, hard to understand solutions first (sarcasm)
While it "might" be possible to reduce the complexity, the example presents a number of "best practice" concepts which you would do well to learn and understand.
The solution could also be done differently depending on the API used, so, it's the "simplest" solution for the specific API choice.
I wanted to do it from the console!
Java can't do that - it's console support is rudimentary at best and doesn't support a concept of "key pressed/released" actions (since it's running in a single thread, it would be impossible for it to do otherwise).
There "are" solutions you might try, but they would require a third party library linked to native binaries to implement, which would (possibly) reduce the number of platforms it would run on

Using Timer To Pause Program Execution [duplicate]

This question already has an answer here:
Repaint is not functioning properly as required
(1 answer)
Closed 8 years ago.
I want to pause the execution of a Swing Program for a specified amount of time. Naturally the first thing that I used was Thread.sleep(100) (since, I am a noob). Then I got to know that my program is not thread safe so I decided to use Timer with some suggestions from fellow programmers. The problem is I am unable to get any sources from where I can learn how to delay the thread, using Timer. Most of them use Timer for delaying execution. Please help me solve this problem. I have provided a compileable code snippet below.
import javax.swing.*;
import java.awt.*;
public class MatrixBoard_swing extends JFrame{
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MatrixBoard_swing b = new MatrixBoard_swing();
}
});
}
MatrixBoard_swing(){
this.setSize(640, 480);
this.setVisible(true);
while(rad < 200){
repaint();
rad++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
int rad = 10;
public void paint(Graphics g){
super.paint(g);
g.drawOval(400-rad, 400-rad, rad, rad);
}
}
EDIT: My trial for a Timer implementation(please tell me if it is wrong):
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MatrixBoard_swing extends JFrame implements ActionListener{
Timer timer;
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MatrixBoard_swing b = new MatrixBoard_swing();
}
});
}
MatrixBoard_swing(){
this.setSize(640, 480);
this.setVisible(true);
timer = new Timer(100, this);
timer.start();
}
int rad = 10;
public void paint(Graphics g){
super.paint(g);
g.drawOval(400-rad, 400-rad, rad, rad);
}
#Override
public void actionPerformed(ActionEvent arg0) {
repaint();
rad++;
if(rad >= 200){
timer.stop();
}
}
So instead of...
while(rad < 200){
repaint();
rad++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
You simply need to turn the logic around a little...
Timer timer = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
rad++;
if (rad < 200) {
repaint();
} else {
((Timer)evt.getSource()).stop();
}
}
});
timer.start();
Basically, the Timer will act as the Thread.sleep(), but in a nice way that doesn't break the UI, but will allow you to inject a delay between execution. Each time it executes, you need to increment your value, test for the "stop" condition and update otherwise...
Take a look at How to Use Swing Timers and the other 3, 800 questions on the subject on SO...

Java 2D Game: repaint(); makes window grey

I'm trying to make a 2D game in Java, but when I call the repaint() method in a thread there's an odd grey-only window.
Here's the source code I have so far:
Spaceshooter.java
package spaceshooter;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Spaceshooter extends JFrame implements KeyListener, Runnable {
private Player player = new Player(5, 186, this);
private boolean up, down;
public Spaceshooter(String title) {
super(title);
this.setFocusable(true);
this.addKeyListener(this);
}
#Override
public void paint(Graphics gr) {
super.paint(gr);
gr.setColor(Color.BLACK);
gr.fillRect(0, 0, 800, 500);
player.paintPlayer(gr);
}
public static void main(String[] args) {
Spaceshooter shooter = new Spaceshooter("Spaceshooter");
new Thread(shooter).start();
shooter.setSize(800,500);
shooter.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
shooter.setVisible(true);
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 38) {
up = true;
down = false;
} else if (e.getKeyCode() == 40) {
down = true;
up = false;
}
}
#Override
public void keyReleased(KeyEvent e) {
down = false;
up = false;
}
#Override
public void run() {
while(true) {
if (up) {
player.moveUp();
} else if (down) {
player.moveDown();
}
repaint();
try {
Thread.sleep(20);
} catch (InterruptedException ex) {
Logger.getLogger(Spaceshooter.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
Player.java
package spaceshooter;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Toolkit;
public class Player {
private int x, y;
private Component comp;
public Player(int x, int y, Component comp) {
this.x = x;
this.y = y;
this.comp = comp;
}
public void moveUp() {
y -= 5;
}
public void moveDown() {
y += 5;
}
public void paintPlayer(Graphics gr) {
gr.drawImage(Toolkit.getDefaultToolkit().getImage("images/player.png"), x, y, comp);
}
}
Thanks for your answers in advance!
What is EDT ?
Swing event handling code runs on a special thread known as the event dispatch thread. Most code that invokes Swing methods also runs on this thread. This is necessary because most Swing object methods are not "thread safe". All GUI related task, any update should be made to GUI while painting process must happen on the EDT, which involves wrapping the request in an event and processing it onto the EventQueue. Then the event are dispatched from the same queue in the one by one in order they en-queued, FIRST IN FIRST OUT. That is, if That is, if Event A is enqueued to the EventQueue before Event B then event B will not be dispatched before event A.
Any task you perform which may take a while, likely to block the EDT, no dispatching will happen, no update will be made and hence your application FREEZES. You will have to kill it to get rid of this freezing state.
In your program, aside from creating your JFrame and making it to visible from the main thread (which we also should not do):
while(true) {
if (up) {
player.moveUp();
} else if (down) {
player.moveDown();
}
repaint();
try {
Thread.sleep(20);
} catch (InterruptedException ex) {
Logger.getLogger(Spaceshooter.class.getName()).log(Level.SEVERE, null, ex);
}
}
you are posting repaint() request from the thread which you sent to sleep right immediately after that.
SwingUtilities.invokeLater():
Swing provides a nice function SwingUtilities.invokeLater(new Runnable(){}) for posting repaint request to the EDT. All you have to do is to write:
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
repaint();
}
});
Now some more thing to mention:
We should not implements a GUI component with Runnable. Make another class implementing Runnable, make your computation inside that then use SwingUtilities to post the component update request.
we should not made custom painting on JFrame directly. JFrame is a top level component. It is more like a container which contains your whole app. If you want custom painting use a custom component MyCanvas extends JComponent.
we should not override paint() function. Instead paintComponent(g) will serve our custom painting purposes nicely.
Learn to use Swing Timer class for timely repeated GUI rendering task.
Tutorial Resource and References:
The Event Dispatch Thread
EventQueue
How to Use Swing Timers
Lesson: Performing Custom Painting

I am trying to develop a game using java applet, the motion of a ball halts whole of my game?

This is just the begining of the game, where there are two squares, one can be controlled by arrow keys and other by mouse, they can fire balls on each other and simultaneously can be saved, the one getting maximum hits will win...
In this code when I fire from the second square there is a long line which goes towards the second player and whole of the game has to halt..
package raship;
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.io.IOException;
public class raship extends Applet implements KeyListener, MouseMotionListener, MouseListener, Runnable
{
int width,flag1=0,flag2=0,height,x1,y1,x2,y2,calc1,calc2x,calc2y;
Thread t=null;
public void init()
{
//Toolkit toolkit=Toolkit.getDefaultToolkit();
t=new Thread();
width=getSize().width;
height=getSize().height;
x1=0;y1=height/2;
x2=width-10;y2=height/2;
addMouseListener(this);
addMouseMotionListener(this);
addKeyListener(this);
setBackground(Color.gray);
repaint();
}
public void keyPressed(KeyEvent e)
{
int c=e.getKeyCode();
System.out.println(c);
if(c==KeyEvent.VK_LEFT)
{
System.out.println("yeah it's on");
x1-=10;
}
else if(c==KeyEvent.VK_UP)
y1-=10;
else if(c==KeyEvent.VK_RIGHT)
x1+=10;
else if(c==KeyEvent.VK_DOWN)
y1+=10;
if(x1>=0 && y1>=0 && y1<=height-20 && x1<=3*width/4)
repaint();
}
public void keyReleased(KeyEvent arg0) {
}
public void keyTyped(KeyEvent arg0) {
}
public void mouseDragged(MouseEvent e) {
}
public void mouseMoved(MouseEvent e)
{
x2=e.getX();
y2=e.getY();
if(x2>=5*width/8 && x2<=width-20)
repaint();
}
public void mouseClicked(MouseEvent e)
{
flag2=1;
calc2x=x2;
calc2y=y2;
System.out.println(calc2x);
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
}
public void mouseReleased(MouseEvent arg0) {
}
public void paint(Graphics g)
{
width=getSize().width;
height=getSize().height;
g.setColor(Color.green);
g.fillRect(x1, y1, 20, 20);
g.setColor(Color.red);
g.fillRect(x2, y2, 20, 20);
if(flag2==1)
{
g.setColor(Color.yellow);
while(true)
{
calc2x-=1;
System.out.println(calc2x);
g.fillOval(calc2x,calc2y,10,10);
try {
Thread.sleep(4);
} catch (InterruptedException e) {e.printStackTrace();}
if(calc2x<10)
{
flag2=0;
break;
}
}
}
}
#SuppressWarnings("static-access")
public void run()
{
if(flag2==1)
while(true)
{
{
repaint();
System.out.println("calc2x="+calc2x);
if(calc2x<10)
{
flag2=0;
}
try
{
t.sleep(4);
} catch (InterruptedException e) {e.printStackTrace();}
calc2x-=1;
}
}
}
}
NEVER have Thread.sleep(...) in a paint method. EVER. This puts all your drawing to sleep. In fact simply calling Thread.sleep(...) in your GUI thread will be enough to put the GUI to sleep, but its worse still in a paint method, since that method must be called over and over, and needs to be blazing fast and over in the blink of an eye or less.
Instead:
Create a Swing JApplet, not an AWT Applet
Override the paintComponent method of a JPanel to do your drawing
Use a Swing Timer to do your game loop.
Edit
You state in comment:
#HovercraftFullOfEels if you can write the syntax of swing timer and swing applet it would be of great help....
You appear to be wanting me to write tutorials for you. I wish I had all the time to do that, but alas, I don't, and I feel that it would be much more efficient for both you and me for you to check out the decent tutorials with sample code that are already in existence just waiting for you to learn from. For example, please check out the following links:
The Java Tutorials, The Really Big Index
Java Applets
Using Swing Components
How to make Java Applets
How to Use Swing Timers

Categories