create game loop thread - java

Hi I have just been messing about polygons and awt. I have created a Jframe and can draw the polygons ok and have one of them move with keypresses.
I'm wondering how to start a gameloop thread(and where to put it!) that will update the jframe independently.
From googling im led to believe that I should have one thread for user input and one for the game itself.
At the moment I have implemented KeyListener on the board class(code shown below),should I put that out into its own class and make it implement runnable?As the code stands I repaint the JFrame in the keypressed() method just so i can see that it moves correctly
Being at it most of the day and I have myself very very confused :)
As always any help much appreciated!
Also while im at it from online tutourials should I use JPanel instead of JFrame and paintComponent() instead of paint()?
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
public class Board extends JFrame implements KeyListener{
AffineTransform identity = new AffineTransform();
Graphics2D g2d;
Ship ship = new Ship();
public static final int ALIENS = 3;
Alien[] alien = new Alien[ALIENS];
Board(){
super("The Board");
setSize(1280,1024);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().setBackground(Color.BLACK);
for(int x=0;x<ALIENS;x++){
alien[x]=new Alien();
}
}
public void paint(Graphics g){
super.paint(g);
addKeyListener(this);
//draw ship
g2d = (Graphics2D)g;
g2d.setTransform(identity);
g2d.translate(ship.getxPos(),ship.getyPos());
g2d.scale(2,2);
g2d.setColor(Color.ORANGE);
g2d.fill(ship.getShape());
g2d.setColor(Color.BLACK);
g2d.draw(ship.getShape());
// draw aliens
for(int x=0;x<ALIENS;x++){
//if alien alive
if(alien[x].isAlive()){
//draw alien
g2d = (Graphics2D)g;
g2d.setTransform(identity);
g2d.translate(alien[x].getxPos(),alien[x].getyPos());
g2d.scale(2,2);
g2d.setColor(Color.BLUE);
g2d.fill(alien[x].getShape());
g2d.setColor(Color.BLACK);
g2d.draw(alien[x].getShape());
}
}
}//end paint
/*****************************************************
* key listener events
*****************************************************/
public void keyReleased(KeyEvent k) { }
public void keyTyped(KeyEvent k) { }
public void keyPressed(KeyEvent k) {
int keyCode = k.getKeyCode();
switch (keyCode) {
case KeyEvent.VK_A:
//move ship left
if(ship.getxPos()<20){
ship.setxPos(20);
}else
ship.setxPos(ship.getxPos()-1);
break;
case KeyEvent.VK_D:
if(ship.getxPos()>1260){
ship.setxPos(1260);
}else
ship.setxPos(ship.getxPos()+1);
}
repaint();
}//end keypressed event
public static void main(String[] args){
new Board();
}
}

These answers depend somewhat on what kind of game you're trying to create.
From googling I'm led to believe that I should have one thread for user input and one for the game itself.
You create one main game loop, which runs in its own thread. In psudeocode
while (running) {
update game model
draw game
wait x milliseconds
}
Your user input would update the game model directly. The game loop updates the game model if the computer is required to make moves or react to your moves. Then, the game loop reads the game model and draws the game based on the values in the model.
At the moment I have implemented KeyListener on the board class(code shown below),should I put that out into its own class and make it implement runnable?
Yes, you should put KeyListener into its own class. No, you don't have to make it a separate thread.
To save yourself future trouble, your Swing components should be defined and used on the Event Dispatch thread.
Here's how you do that.
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Board();
}
});
}
Should I use JPanel instead of JFrame and paintComponent() instead of paint()?
Yes.
You should have a JPanel inside of a JFrame. The JPanel is where you perform the draw game psudeocode, using the paintComponent method.
Some people are going to disagree with me, but I've found it best if every object in the game has a draw method to draw itself.
public void draw(Graphics g)
The game model would also have a draw method, which draws all of the objects in the model.
The JPanel paintComponent method would look like this:
public void paintComponent(Graphics g) {
super.paintComponent(g);
gameModel.draw(g);
}

