Pong.Java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
public class Pong {
private static final int WIDTH = 900, HEIGHT = 700;
JFrame win = new JFrame();
Paddle paddleOne = new Paddle(1);
Paddle paddleTwo = new Paddle(2);
Graphics g;
Pong(){
init();
}
void init(){
win.setTitle("PONG");
win.setSize(WIDTH, HEIGHT);
win.addKeyListener(keyListener);
win.setLocationRelativeTo(null);
win.getContentPane().setBackground(Color.black);
win.setVisible(true);
win.getContentPane().validate();
win.repaint();
}
public void paintComponent(Graphics g){
g.setColor(Color.white);
g.fillRect(paddleOne.getX(), paddleOne.getY(), paddleOne.getHEIGHT(), paddleOne.getWIDTH());
System.out.println("drawn");
}
KeyListener keyListener = new KeyListener() {
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
switch(key){
case 87:
System.out.println("Player 1 Up");
break;
case 83:
System.out.println("Player 1 Down");
break;
case 38:
System.out.println("Player 2 Up");
break;
case 40:
System.out.println("Player 2 Down");
break;
}
win.getContentPane().validate();
win.repaint();
}
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
};
public static void main(String[] args) {
Pong p = new Pong();
}
}
Paddle.Java
public class Paddle{
private int WIDTH = 50, HEIGHT = 150, X, Y;
int playerNum;
Paddle(int playerNum){
if(playerNum == 1){
X = 10;
Y = 10;
}else if (playerNum == 2){
X = 500;
Y = 10;
}
}
public void setX(int x){
X = x;
}
public void setY(int y){
Y = y;
}
public int getWIDTH() {
return WIDTH;
}
public int getHEIGHT() {
return HEIGHT;
}
public int getX() {
return X;
}
public int getY() {
return Y;
}
}
I'm relatively new to Java programming, or more specifically Awt & Swing, what my question is, why isn't my rectangle drawing? Any help is appreciated. Thank you so much!
In order for something to be painted within in Swing, it first must extend from something Swing know's how to paint, this commonly means a JComponent (or more typically a JPanel).
Then you can override one of the paint methods, which is called by the painting subsystem, in this case, it's generally preferred to override paintComponent, but don't forget to call super.paintComponent before you do any custom painting, or you're setting yourself up for some weird and generally unpredictable painting issues.
Take a look at Painting in AWT and Swing and Performing Custom Painting for more details.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Pongo {
public static void main(String[] args) {
new Pongo();
}
public Pongo() {
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 PongPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class PongPane extends JPanel {
private static final int WIDTH = 900, HEIGHT = 700;
Paddle paddleOne = new Paddle(1);
Paddle paddleTwo = new Paddle(2);
public PongPane() {
setBackground(Color.BLACK);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.white);
g.fillRect(paddleOne.getX(), paddleOne.getY(), paddleOne.getHEIGHT(), paddleOne.getWIDTH());
//System.out.println("drawn"); //Should not put something here which may overhead.
}
}
public class Paddle {
private int WIDTH = 50, HEIGHT = 150, X, Y;
int playerNum;
Paddle(int playerNum) {
if (playerNum == 1) {
X = 10;
Y = 10;
} else if (playerNum == 2) {
X = 500;
Y = 10;
}
}
public void setX(int x) {
X = x;
}
public void setY(int y) {
Y = y;
}
public int getWIDTH() {
return WIDTH;
}
public int getHEIGHT() {
return HEIGHT;
}
public int getX() {
return X;
}
public int getY() {
return Y;
}
}
}
I would also discourage the use of KeyListener within this context and inside advice the use of the key bindings API, it doesn't suffer from the same focus related issues that KeyListener does. See How to Use Key Bindings for more details
You need to overriding paintComponents to draw.
Here is your Pong.java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Pong {
private static final int WIDTH = 900, HEIGHT = 700;
JFrame win = new JFrame();
Paddle paddleOne = new Paddle(1);
Paddle paddleTwo = new Paddle(2);
Graphics g;
Pong() {
init();
}
void init() {
win.setTitle("PONG");
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
win.setSize(WIDTH, HEIGHT);
win.addKeyListener(keyListener);
win.setLocationRelativeTo(null);
win.add(new Panel(paddleOne));
win.getContentPane().setBackground(Color.black);
win.setVisible(true);
win.getContentPane().validate();
win.repaint();
}
KeyListener keyListener = new KeyListener() {
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
switch (key) {
case 87:
System.out.println("Player 1 Up");
break;
case 83:
System.out.println("Player 1 Down");
break;
case 38:
System.out.println("Player 2 Up");
break;
case 40:
System.out.println("Player 2 Down");
break;
}
win.getContentPane().validate();
win.repaint();
}
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
};
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Pong();
}
});
}
}
Here Panel.java in where paintComponent overridden.
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Panel extends JPanel{
private Paddle paddleOne;
public Panel(Paddle pdl) {
paddleOne = pdl;
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.white);
g.fillRect(paddleOne.getX(), paddleOne.getY(), paddleOne.getHEIGHT(), paddleOne.getWIDTH());
//System.out.println("drawn"); //Should not put something here which may overhead.
}
}
Related
I have a project that a circle goes with random x and y values and with selected colors but when the user pressed the space bar the color of the circle must be changed. My circle moves both x and y coordinate and I want to change the color of the circle when I press the space button. But it does not work when I pressed it. It goes with its original color. So how can I make this code right?
public class c {
private int x,y,r;
private Color co;
private int Re,G,B;
private Random ran;
public c() {
// TODO Auto-generated constructor stub
ran= new Random();
x=100;
y=50;
r= ran.nextInt(200)+50;
Re=ran.nextInt(255);
G=ran.nextInt(255);
B=ran.nextInt(255);
co= new Color(Re,G,B);
}
public int getRe() {
return Re;
}
public int getG() {
return G;
}
public int getB() {
return B;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getR() {
return r;
}
public void setCo(int Re,int G,int B) {
co= new Color(Re,G,B);
}
public Color getCo() {
return co;
}
public Random getRan() {
return ran;
}
public void setX(int x) {
this.x=x;
}
public void setY(int y) {
this.y=y;
}
}
public class Circle extends JFrame implements ActionListener,KeyListener{
private Timer timer;
private int x,y,a=5,b=5;
private Random rand;
c circ = new c();
public Circle() {
setLayout(new BorderLayout());
x=circ.getX();
y=circ.getY();
timer=new Timer(50,this);
timer.start();
addKeyListener(this);
setSize(550,550);
setVisible(true);
}
public void paint(Graphics g) {
super.paint(g);
g.fillOval(x,y,100,100);
g.setColor(circ.getCo());
}
public static void main(String[]args) {
new Circle();
}
#Override
public void actionPerformed(ActionEvent e) {
moveWithTimer();
repaint();
}
public void moveWithTimer() {
x=x+b;
y=y+a;
if(x<0) {
b=5;
}
if(x+50>500) {
b=-5;
}
if(y<0){
a=5;
}
if(y+50>500) {
a=-5;
}
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if(e.getKeyCode()==e.VK_SPACE) {
circ.setCo(rand.nextInt(255),rand.nextInt(255),rand.nextInt(255));
}
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
}
But it does not work when I pressed it. It goes with its original color. So how can I make this code right?
KeyListener is fickle, better to use the Key Bindings API which overcomes the primary, focus related, issues of KeyListener
As a general rule of thumb, you shouldn't override paint of top level containers like JFrame, they are compound components and it's just a real mess.
Instead, start with a JPanel and override it's paintComponent method. It's generally more flexible. Have a look at Performing Custom Painting for more details.
Your movement code is wrong. You assign the x/y values of the circle class to some other variables, the problem here is, changing the values of these variables will have no affect on the variables in you circle class, instead, you need assign them back...
public void moveWithTimer() {
int x = circ.getX();
int y = circ.getY();
x = x + b;
y = y + a;
if (x < 0) {
b = 5;
}
if (x + 50 > 500) {
b = -5;
}
if (y < 0) {
a = 5;
}
if (y + 50 > 500) {
a = -5;
}
circ.setX(x);
circ.setY(y);
}
Your "circle" class could also use a couple of additional methods. One to randomise the color (it already has a Random object, might as well use it) and one to paint the object.
public class Circle {
//...
public void paint(Graphics2D g2d) {
g2d.setColor(co);
g2d.fillOval(x, y, r * 2, r * 2);
}
//...
public void randomColor() {
setCo(ran.nextInt(255), ran.nextInt(255), ran.nextInt(255));
}
//...
}
If it was me, I'd be tempted to add a move method as well, but that's me ;)
As a runnable example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
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 Timer timer;
private int a = 5, b = 5;
private Random rand;
private Circle circ = new Circle();
public TestPane() {
timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
moveWithTimer();
repaint();
}
});
timer.start();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "spaced");
am.put("spaced", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
circ.randomColor();
repaint();
}
});
}
public void moveWithTimer() {
int x = circ.getX();
int y = circ.getY();
x = x + b;
y = y + a;
if (x < 0) {
b = 5;
}
if (x + 50 > 500) {
b = -5;
}
if (y < 0) {
a = 5;
}
if (y + 50 > 500) {
a = -5;
}
circ.setX(x);
circ.setY(y);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
circ.paint(g2d);
g2d.dispose();
}
}
public class Circle {
private int x, y, r;
private Color co;
private int Re, G, B;
private Random ran;
public Circle() {
// TODO Auto-generated constructor stub
ran = new Random();
x = 100;
y = 50;
r = ran.nextInt(50) + 50;
Re = ran.nextInt(255);
G = ran.nextInt(255);
B = ran.nextInt(255);
co = new Color(Re, G, B);
}
public void paint(Graphics2D g2d) {
g2d.setColor(co);
g2d.fillOval(x, y, r * 2, r * 2);
}
public int getRe() {
return Re;
}
public int getG() {
return G;
}
public int getB() {
return B;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getR() {
return r;
}
public void randomColor() {
setCo(ran.nextInt(255), ran.nextInt(255), ran.nextInt(255));
}
public void setCo(int Re, int G, int B) {
co = new Color(Re, G, B);
}
public Color getCo() {
return co;
}
public Random getRan() {
return ran;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
}
}
I suggest you should set the color of the graphics object within paint(g) before painting the circle.
public void paint(Graphics g) {
super.paint(g);
g.setColor(circ.getCo());
g.fillOval(x,y,100,100);
}
In general, you should not override the paint() method of the JFrame. Instead, create a JPanel, add it to your frame and override the paintComponent() method of the panel.
I'm trying to create a square that can move by pressing keys. When I Compiled & Ran the code it wouldn't move. So I began debugging (as well as I'm capable of). The problem seems to be that the run() function isn't being called. Why is this ? My understanding was that when using the interface Runnable, the run method is called automatically. I posted all the code in action.
Why isn't run() being called automatically and how can I change my program so it will call ?
Game.java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Game extends JPanel implements Runnable{
private static final int WIDTH = 800, HEIGHT = WIDTH / 12 * 9; //Widescreen
private Thread game_thread;
private boolean running = false;
public int x_speed = 0, y_speed = 0;
public Square square;
public Game(){
game_thread = new Thread("GameThread");
square = new Square(this);
addKeyListener(new KeyListener(){
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == e.VK_A){
x_speed = -1;
}
if(e.getKeyCode() == e.VK_D){
x_speed = 1;
}
if(e.getKeyCode() == e.VK_S){
y_speed = -1;
}
if(e.getKeyCode() == e.VK_W){
y_speed = 1;
}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
});
}
public void start(){
System.out.println("Started");
game_thread.start();
running = true;
System.out.println(running);
}
public void stop(){
try{
running = false;
game_thread.join();
}catch(InterruptedException e){
e.printStackTrace();
}
}
public void paint(Graphics g){
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, WIDTH, HEIGHT);
square.render(g2d);
}
public void update(){
square.move();
System.out.println(x_speed + ", " + y_speed);
}
public void run(){
System.out.println("run method started");
while(running){
System.out.println("Running");
//Update screen info
update();
//Re-render
repaint();
try{
game_thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String args[]){
JFrame frame = new JFrame("Moving Thangs");
Game game = new Game();
frame.setSize(game.WIDTH, game.HEIGHT);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.add(game);
frame.setVisible(true);
game.start();
}
}
Square.java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
public class Square {
public static final int s_WIDTH = 80, s_HEIGHT = s_WIDTH;
public int x, y;
private Game game;
public Square(Game game){
x = 50;
y = 50;
this.game = game;
}
public void move(){
if(x >= 0 && x <= game.getWidth() - s_WIDTH){
x += game.x_speed;
}
if(y >= 0 && y <= game.getHeight() - s_HEIGHT){
y += game.y_speed;
}
}
public void render(Graphics2D g2d){
g2d.setColor(Color.ORANGE);
g2d.fillRect(x, y, s_WIDTH, s_HEIGHT);
}
}
When you create the thread using new Thread("GameThread") you don't pass this as a runnable to the thread. You need to pass it as the first argument in the constructor like new Thread(this, "GameThread") and then everything should work.
I have a problem with repaint() method in my Java code. I want to call it in another class but I can't, something doesn't work at all. I've searched on forums, but nothing was able to help me out.
My Main class:
public class Main {
public static Main main;
public static JFrame f;
public Main(){
}
public static void main(String[] args) {
main = new Main();
f = new JFrame();
Ball b = new Ball();
f.getContentPane().setBackground(Color.GRAY);
f.add(b);
f.setSize(500, 500);
f.setLocationRelativeTo(null);
f.setTitle("Test");
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.addMouseMotionListener(b);
f.addKeyListener(new Key());
}
}
Ball class where I created 2DGraphics for moving shapes:
public class Ball extends JLabel implements MouseMotionListener{
public Ball(){
}
public static double x = 10;
public static double y = 10;
public static double width = 40;
public static double height = 40;
String nick;
boolean isEllipse = true;
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if(isEllipse){
Ellipse2D e2d = new Ellipse2D.Double(x, y, width, height);
g2d.setColor(Color.RED);
g2d.fill(e2d);
}
else{
Rectangle2D r2d = new Rectangle2D.Double(x, y, width, height);
g2d.setColor(Color.GREEN);
g2d.fill(r2d);
}
}
#Override
public void mouseDragged(MouseEvent e) {
isEllipse = false;
x = e.getX() - 30;
y = e.getY() - 40;
this.repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
x = e.getX() - 30;
y = e.getY() - 40;
isEllipse = true;
this.repaint();
}
}
And Key class where I put KeyListener for move the shapes by key pressing:
public class Key extends Ball implements KeyListener {
public Key() {
}
#SuppressWarnings("static-access")
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_W){
super.x += 10;
super.repaint();
System.out.println("x: " + super.x);
}
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
}
But something is wrong with this code: super method doesn't work for Key class. Everything in Ball class is working well. Where is the problem?
Super works fine, but your interpretation of what it does is wrong. Your problem is that you're trying to use inheritance to solve a problem that isn't solved with inheritance. You need to call repaint() on the actual visualized and used Ball instance, not on an instance of some completely different class, Key, that inappropriately extends from Ball. First off, make Key not extend Ball, pass in a true Ball reference into Key and the solution will fall from that.
Perhaps do something like this:
f.addKeyListener(new Key(b));
and
public class Key implements KeyListener {
private Ball ball;
public Key(Ball ball) {
this.ball = ball;
}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_W){
b.incrX(10); // give Ball a public method for this
b.repaint();
// System.out.println("x: " + super.x);
}
}
// .... etc...
Note, myself, I'd use Key Bindings for this, not a KeyListener, since then I wouldn't have to futz with keyboard focus.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.*;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class MoveBall {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private static void createAndShowGui() {
BallPanel ballPanel = new BallPanel(PREF_W, PREF_H);
MyMouse myMouse = new MyMouse(ballPanel);
ballPanel.addMouseListener(myMouse);
ballPanel.addMouseMotionListener(myMouse);
new CreateKeyBindings(ballPanel);
JFrame frame = new JFrame("MoveBall");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(ballPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}
#SuppressWarnings("serial")
class BallPanel extends JPanel {
private static final Color ELLIPSE_COLOR = Color.RED;
private static final Color SQUARE_COLOR = Color.GREEN;
private static final int BALL_WIDTH = 40;
private int prefW;
private int prefH;
private boolean isEllipse = true;
private int ballX;
private int ballY;
public BallPanel(int prefW, int prefH) {
this.prefW = prefW;
this.prefH = prefH;
}
public boolean isEllipse() {
return isEllipse;
}
public void setEllipse(boolean isEllipse) {
this.isEllipse = isEllipse;
}
public int getBallX() {
return ballX;
}
public void setBallX(int ballX) {
this.ballX = ballX;
}
public void setXY(int x, int y) {
ballX = x;
ballY = y;
repaint();
}
public void setXYCenter(int x, int y) {
ballX = x - BALL_WIDTH / 2;
ballY = y - BALL_WIDTH / 2;
repaint();
}
public void setXYCenter(Point p) {
setXYCenter(p.x, p.y);
}
public int getBallY() {
return ballY;
}
public void setBallY(int ballY) {
this.ballY = ballY;
}
public void incrementBallX(int x) {
ballX += x;
repaint();
}
public void incrementBallY(int y) {
ballY += y;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (isEllipse) {
g2.setColor(ELLIPSE_COLOR);
g2.fillOval(ballX, ballY, BALL_WIDTH, BALL_WIDTH);
} else {
g2.setColor(SQUARE_COLOR);
g2.fillOval(ballX, ballY, BALL_WIDTH, BALL_WIDTH);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(prefW, prefH);
}
}
class MyMouse extends MouseAdapter {
private BallPanel ballPanel;
public MyMouse(BallPanel ballPanel) {
this.ballPanel = ballPanel;
}
#Override
public void mousePressed(MouseEvent e) {
ballPanel.setXYCenter(e.getPoint());
}
#Override
public void mouseDragged(MouseEvent e) {
ballPanel.setXYCenter(e.getPoint());
}
#Override
public void mouseReleased(MouseEvent e) {
ballPanel.setXYCenter(e.getPoint());
}
}
enum Direction {
UP(KeyEvent.VK_UP), DOWN(KeyEvent.VK_DOWN), LEFT(KeyEvent.VK_LEFT), RIGHT(KeyEvent.VK_RIGHT);
private int key;
private Direction(int key) {
this.key = key;
}
public int getKey() {
return key;
}
}
// Actions for the key binding
#SuppressWarnings("serial")
class MyKeyAction extends AbstractAction {
private static final int STEP_DISTANCE = 5;
private BallPanel ballPanel;
private Direction direction;
public MyKeyAction(BallPanel ballPanel, Direction direction) {
this.ballPanel = ballPanel;
this.direction = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
switch (direction) {
case UP:
ballPanel.incrementBallY(-STEP_DISTANCE);
break;
case DOWN:
ballPanel.incrementBallY(STEP_DISTANCE);
break;
case LEFT:
ballPanel.incrementBallX(-STEP_DISTANCE);
break;
case RIGHT:
ballPanel.incrementBallX(STEP_DISTANCE);
break;
default:
break;
}
}
}
class CreateKeyBindings {
private BallPanel ballPanel;
public CreateKeyBindings(BallPanel ballPanel) {
this.ballPanel = ballPanel;
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = ballPanel.getInputMap(condition);
ActionMap actionMap = ballPanel.getActionMap();
for (Direction direction : Direction.values()) {
KeyStroke keyStroke = KeyStroke.getKeyStroke(direction.getKey(), 0);
String keyString = keyStroke.toString();
inputMap.put(keyStroke, keyString);
actionMap.put(keyString, new MyKeyAction(ballPanel, direction));
}
}
}
The program draws a bunch of rectangles for a bar graph. I know the bar class works perfectly fine because I've got it working before adding in the graph panel class. I was drawing straight onto the frame instead of the graph panel. I assume its a problem in the way my set visible methods are called as it was pointed out to me before. I tried looking into it but I've had no luck after playing around and reading documentation.
import java.awt.Color;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.concurrent.Semaphore;
#SuppressWarnings("serial")
public class GraphPanel extends JPanel {
private ArrayList<Bar> graphBars;
private int nBars;
public GraphPanel(int nBars, JFrame mainFrame) {
this.setSize(400, 400);
this.graphBars = new ArrayList<Bar>(nBars);
this.nBars = nBars;
this.initBars(mainFrame.getWidth());
for(Bar b: this.graphBars) {
this.add(b);
}
}
private void initBars(int frameW) {
Random random = new Random();
float hue;
Color color;
int barPadding = frameW/this.nBars;
for(int i = 0; i < this.nBars; i++) {
hue = random.nextFloat();
color = Color.getHSBColor(hue, 0.9f, 1.0f);
this.graphBars.add(new Bar(i*barPadding + 30, 350, color));
}
}
public ArrayList<Bar> getBarList() {
return this.graphBars;
}
}
#SuppressWarnings("serial")
public class Bar extends JPanel implements Runnable {
int height = 0;
Color barColor;
Rectangle bar;
private final int WIDTH = 20;
Thread bartender;
private Semaphore s;
public Bar(int x, int y, Color barColor) {
this.barColor= barColor;
this.bar = new Rectangle(x, y, this.WIDTH, this.height);
this.bartender= new Thread(this);
this.s = new Semaphore(1);
}
public boolean setNewHeight(int h) {
try {
this.s.acquire();
this.height = h;
this.s.release();
return true;
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
#SuppressWarnings("deprecation")
public void update() {
if (this.bar.height < this.height) {
bar.reshape(this.bar.x, --this.bar.y, this.bar.width, ++this.bar.height);
} else {
bar.reshape(this.bar.x, ++this.bar.y, this.bar.width, --this.bar.height);
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(this.barColor);
g2d.fill(this.bar);
}
#SuppressWarnings("deprecation")
public void callBarTender() {
this.bartender.resume();
}
#SuppressWarnings("deprecation")
#Override
public void run() {
System.out.println("sdf");
while(true) {
if (this.bar.height < this.height) {
for(int i = this.bar.height; i<this.height; i++ ) {
try {
update();
repaint();
Thread.sleep(15);
} catch(Exception e) {
System.out.println(e);
}
}
} else if (this.height < this.bar.height) {
for(int i = this.bar.height; i>this.height; i-- ) {
try {
update();
repaint();
Thread.sleep(15);
} catch(Exception e) {
System.out.println(e);
}
}
}
this.bartender.suspend();
}
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(400, 400);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GraphPanel gPane = new GraphPanel(3, frame);
frame.add(gPane);
gPane.getBarList().get(0).setVisible(true);
gPane.getBarList().get(1).setVisible(true);
gPane.getBarList().get(2).setVisible(true);
gPane.setVisible(true);
frame.setVisible(true);
gPane.getBarList().get(0).setNewHeight(100);
gPane.getBarList().get(1).setNewHeight(100);
gPane.getBarList().get(2).setNewHeight(100);
gPane.getBarList().get(0).bartender.start();
gPane.getBarList().get(1).bartender.start();
gPane.getBarList().get(2).bartender.start();
}
You should override getPreferredSize of your GraphPanel to ensure that they are laid out correctly
The x/y positions you are passing to the Bar class are irrelevant, as this is causing your Rectangle to paint outside of the visible context of the Bar pane. Painting is done from within the context of the component (0x0 been the top/left corner of the component)
The use of Rectangle or the way you are using it, is actually causing issues. It's impossible to know exactly how big you component will be until it's layed or painted
There is a reason why resume and suspend are deprecated, this could cause no end of "weird" (and wonderful) issues
Take a look at Laying Out Components Within a Container for why you're bars aren't been updated correctly and why the x/y coordinates are pointless
Take a look at How to use Swing Timers for an alternative to your use of Thread
Possibly, something more like...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
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();
frame.setSize(400, 400);
// frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GraphPanel gPane = new GraphPanel(3, frame);
frame.add(gPane);
gPane.getBarList().get(1).setFill(false);
gPane.getBarList().get(0).start();
gPane.getBarList().get(1).start();
gPane.getBarList().get(2).start();
frame.setVisible(true);
}
});
}
public class GraphPanel extends JPanel {
private ArrayList<Bar> graphBars;
private int nBars;
public GraphPanel(int nBars, JFrame mainFrame) {
this.graphBars = new ArrayList<Bar>(nBars);
this.nBars = nBars;
this.initBars(mainFrame.getWidth());
for (Bar b : this.graphBars) {
this.add(b);
}
}
private void initBars(int frameW) {
Random random = new Random();
float hue;
Color color;
for (int i = 0; i < this.nBars; i++) {
hue = random.nextFloat();
color = Color.getHSBColor(hue, 0.9f, 1.0f);
this.graphBars.add(new Bar(color));
}
}
public ArrayList<Bar> getBarList() {
return this.graphBars;
}
}
#SuppressWarnings("serial")
public class Bar extends JPanel {
private Color barColor;
private boolean fill = true;
private float fillAmount = 0;
private float delta = 0.01f;
private Timer timer;
private Rectangle bar;
public Bar(Color barColor) {
bar = new Rectangle();
setBorder(new LineBorder(Color.RED));
this.barColor = barColor;
timer = new Timer(15, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
fillAmount += isFill() ? delta : -delta;
// System.out.println(fillAmount);
if (fillAmount < 0) {
fillAmount = 0;
((Timer) e.getSource()).stop();
} else if (fillAmount > 1.0f) {
fillAmount = 1f;
((Timer) e.getSource()).stop();
}
repaint();
}
});
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
public void setFill(boolean fill) {
this.fill = fill;
if (!timer.isRunning()) {
if (fill && fillAmount == 1) {
fillAmount = 0;
} else if (!fill && fillAmount == 0) {
fillAmount = 1;
}
}
}
public boolean isFill() {
return fill;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(20, 100);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(this.barColor);
int height = Math.round(getHeight() * fillAmount);
bar.setSize(getWidth(), height);
bar.setLocation(0, getHeight() - height);
g2d.fill(bar);
g2d.dispose();
}
}
}
I decided to re-write my game using Swing's painting technique paintComponent() method(someone on SO actually told me to use this method). I decided to use JPanel as the game's base instead of Canvas. My previous written game uses a Canvas but the game could not show up on my 64 bit desktop but could show up on my 32 bit labtop which is why this game had to be re-written.
Problem now is, while the ship's movement works, the drawing seems awfully slow(unless it is my laptop problem?) compare to what I did before which was using AWT's double buffering drawing technique. I spend a whole day but could not figure out what could possibly make the ship run faster.
public class Ship extends JLabel implements KeyListener{
private Image image;
private boolean turnRight;
private int x;
private int y;
private int speed = 5;
private boolean turnLeft;
public Ship(int x, int y)
{
this.x = x;
this.y = y;
try {
image = ImageIO.read(new File("Ship/Ship.PNG"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
addKeyListener(this);
}
public Image getImage()
{
return image;
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if(e.getKeyCode() == KeyEvent.VK_RIGHT)
{
x += speed;
setTurnRight(true);
setTurnLeft(false);
}
else if(e.getKeyCode() == KeyEvent.VK_LEFT)
{
x -= speed;
setTurnLeft(true);
setTurnRight(false);
}
// redraw yourself
repaint();
}
private void setTurnLeft(boolean turnLeft) {
// TODO Auto-generated method stub
this.turnLeft = turnLeft;
}
// swing custom painting
public void paintComponent(Graphics g)
{
if(x <= 0)
{
x = 0;
}
else if(x >= 610)
{
x = 610;
}
g.drawImage(getImage(), x, y, null);
}
public void setTurnRight(boolean turnRight)
{
this.turnRight = turnRight;
}
public boolean getTurnLeft()
{
return turnLeft;
}
public boolean getTurnRight()
{
return turnRight;
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
}
Normally, I would create a concept of a renderable element. I would maintain a list of these elements and update them accordingly within my main loop.
At the very least, each would have a concept of location, direction and rotation (if required), they would also be capable of been painted.
Within my main component, I would simply loop through all these elements and "draw" them, offset the Graphics context to allow for there position within the game space.
But that's not what you are doing...
Remember, components have a sense of location and size already, you shouldn't be trying to re-implement this requirement, instead, you should be finding ways to take advantage of it...(ie, don't maintain a reference to the x/y values ;))
The following is a simple example. It uses a JPanel to render the main image. The main loop (in this case a javax.swing.Timer), tells the component that it should update it's movement as required.
The ship itself is responding to key events by changing the rotation value by a given, variable, delta. This allows you to control the speed of the spin as you need (I've deliberately set it low to start with, so play around with it)
What you should resist doing, is changing the frame rate ;)
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
public class BattleShipGame {
public static void main(String[] args) {
new BattleShipGame();
}
public BattleShipGame() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new OceanPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class OceanPane extends JPanel {
private BattleShip ship;
public OceanPane() {
setLayout(new GridBagLayout());
ship = new BattleShip();
add(ship);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
ship.move();
revalidate();
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
public static class BattleShip extends JPanel {
protected static final int MAX_TURN_RATE = 5;
private BufferedImage ship;
private float angle;
private float angleDelta;
public BattleShip() {
setOpaque(false);
try {
ship = ImageIO.read(new File("BattleShip.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
setFocusable(true);
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "leftTurn");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "rightTurn");
am.put("leftTurn", new TurnAction(-0.1f));
am.put("rightTurn", new TurnAction(0.1f));
}
public void move() {
angle += angleDelta;
}
public void setAngle(float angle) {
this.angle = angle;
}
public float getAngle() {
return angle;
}
#Override
public Dimension getPreferredSize() {
Dimension size = new Dimension(0, 0);
if (ship != null) {
double rads = Math.toRadians(getAngle());
double sin = Math.abs(Math.sin(rads)), cos = Math.abs(Math.cos(rads));
int w = ship.getWidth();
int h = ship.getHeight();
size.width = (int) Math.floor(w * cos + h * sin);
size.height = (int) Math.floor(h * cos + w * sin);
}
return size;
}
#Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (ship != null) {
Graphics2D g2d = (Graphics2D) g.create();
double rads = Math.toRadians(getAngle());
double sin = Math.abs(Math.sin(rads)), cos = Math.abs(Math.cos(rads));
int w = ship.getWidth();
int h = ship.getHeight();
int newWidth = (int) Math.floor(w * cos + h * sin);
int newHeight = (int) Math.floor(h * cos + w * sin);
AffineTransform at = new AffineTransform();
at.translate((newWidth - w) / 2, (newHeight - h) / 2);
at.rotate(Math.toRadians(getAngle()), w / 2, h / 2);
g2d.drawImage(ship, at, this);
g2d.dispose();
}
}
protected class TurnAction extends AbstractAction {
protected float delta;
public TurnAction(float delta) {
this.delta = delta;
}
#Override
public void actionPerformed(ActionEvent e) {
angleDelta += delta;
if (angleDelta > MAX_TURN_RATE) {
angleDelta = MAX_TURN_RATE;
} else if (angleDelta < (MAX_TURN_RATE * -1)) {
angleDelta = (MAX_TURN_RATE * -1);
}
}
}
}
}
I would recommend having a class which extends JPanel, using a javax.swing.Timer in there, defining your 1000/fps and your ActionListener, in which you use a repaint() which uses a paintComponent that you will make that would call upon the draw method in your Ship, which is now called paintComponent.
So, as that explaination was terrible, here is some code:
public class Class_Name extends JPanel()
{
Ship ship = new Ship(0,0);
javax.swing.Timer timer = new javax.swing.Timer(1000/60, new ActionListener(){
repaint();
});
public void paintComponent(Graphics g)
{
super.paintComponent();
ship.draw(g);
}
}
and the draw is, what is now called paintComponent.
If this didn't answer your question, please let me know.