I can draw static things to the screen, but I want to make them move with user key input. I don't know what to do, I've been searching and searching and haven't come up with an answer yet. Please help!
package com.Game.game;
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.JPanel;
public class Game extends JFrame
{
final static int width = 500;
final static int height = 500;
public int x = 250;
public int y = 250;
public int changeX = 10;
public int changeY = 10;
public static void main(String[] args)
{
new Game();
}
public Game()
{
KeyListener listener = new KeyListening();
addKeyListener(listener);
setFocusable(true);
DrawingStuff drawingstuff = new DrawingStuff();
add(drawingstuff);
setSize(width, height);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public class DrawingStuff extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawString("Hey there!", 300, 300);
g.setColor(Color.RED);
g.fillRect(x, y, 50, 50);
}
}
public class KeyListening implements KeyListener
{
DrawingStuff drawingstuff = new DrawingStuff();
#Override
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode() == KeyEvent.VK_UP)
{
y = y + changeY;
System.out.println("Hey");
drawingstuff.repaint();
}
}
#Override
public void keyReleased(KeyEvent e)
{
}
#Override
public void keyTyped(KeyEvent e)
{
}
}
public void update()
{
}
}
EDIT: Fixed it. I took away the key listener stuff in the constructor method, added a command to focus on "drawingstuff" in the constructor method, and, most importantly, added this bit of code to the end of the constructor method:
while(true)
{
drawingstuff.repaint();
}
The problem is that your KeyListening object has a reference to a different DrawingStuff object than the one you added to your UI inside the Game constructor.
public class KeyListening implements KeyListener
{
DrawingStuff drawingstuff = new DrawingStuff();
...
You should pass a DrawingStuff reference to the KeyListening instance so that it can tell the right object to repaint itself.
Related
I need the loop() method to be able to run repaint() but it does nothing when it's called.
Moreover, the loop() method also doesn't update() when executing the code. I've tried many different variations to try and get it to work but nothing has worked. In addition to that, I use the IDE Eclipse because my teacher said we shouldn't use game engines but write the code from scratch.
class:GamePanel
package main;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class GamePanel extends JPanel implements Runnable {
private static final long serialVersionUID = 1L;
final int originalTilesize=16;
final int scale=3;
final int tileSize=originalTilesize *scale;
final int maxScreenRow=12;
final int maxScreenCol=16;
final int screenWidth=tileSize*maxScreenCol;
final int screenHeight=tileSize*maxScreenRow;
int FPS=60;
KeyHandler keyH = new KeyHandler(); // implements KeyHandler in GamePanel
Thread gameThread;
int playerX=100;
int playerY=100;
int playerSpeed=4;
public GamePanel()
{
this.setPreferredSize(new Dimension(screenWidth,screenHeight));
this.setBackground(Color.black);
this.setDoubleBuffered(true);
this.addKeyListener(keyH);
this.setFocusable(true);
}
public void startGameThread()
{
gameThread = new Thread(this);
gameThread.start();
}
public void run() {
double drawInterval=1000000000/FPS;
double nextDrawTime=System.nanoTime()+drawInterval;// TODO Auto-generated method stub
while (gameThread !=null) {
update();
repaint();
try {
double remainingTime=nextDrawTime-System.nanoTime();
remainingTime=remainingTime/1000000;
if (remainingTime<0)
{
remainingTime=0;
}
Thread.sleep((long)remainingTime);
nextDrawTime+=drawInterval;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
}
public void update()
{
if (keyH.upPressed == true)
{
playerY-=playerSpeed;
}
else if(keyH.downPressed==true)
{
playerY +=playerSpeed;
}
else if(keyH.leftPressed==true)
{
playerX -=playerSpeed;
}
else if(keyH.rightPressed==true)
{
playerX +=playerSpeed;
}
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2=(Graphics2D)g;
g2.setColor(Color.white);
g2.fillRect(playerX,playerY,tileSize,tileSize);
g2.dispose();
}
}
class:KeyListener
package main;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class KeyHandler implements KeyListener {
public boolean upPressed,downPressed,leftPressed,rightPressed;
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
int code =e.getKeyCode();
if (code==KeyEvent.VK_W) {
upPressed = true;
}
if (code==KeyEvent.VK_S) {
downPressed=true;
}
if (code==KeyEvent.VK_A) {
leftPressed=true;
}
if (code==KeyEvent.VK_D) {
rightPressed=true;
}
}
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
int code=e.getKeyCode();
if (code==KeyEvent.VK_W) {
upPressed = false;
}
if (code==KeyEvent.VK_S) {
downPressed=false;
}
if (code==KeyEvent.VK_A) {
leftPressed=false;
}
if (code==KeyEvent.VK_D) {
rightPressed=false;
}
}
}
You don't need a "game loop" that redraws the GUI every few seconds. You just need to call method repaint. Also, it is not recommended to call method repaint from a thread which is not the Event Dispatch Thread (EDT). Lastly, you should use key bindings rather than a KeyListener.
The below code is not a complete solution. It just sets up a key binding for the down-arrow key which moves the rectangle down when the user presses the down-arrow key. Note that you also need to handle the situation when the rectangle hits the edge of the GamePanel – unless you want to draw the rectangle at such a location that it doesn't appear in the GamePanel.
(More notes after the code.)
package main;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
public class GamePanel extends JPanel {
private static final long serialVersionUID = 1L;
final int originalTilesize = 16;
final int scale = 3;
final int tileSize = originalTilesize * scale;
final int maxScreenRow = 12;
final int maxScreenCol = 16;
final int screenWidth = tileSize * maxScreenCol;
final int screenHeight = tileSize * maxScreenRow;
int playerX = 100;
int playerY = 100;
int playerSpeed = 4;
public GamePanel() {
setPreferredSize(new Dimension(screenWidth, screenHeight));
setBackground(Color.black);
setFocusable(true);
InputMap im = getInputMap();
im.put(KeyStroke.getKeyStroke("DOWN"), "DOWN");
ActionMap am = getActionMap();
am.put("DOWN", new DownAction());
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.white);
g.fillRect(playerX, playerY, tileSize, tileSize);
g.dispose();
}
class DownAction extends AbstractAction {
#Override
public void actionPerformed(ActionEvent e) {
playerY += playerSpeed;
repaint();
}
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame frame = new JFrame();
frame.add(new GamePanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
This line is not required as it is the default.
this.setDoubleBuffered(true);
Method startGameThread is not required (as explained above) and should therefore be removed. Consequently, method run should also be removed which means that class GamePanel does not need to implement interface Runnable. Also no need for variables FPS and gameThread.
As shown in above code, you need to write an Action for each key binding which therefore replaces method update so that method can also be removed. In the above code, I have created one Action, named DownAction. You need to create an Action for the other arrow keys. The keystroke names are:
UP
RIGHT
LEFT
Since we are using key bindings, no need for class KeyHandler nor for variable keyH.
Method paintComponent has protected access so no need to make it public.
In method paintComponent you are only calling methods of class Graphics so no need for the cast.
I added a main method so that the above code is a runnable application.
While using Swing in java, I am trying to move a circle slowly from a starting position to an end position when clicking a button. However, I can't see the circle moving. It just moves from start to end in an instant.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MyApp {
private int x = 10;
private int y = 10;
private JFrame f;
private MyDraw m;
private JButton b;
public void go() {
f = new JFrame("Moving circle");
b = new JButton("click me to move circle");
m = new MyDraw();
f.add(BorderLayout.SOUTH, b);
f.add(BorderLayout.CENTER, m);
f.setSize(500, 500);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
b.addActionListener(new Bute());
}
public static void main(String[] args) {
MyApp m = new MyApp();
m.go();
}
private class Bute implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < 150; i++) {
++x;
++y;
m.repaint();
Thread.sleep(50);
}
}
}
private class MyDraw extends JPanel {
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.white);
g.fillRect(0, 0, 500, 500);
g.setColor(Color.red);
g.fillOval(x, y, 40, 40);
}
}
}
I think the problem is with the action listener because when I'm doing it without using button it is working. Any suggestions?
As Andrew Thompson said, calling Thread.sleep() without defining a second thread freezes everything, so the solution is to define and run another thread like so:
class Bute implements ActionListener, Runnable {
//let class implement Runnable interface
Thread t; // define 2nd thread
public void actionPerformed(ActionEvent e) {
t = new Thread(this); //start a new thread
t.start();
}
#Override //override our thread's run() method to do what we want
public void run() { //this is after some java-internal init stuff called by start()
//b.setEnabled(false);
for (int i = 0; i < 150; i++) {
x++;
y++;
m.repaint();
try {
Thread.sleep(50); //let the 2nd thread sleep
} catch (InterruptedException iEx) {
iEx.printStackTrace();
}
}
//b.setEnabled(true);
}
}
The only problem with this solution is that pressing the button multiple times will speed up the circle, but this can be fixed by making the button unclickable during the animation via b.setEnabled(true/false). Not the best solution but it works.
As said in the comments and another answer, don't block the EDT. Thead.sleep(...) will block it, so you have two options:
Create and manage your own (new) thread.
Use a Swing Timer
In this answer I'll be using a Swing Timer, since it's easier to use. I also changed the paintComponent method to use the Shape API and change the button text to start and stop accordingly as well as reusing the same ActionListener for the button and the timer:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MovingCircle {
private JFrame frame;
private CustomCircle circle;
private Timer timer;
private JButton button;
public static void main(String[] args) {
SwingUtilities.invokeLater(new MovingCircle()::createAndShowGui);
}
private void createAndShowGui() {
frame = new JFrame(this.getClass().getSimpleName());
circle = new CustomCircle(Color.RED);
timer = new Timer(100, listener);
button = new JButton("Start");
button.addActionListener(listener);
circle.setBackground(Color.WHITE);
frame.add(circle);
frame.add(button, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private ActionListener listener = (e -> {
if (!timer.isRunning()) {
timer.start();
button.setText("Stop");
} else {
if (e.getSource().equals(button)) {
timer.stop();
button.setText("Start");
}
}
circle.move(1, 1);
});
#SuppressWarnings("serial")
class CustomCircle extends JPanel {
private Color color;
private int circleX;
private int circleY;
public CustomCircle(Color color) {
this.color = color;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(color);
g2d.fill(new Ellipse2D.Double(circleX, circleY, 50, 50));
}
#Override
public Dimension preferredSize() {
return new Dimension(100, 100);
}
public void move(int xGap, int yGap) {
circleX += xGap;
circleY += yGap;
revalidate();
repaint();
}
public int getCircleX() {
return circleX;
}
public void setCircleX(int circleX) {
this.circleX = circleX;
}
public int getCircleY() {
return circleY;
}
public void setCircleY(int circleY) {
this.circleY = circleY;
}
}
}
I'm sorry, I can't post a GIF as I wanted but this example runs as expected.
I want my button to change color on the mod == 0 of i % 3. The paintComponent(...) is called when the form is re-sized and index is passed in so I would think that this should change the color of my button ever time I start moving the form around the screen.
I have two components on the screen but both will not show up this might be a factor.
Code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class testform {
public static void main (String[] p) {
testBall3 j1 = new testBall3();
myButton b1 = new myButton("test");
JPanel testPane = new JPanel();
testPane.setBackground(Color.green);
testPane.setLayout(new BorderLayout());
j1.setPreferredSize(new Dimension(10,10));
//testPane.add(b1);
testPane.add(j1);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(testPane);
frame.pack();
frame.setSize(300, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
//j1.setColorBall(Color.BLACK);
//j1.repaint();
}
}
class myButton extends JButton {
private static final long serialVersionUID = 1L;
public myButton(String s) {
super(s);
}
public void setPrefferedSize(Dimension d) {
//this.setBounds(x, y, width, height)
setPreferredSize(d);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
index += i;
System.out.println(i);
if (index % 3 == 0) {
setBackground(Color.RED);
}
else {
setBackground(Color.BLACK);
}
}
}
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;
class testBall3 extends JComponent
{
private static final long serialVersionUID = 1L;
private Color colorBall = Color.red;
private int x1, y1;
int index = 0;
public void setColorBall(Color c)
{
this.colorBall = c;
}
public testBall3()
{
super();
System.out.println("MyBall (0)");
}
public testBall3(int x, int y, int diameter)
{
super();
this.setLocation(x, y);
this.setSize(diameter, diameter);
System.out.println("MyBall (1)");
x1 = x;
y1 = y;
}
public void paintBorder(Graphics g)
{
super.paintBorder(g);
g.setColor(Color.YELLOW);
g.fillOval(100, 100, 50, 50);
System.out.println("PaintBorder");
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(colorBall);
g.fillOval(x1, y1, 10, 10);
System.out.println("paintComponent");
}
public void paint(Graphics g)
{
super.paint(g);
paintComponent(g);
paintBorder(g);
paintChildren(g);
System.out.println("Paint");
}
}
But paintComponent doesn't take a second parameter, how are you passing it? I would think that instead of trying to pass i, you would want make i an attribute of class myButton instead and initialize it to 0 upon instantiation. That is, if you want each button to have its own counter. That sounds like the better plan.
You've got a lot of strange stuff going on...
You've got a component where you override all four major painting methods for no good reason.
In this component, your paint method override calls the super method, and calls the other 3 methods, which in essence will make those 3 methods be called twice.
You've got program logic (advancement of i) inside of your myButton's paintComponent method -- something that should never be done. You do not have full control over when or even if this method is called.
You are calling setBackground(...) from within paintComponent, something which shouldn't be done.
Your class names do not begin with an upper case letter, going against coding conventions, and potentially confusing anyone who tries to read your code.
If you want to change the state of a component on resize, use a ComponentListener.
e.g.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class Foo2 extends JPanel {
protected static final Color MAGIC_BACKGROUND = Color.red;
protected static final int MAGIC_NUMBER = 3;
private static final int TIMER_DELAY = 20;
private int index = 0;
private JButton myButton = new JButton("My Button");
protected int DELTA_SIZE = 2;
public Foo2() {
add(myButton);
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
index++;
if (index % MAGIC_NUMBER == 0) {
myButton.setBackground(MAGIC_BACKGROUND);
} else {
myButton.setBackground(null);
}
}
});
new Timer(TIMER_DELAY, new ActionListener() {
private Toolkit toolkit = Toolkit.getDefaultToolkit();
private int screenWidth = toolkit.getScreenSize().width;
private int screenHeight = toolkit.getScreenSize().height;
#Override
public void actionPerformed(ActionEvent e) {
if (getWidth() >= screenWidth || getHeight() >= screenHeight) {
((Timer)e.getSource()).stop();
} else {
int width = getWidth() + DELTA_SIZE;
int height = getHeight() + DELTA_SIZE;
setPreferredSize(new Dimension(width, height));
Window win = SwingUtilities.getWindowAncestor(Foo2.this);
win.pack();
win.setLocationRelativeTo(null);
}
}
}).start();
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Foo2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new Foo2());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I am new to Java.
I want to create a program, in which i need to draw freely on JPanel by dragging the mouse. I go through the basics of paint function and able to achieve this.
public class DrawLine extends JPanel {
public void paint(Graphics g)
{
g.drawLine(0, 0, 50, 50);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run()
{
JFrame frame=new JFrame("Top Level Demo");
frame.setSize(300, 250);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel myPanel=new JPanel();
myPanel.setLayout(null);
frame.add(myPanel);
frame.add(new DrawLine());
frame.setVisible(true);
}});
}}
But this generates an out put like this where the straight line is determined by coordinates.
Please some one help me to implement free drawing inside JPanel.
Override paintComponent() rather than paint() method of JPanel.
Create list of Point to be used in the paintComponent().
Iterate the list in a loop and For each pair of points from the list call
g.drawLine(currentPoint.x,currentPoint.y, nextPoint.x,nextPoint.y);
Add drag listening to store drag points in the list.
Try to use MouseListener and paintComponent method.
Try next simple example for drawing your line.
public class Test extends JPanel{
public static int xS = 0;
public static int yS = 0;
public static int xF = 0;
public static int yF = 0;
public static void main(String[] args){
JFrame frame = new JFrame("Movement of 2d Shapes");
frame.setSize(400,400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Test t = new Test();
t.setOpaque(true);
t.addMouseListener(getMouseListener(t));
frame.getContentPane().add(t);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static MouseListener getMouseListener(final Test t) {
return new MouseAdapter() {
#Override
public void mousePressed(MouseEvent arg0) {
xS = arg0.getPoint().x;
yS = arg0.getPoint().y;
}
#Override
public void mouseReleased(MouseEvent arg0) {
xF = arg0.getPoint().x;
yF = arg0.getPoint().y;
t.repaint();
}
};
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(xS,yS, xF, yF);
}
}
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class DrawLine extends JFrame implements MouseMotionListener {
int x1,y1,x,y;
private boolean first = true;
public DrawLine() {
super("Top Level Demo");
setSize(300, 250);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setBackground(Color.white);
addMouseMotionListener(this);
}
#Override
public void mouseDragged(MouseEvent e) {
x =x1; y = y1;
x1 = e.getX();
y1 = e.getY();
if(first){
x = x1;
y = y1;
first = false;
}
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
}
public void paint(Graphics graphics){
graphics.drawLine(x, y, x1, y1);
}
public static void main(String[] args) {
new DrawLine();
}
}
Can someone please tell me how i can get the value of int x in my following code:
when i move the square left and right, the output from Test stays as 500.
I am wanting the Thread in Test to get the value of x every time it loops, but x is always 500 (from the thread) even though the value of x is changing in Drawing.java. please help me with this.
package block;
public class Test extends Drawing implements Runnable{
Thread collision = new Thread(this);
public Test() {
collision.start();
}
#Override
public void run() {
while(true) {
System.out.println(x);
}
}
}
package block;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Drawing extends Canvas implements Runnable, KeyListener{
Thread thread = new Thread(this);
int x = 500;
int y = 540;
public Drawing() {
setSize(1000, 800);
addKeyListener(this);
thread.start();
}
public void update(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, 1000, 700);
paint(g);
}
public void paint(Graphics g) {
g.setColor(Color.cyan);
g.fillRect(x, y, 50, 50);
}
#Override
public void run() {
while(true) {
repaint();
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Drawing.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
#Override
public void keyTyped(KeyEvent ke) {
}
#Override
public void keyPressed(KeyEvent ke) {
if(ke.getKeyCode() == KeyEvent.VK_LEFT) {
x -= 5;
}
if(ke.getKeyCode() == KeyEvent.VK_RIGHT) {
x += 5;
}
}
#Override
public void keyReleased(KeyEvent ke) {
}
}
EDIT: --------------------
package block;
import javax.swing.JFrame;
public class Block extends JFrame{
Test test = new Test();
public Block() {
setSize(1000, 700);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
add(new Drawing());
setVisible(true);
createBufferStrategy(2);
}
public static void main(String[] args) {
new Block();
}
}
You are creating multiple instances of x because you are not using inheritance correctly. When you call new Test(), that creates one instance of a Test (which contains a Drawing, which contains a member x), and then the call to new Drawing() creates another instance of a Drawing (which contains a different member x).
To fix this, you could pass a reference to a Drawing object to your Test constructor, and access the x member through the reference.