Related

Updating the position of a rectangle or any other image without overlapping in JPanel

My approach was to use 'reprint()' inside a loop was successful to move an image that means updating its position without overlapping but now I want to see the image moving and I used 'thread.sleep()' to give time gaps between the repaint()s but it doesn't seem to work
import java.awt.*;
import javax.swing.*;
public class jp extends JPanel implements ActionListener{
Timer t;
JPanel jl=new JPanel();
int x,y;
jp(){
x=10;
//y=10;
t=new Timer(5,this);
t.start();
}
public void actionPerformed(ActionEvent e){
x++;
y++;
if(x>500){
x=0;
y=0;
}
repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
this.setBackground(Color.black);
g.setColor(Color.blue);
g.fillRect(x,20,50,50);
}
}
public class Jpanel extends JFrame{
public static void main(String[] args) {
jp p=new jp();
JFrame j=new JFrame("TEST_CASE-1");
j.add(p);
j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
j.setSize(700,500);
j.setVisible(true);
}
}
If you are hoping to get an animation by having multiple drawings in paintComponent() such as using a for-loop like what you did, you are going to be disappointed.
It will only perform all the drawings and show it to you all at once. Instead of concurrently drawing 200 rectangles, you only need to draw 1 rectangle and update its position.
To perform an animation, you need either a timer (You may try javax.swing.timer) or a loop if you are planning to do something more sophisticated. You can implement an infinite loop and perform the rendering in the loop while updating the position of your rectangle.
Ultimately, no matter which approach you adopt. You will need store the position of your rectangle. Update and render it in such as way:
update position
render
update position
render
update position
render
and not
update position
update position
update position
render
render
render
Example of animation with timer:
Repainting/refreshing the JFrame
Example animating using loop:
while(running){
update(); //update position of your rect
repaint(); //redraw your rect (and all other stuff)
}

Java graphics update stutters heavily (simple pong game)

Im new to more dynamic java swing programing. I have of course used the regular swing components before with Buttons, Panels and more.
So I'm trying to make a very basic pong game using Swing and Graphics2D. I previously made a painting program which i succeeded to do.
My issue is that the graphics stutters heavily when the program is running. So far I have only implemented the ball and its just choosing a random direction and starts to bounce around in the panel. Which works. But, I can only see the ball if I'm constantly resizing the frame all the time, otherwise it stutters so heavily that it looks blank. In the first second or so you can actually see the ball moving, but heavily stuttering, and then the panel start to seem blank.
Relevant code and structure:
Main parts of the program are the Controller and Frame class. Where the Controller implements runnable and contains a run method that does the game updating.
The Frame class extends JFrame and contains a private instance variable JPanel gamePanel where all the graphics are painted. JFrame also has a Overridden paint(); method
When the Controller updates the program it calls a class in Frame called updateGraphics() which previously called paint(getGraphics());
public class Frame extends JFrame {
private JPanel gamePanel;
....
public void paint(Graphics g) {
super.paint(g);
label.setText(Integer.toString(ball.getPos().x) + ", " + Integer.toString(ball.getPos().y));
Graphics2D g2 = (Graphics2D) gamePanel.getGraphics();
g2.setStroke(new BasicStroke(2));
//g2.drawRect(0, 0, gamePanel.getWidth(), gamePanel.getHeight());
try{
//Draws the ball
g2.fillOval(ball.getPos().x, ball.getPos().y, 10, 10);
//Draws the player1(left) shield
g2.setStroke(new BasicStroke(2));
g2.drawLine(playerShield.getNorthX(), playerShield.getNorthY(), playerShield.getSouthX(), playerShield.getSouthY());
g2.drawLine(playerShield.getNorthX(), playerShield.getNorthY(), playerShield.getSouthX(), playerShield.getSouthY());
//Draws the computer/Player2(right) Shield
g2.drawLine(computerShield.getNorthX(), computerShield.getNorthY(), computerShield.getSouthX(), computerShield.getSouthY());
g2.drawLine(computerShield.getNorthX(), computerShield.getNorthY(), computerShield.getSouthX(), computerShield.getSouthY());
} catch(Exception e) {
System.out.println(e);
}
}
...
public void updateGraphics() {
paint(getGraphics());
}
//Another version of the updateGraphics i have tried to use with as little success
public void updateGrapgics() {
gamePanel.validate();
gamePanel.repaint();
}
}
When searching I have found people that says that I should and shouldn't use the paint or repaint method.
Can someone explain to me why its stuttering and how I should do to make it stutter-free?
There's no need to implement double-buffering or other tricks. Just do the following:
public class SomeVisualObject extends JComponent {
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
// paint things
}
}
...
final SomeVisualObject obj = new SomeVisualObject()
frame.add(obj);
...
final Timer repaintTimer = new Timer(20, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
// do some stuff here, for example calculate game physics.
// repaint actually does not repaint anything, but enqueues repaint request
obj.repaint();
}
});
repaintTimer.start();
and it will run and paint smoothly, without glitches.
Just don't mess with the loops. Swing runs it's own event loop, which is vital for repaints and other stuff.
See a complete and working example of 2d game object (bouncing ball) here: https://gist.github.com/akhikhl/8199472
I think that you should implement some kind of double buffering.
Your problem is simillar to this one Java Panel Double Buffering and this tutorial should help you a lot http://www.cokeandcode.com/info/tut2d.html.

Rectangle X axis move issue

I was just doing random stuff with GUI and I came to this issue.When I press right arrow key, rectangle really moves on X axis, but distance moved is not constant.It's raising pretty fast and rectangle does after each press bigger moves.In my recent code line X=X+1 seemed to work pretty fine.Here's my code:
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.*;
public class Buffer extends JPanel implements KeyListener{
public static JFrame frame;
public int x;
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(x,0,20,20);
frame.addKeyListener(this);
}
public static void main(String args[]){
Buffer z=new Buffer();
frame=new JFrame();
frame.setSize(500,500);
frame.setVisible(true);
frame.setFocusable(true);
frame.add(z);
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_RIGHT){
x=x+1;
repaint();
}
}
}
Remove the following line from public void paintComponent(Graphics g)
frame.addKeyListener(this);
and add the following line to public static void main(String args[])
frame.addKeyListener(z);
The problem was that after every repaint(); through the keyPressed method, the paintComponent method added a new KeyListener to your frame.
But if you have several KeyListeners, every Listener will invoke the keyPressed method for the same event. So if you have 5 Listeners and you press the right arrow once, the keyPressed method is invoked five times and x is incremented by five.
That meant that the rectangle moved with every hit of the right arrow a bit faster.
Try keyReleased instead of keyPressed. It may be so that while the key is pressed, it keeps invoking the keyPressed event.

Animated drawing of a successively complex image in Java

I am trying to draw an image on a JPanel that requires thousands of calculations, and I want to animate the progression of the drawing. I.e., instead of doing all 100K iterations of drawing in one go, and then repainting the JPanel, I want to repaint after each iteration, then pause for a fraction of a second, so the user sees the image gradually appearing. However, each refresh of the JPanel erases previous drawings, so my method doesn't work. How can I do this without replicating all (1..N-1) calculations on the Nth iteration?
Consider this example: I want "snow" to gradually appear on the screen. However, this code will only show the 100,000th "snowflake" as all previous ones get erased each time repaint() is called.
import javax.swing.*;
import java.awt.*;
import java.util.Random;
class spanel extends JPanel{
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawLine(snow.x, snow.y, snow.x, snow.y);
}
}
class snow extends Thread {
static int x,y;
Random r = new Random();
public void run(){
JFrame sboard = new JFrame();
sboard.setSize(600,600);
sboard.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
spanel mypanel = new spanel();
sboard.add(mypanel);
sboard.setVisible(true);
for (int i=0;i<100000;i++){
x=r.nextInt(600);
y=r.nextInt(600);
sboard.repaint();
try {
snow.sleep((long)10);
} catch (InterruptedException e) {};
}
}
}
public class SnowAnim {
public static void main(String[] args) {
(new snow()).start();
}
}
Custom Painting Approaches shows how to draw on a BufferedImage. There are also plenty of other examples in the forum.
Also, when doing animation you should use a Swing Timer to schedule the animation.
How can I do this without replicating all (1..N-1) calculations on the Nth iteration?
Add them to a BufferedImage as seen in this example.
You should probably do your painting on a buffer, then draw the current state of the buffer in paintComponent();
You could also skip the call to super.paintComponent(g);, but then you would have to worry about other elements getting visually "stuck" in your panel.
Thanks all! I figured it out. BufferedImage solves it. For the record, here is my updated code. It also implements camickr's suggestion to use a Swing timer instead of a thread to schedule the animation. I also made some aesthetic changes to make the output look more like snow :-) Thanks again!
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.util.Random;
import java.awt.event.*;
class spanel extends JPanel{
int x,y,rad,i;
static Random r = new Random();
BufferedImage image;
Graphics2D g2d;
Timer timer;
spanel(){
image = new BufferedImage(600, 600, BufferedImage.TYPE_INT_ARGB);
g2d = (Graphics2D)image.getGraphics();
setBackground(Color.black);
g2d.setColor(Color.white);
i=0;
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent ae) {
iterate();
}
};
timer = new Timer(10, listener);
timer.start();
}
public void iterate(){
x=r.nextInt(600);
y=r.nextInt(600);
rad=r.nextInt(5)+5;
g2d.fillOval(x, y, rad, rad);
repaint();
i++;
if (i==1000){timer.stop();}
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(image,0,0,null);
}
}
public class SnowAnim {
public static void main(String[] args) {
JFrame sboard = new JFrame();
sboard.setSize(600,600);
sboard.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
spanel mypanel = new spanel();
sboard.add(mypanel);
sboard.setVisible(true);
}
}

simple intro java game programming

My question is where did i go wrong. it is supposed to make a frame where i can control an oval, move it around back forth left and right, and then make it move with the arrows. but right now i cant even make the oval, or even insert a word into it.
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class JavaGame extends JFrame{
int x, y;
public class AL extends KeyAdapter {
public void keyPressed(KeyEvent e){
int keyCode = e.getKeyCode();
if(keyCode ==e.VK_LEFT){
x--;
}
if(keyCode ==e.VK_RIGHT){
x++;
}
if(keyCode ==e.VK_DOWN){
y--;
}
if(keyCode==e.VK_UP){
y++;
}
}
public void keyReleased(KeyEvent e){
}
}
public JavaGame (){
addKeyListener(new AL());
setTitle("Game");
setSize(250,250);
setResizable(false);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void Paint(Graphics g){
x = 150;
y = 150;
g.fillOval(x, y, 15, 15);
repaint();
}
public static void main(String[] Args){
new JavaGame();
}
}
Probably because Paint isn't a standard Java paint method. I don't see anything resembling an event loop, either--have you considered checking out any Swing tutorials/etc.?
Recommendations:
As Dave says, you need to override the correct method
even so, you shouldn't be drawing directly in a top-level window but rather in a component that derives from JComponent such as JPanel or JComponent itself.
draw in this class's paintComponent(...) method (usually).
Use the #Override annotation to make sure that you are truly overriding a class's method.
Don't use KeyListener's with Swing but rather Key Bindings.
For example, please have a look at my sample code here: How to make an image move while listening to a keypress in Java.

Categories

Resources