I need a on-screen virtual joystick solution to control external device - a cart. My program is written on Java/Swing and it works on windows rugged tablet with resistive touch screen. There's no x-y slider control in Swing, failed to find it thru Google, so probably I need to write it from scratch. Now the question: what is best approach to write such control? I was thinking to place two JSlider controls, one horizontal and second vertical, and make custom thumb, but i'm afraid this can bring me troubles because it's actually a hack. Maybe just write it from scratch? Maybe there's an existing solution?
I will use it with thumb dragging and touching final thumb position.
for example
import java.awt.BasicStroke;
import java.awt.BorderLayout;
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.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SimpleJoystickDemo extends JFrame {
//I think that orginally made by #HFOE
private int displayWidth = 340;
private int displayHeight = 550;
private final Point position;
public SimpleJoystickDemo() {
super("SimpleJoystickDemo");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(displayWidth, displayHeight);
setLocationRelativeTo(null);
position = new Point();
SimpleJoystick myJoystick = new SimpleJoystick(150, position, 100);
add(myJoystick, BorderLayout.PAGE_END);
Drawing drawing = new Drawing(position);
add(drawing);
}
public static void main(final String[] args) {
Runnable gui = new Runnable() {
#Override
public void run() {
new SimpleJoystickDemo().setVisible(true);
}
};
SwingUtilities.invokeLater(gui);
}
private class Drawing extends JPanel {
private static final long serialVersionUID = 1L;
private final Point position;
public Drawing(Point position) {
this.position = position;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.RED);
g2.fillOval(160 + position.x, 160 - position.y, 15, 15);
}
}
}
class SimpleJoystick extends JPanel {
private static final long serialVersionUID = 1L;
//Maximum value for full horiz or vert position where centered is 0:
private int joyOutputRange;
private float joySize; //joystick icon size
private float joyWidth, joyHeight;
private float joyCenterX, joyCenterY; //Joystick displayed Center
//Display positions for text feedback values:
private int textHorizPos, textVertPos;
private int fontSpace = 12;
private float curJoyAngle; //Current joystick angle
private float curJoySize; //Current joystick size
private boolean isMouseTracking;
private boolean leftMouseButton;
private int mouseX, mouseY;
private Stroke lineStroke = new BasicStroke(10, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND);
private final Point position;
public SimpleJoystick(final int joyOutputRange,
final Point position, final int joySize) {
this.joyOutputRange = joyOutputRange;
this.position = position;
this.joySize = joySize;
joyWidth = joySize;
joyHeight = joyWidth;
setPreferredSize(new Dimension((int) joyWidth + 250,
(int) joyHeight + 80));
joyCenterX = getPreferredSize().width / 2;
joyCenterY = getPreferredSize().height / 2;
this.joySize = joyWidth / 2;
setBackground(new Color(226, 226, 226));
MouseAdapter mouseAdapter = new MouseAdapter() {
#Override
public void mouseMoved(final MouseEvent e) {
mouseCheck(e);
}
#Override
public void mousePressed(final MouseEvent e) {
leftMouseButton = SwingUtilities.isLeftMouseButton(e);
mouseCheck(e);
}
};
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
}
private void mouseCheck(final MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
float dx = mouseX - joyCenterX;
float dy = mouseY - joyCenterY;
if (leftMouseButton) {
isMouseTracking = true;
} else {
isMouseTracking = false;
}
if (isMouseTracking) {
curJoyAngle = (float) Math.atan2(dy, dx);
curJoySize = (float) Point.distance(mouseX, mouseY,
joyCenterX, joyCenterY);
} else {
curJoySize = 0;
}
if (curJoySize > joySize) {
curJoySize = joySize;
}
position.x = (int) (joyOutputRange * (Math.cos(curJoyAngle)
* curJoySize) / joySize);
position.y = (int) (joyOutputRange * (-(Math.sin(curJoyAngle)
* curJoySize) / joySize));
SwingUtilities.getRoot(SimpleJoystick.this).repaint();
}
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.LIGHT_GRAY);
g2.fillOval((int) (joyCenterX - joyWidth / 2),
(int) (joyCenterY - joyHeight / 2),
(int) joyWidth, (int) joyHeight);
//rotate and draw joystick line segment:
Graphics2D g3 = (Graphics2D) g2.create();
g3.translate(joyCenterX, joyCenterY);
g3.rotate(curJoyAngle);
g3.setColor(Color.GRAY);
g3.setStroke(lineStroke);
g3.drawLine(0, 0, (int) curJoySize, 0);
g3.dispose();
//
g2.setColor(Color.GRAY);
g2.fillOval((int) joyCenterX - 10, (int) joyCenterY - 10, 20, 20);
textHorizPos = 50;
textVertPos = (int) (joyCenterY - 50);
g2.drawString("Horizont:", textHorizPos, textVertPos);
textHorizPos += (4 * fontSpace);
g2.drawString(String.valueOf(position.x), textHorizPos, textVertPos);
textHorizPos = 50;
textVertPos += 12;
g2.drawString("Vertical :", textHorizPos, textVertPos);
textHorizPos += (4 * fontSpace);
g2.drawString(String.valueOf(position.y), textHorizPos, textVertPos);
}
}
Reworked code from #tutejszy. Warnings removed and the missing PointChangeEvent added.
import java.awt.BorderLayout;
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.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class VJoystick extends JPanel implements ChangeListener
{
private JLabel lblPosition;
private static final long serialVersionUID = 1L;
public VJoystick()
{
setLayout(new BorderLayout(0, 0));
SimpleJoystick myJoystick = new SimpleJoystick(150);
myJoystick.setPreferredSize(new Dimension(100,100));
myJoystick.addChangeListener(this);
add(myJoystick, BorderLayout.CENTER);
lblPosition = new JLabel("position");
add(lblPosition, BorderLayout.SOUTH);
}
#Override
public void stateChanged(ChangeEvent ev) {
Point p = null;
try {
p = ((PointChangeEvent)ev).p;
} catch (Exception e) {
return;
}
lblPosition.setText("x="+p.x+" y="+p.y);
}
}
class SimpleJoystick extends JPanel
{
private static final long serialVersionUID = 1L;
/** Maximum value for full horiz or vert position where centered is 0 */
private int joyOutputRange;
/** max x and y value, in pixels */
private int joyRadius;
/** Joystick displayed Center, in pixels */
private int joyCenterX, joyCenterY;
/** joystick output position scaled to given joyOutputRange */
private Point position = new Point();
/** joystick x axis value in pixels */
private int dx = 0;
/** joystick y axis value in pixels */
private int dy = 0;
/**
* #param joyOutputRange
*/
public SimpleJoystick(final int joyOutputRange) {
this.joyOutputRange = joyOutputRange;
setBackground(new Color(226, 226, 226));
MouseAdapter mouseAdapter = new MouseAdapter() {
#Override
public void mousePressed(final MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && cursorChanged(e.getX(), e.getY())) {
SwingUtilities.getRoot(SimpleJoystick.this).repaint();
fireStateChanged();
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && cursorChanged(e.getX(), e.getY())) {
SwingUtilities.getRoot(SimpleJoystick.this).repaint();
fireStateChanged();
}
}
};
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
}
private boolean cursorChanged(int mouseX, int mouseY) {
if (joyRadius == 0) return false;
dx = mouseX - joyCenterX;
dy = mouseY - joyCenterY;
if (dx > joyRadius) dx = joyRadius;
if (dy > joyRadius) dy = joyRadius;
if (dx < -joyRadius) dx = -joyRadius;
if (dy < -joyRadius) dy = -joyRadius;
position.x = joyOutputRange * dx / joyRadius;
position.y = -joyOutputRange * dy / joyRadius;
return true;
}
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
int joyWidth = getSize().width;
int joyHeight = getSize().height;
joyRadius = Math.min(joyWidth, joyHeight) / 2;
if (joyRadius == 0) return;
joyCenterX = joyWidth / 2;
joyCenterY = joyHeight / 2;
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int diameter;
//background
g2.setColor(Color.LIGHT_GRAY);
diameter = joyRadius*2;
g2.fillOval(joyCenterX - diameter/2 , joyCenterY - diameter/2, diameter, diameter);
g2.setColor(Color.RED);
diameter = 40;
g2.fillOval(joyCenterX + dx - diameter/2 , joyCenterY + dy - diameter/2, diameter, diameter);
//thumb
g2.setColor(Color.GRAY);
diameter = 20;
g2.fillOval(joyCenterX - diameter/2 , joyCenterY - diameter/2, diameter, diameter);
}
void addChangeListener(ChangeListener listener) {
listenerList.add(ChangeListener.class, listener);
}
void removeChangeListener(ChangeListener listener) {
listenerList.remove(ChangeListener.class, listener);
}
protected void fireStateChanged() {
ChangeEvent e = new PointChangeEvent(this, position);
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ChangeListener.class) {
((ChangeListener) listeners[i + 1]).stateChanged(e);
}
}
}
}
class PointChangeEvent extends ChangeEvent
{
private static final long serialVersionUID = 1L;
public Point p;
public PointChangeEvent(Object source, Point p)
{
super(source);
this.p=p;
}
}
This is reworked code from #mKorbel, it's event based and more swing-oriented, without floats:
import java.awt.BorderLayout;
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.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
class JoyDemo extends JPanel implements ChangeListener {
private JLabel lblPosition;
public JoyDemo() {
setLayout(new BorderLayout(0, 0));
SimpleJoystick myJoystick = new SimpleJoystick(150);
myJoystick.setPreferredSize(new Dimension(100,100));
myJoystick.addChangeListener(this);
add(myJoystick, BorderLayout.CENTER);
lblPosition = new JLabel("position");
add(lblPosition, BorderLayout.SOUTH);
}
#Override
public void stateChanged(ChangeEvent ev) {
Point p = null;
try {
p = ((PointChangeEvent)ev).p;
} catch (Exception e) {
return;
}
lblPosition.setText("x="+p.x+" y="+p.y);
}
}
class SimpleJoystick extends JPanel {
/** Maximum value for full horiz or vert position where centered is 0 */
private int joyOutputRange;
/** max x and y value, in pixels */
private int joyRadius;
/** Joystick displayed Center, in pixels */
private int joyCenterX, joyCenterY;
/** joystick output position scaled to given joyOutputRange */
private Point position = new Point();
/** joystick x axis value in pixels */
private int dx = 0;
/** joystick y axis value in pixels */
private int dy = 0;
/**
* #param joyOutputRange
*/
public SimpleJoystick(final int joyOutputRange) {
this.joyOutputRange = joyOutputRange;
setBackground(new Color(226, 226, 226));
MouseAdapter mouseAdapter = new MouseAdapter() {
#Override
public void mousePressed(final MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && cursorChanged(e.getX(), e.getY())) {
SwingUtilities.getRoot(SimpleJoystick.this).repaint();
fireStateChanged();
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && cursorChanged(e.getX(), e.getY())) {
SwingUtilities.getRoot(SimpleJoystick.this).repaint();
fireStateChanged();
}
}
};
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
}
private boolean cursorChanged(int mouseX, int mouseY) {
if (joyRadius == 0) return false;
dx = mouseX - joyCenterX;
dy = mouseY - joyCenterY;
if (dx > joyRadius) dx = joyRadius;
if (dy > joyRadius) dy = joyRadius;
if (dx < -joyRadius) dx = -joyRadius;
if (dy < -joyRadius) dy = -joyRadius;
position.x = joyOutputRange * dx / joyRadius;
position.y = -joyOutputRange * dy / joyRadius;
return true;
}
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
int joyWidth = getSize().width;
int joyHeight = getSize().height;
joyRadius = Math.min(joyWidth, joyHeight) / 2;
if (joyRadius == 0) return;
joyCenterX = joyWidth / 2;
joyCenterY = joyHeight / 2;
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int diameter;
//background
g2.setColor(Color.LIGHT_GRAY);
diameter = joyRadius*2;
g2.fillOval(joyCenterX - diameter/2 , joyCenterY - diameter/2, diameter, diameter);
g2.setColor(Color.RED);
diameter = 40;
g2.fillOval(joyCenterX + dx - diameter/2 , joyCenterY + dy - diameter/2, diameter, diameter);
//thumb
g2.setColor(Color.GRAY);
diameter = 20;
g2.fillOval(joyCenterX - diameter/2 , joyCenterY - diameter/2, diameter, diameter);
}
void addChangeListener(ChangeListener listener) {
listenerList.add(ChangeListener.class, listener);
}
void removeChangeListener(ChangeListener listener) {
listenerList.remove(ChangeListener.class, listener);
}
protected void fireStateChanged() {
ChangeEvent e = new PointChangeEvent(this, position);
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ChangeListener.class) {
((ChangeListener) listeners[i + 1]).stateChanged(e);
}
}
}
}
Here's my solution. Note, I reworked #Dark.Rider's answer, who reworked #tutejszy's answer, who reworked #mKorbel's answer...
I made 2 files. One called Joystick.java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;
import javax.swing.SwingUtilities;
public class Joystick extends javax.swing.JPanel {
private final int outputMax;
private final int thumbDiameter;
private final int thumbRadius;
private final int panelWidth;
private final int arrowRadius;
private final int BORDER_THICKNESS = 2;
private final Point thumbPos = new Point();
protected SwingPropertyChangeSupport propertySupporter = new SwingPropertyChangeSupport(this);
/**
* #param output_max The maximum value to scale output to. If this value was
* 5 and the joystick thumb was dragged to the top-left corner, the output
* would be (-5,5)
* #param panel_width how big the JPanel will be. The sizes of the joystick's
* visual components are proportional to this value
*/
public Joystick(int output_max, int panel_width) {
assert output_max > 0;
assert panel_width > 0;
outputMax = output_max;
panelWidth = panel_width;
thumbDiameter = panel_width/4;
thumbRadius = thumbDiameter/2;
arrowRadius = panel_width/24;
MouseAdapter mouseAdapter = new MouseAdapter() {
private void repaintAndTriggerListeners(){
SwingUtilities.getRoot(Joystick.this).repaint();
propertySupporter.firePropertyChange(null, null, getOutputPos());
}
#Override
public void mousePressed(final MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
updateThumbPos(e.getX(), e.getY());
repaintAndTriggerListeners();
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
updateThumbPos(e.getX(), e.getY());
repaintAndTriggerListeners();
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
centerThumbPad();
repaintAndTriggerListeners();
}
}
};
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
setPreferredSize(new java.awt.Dimension(panel_width, panel_width));
setOpaque(false);
centerThumbPad();
}
private void centerThumbPad(){
thumbPos.x = panelWidth/2;
thumbPos.y = panelWidth/2;
}
/**
* update both thumbPos
* #param mouseX the x position of cursor that has clicked in the joystick panel
* #param mouseY the y position of cursor that has clicked in the joystick panel
* #return
*/
private void updateThumbPos(int mouseX, int mouseY) {
// if the cursor is clicked out of bounds, we'll modify the position
// to be the closest point where we can draw the thumb pad completely
if (mouseX < thumbRadius)
mouseX = thumbRadius;
else if(mouseX > panelWidth - thumbRadius)
mouseX = panelWidth - thumbRadius;
if (mouseY < thumbRadius)
mouseY = thumbRadius;
else if(mouseY > panelWidth - thumbRadius)
mouseY = panelWidth - thumbRadius;
thumbPos.x = mouseX;
thumbPos.y = mouseY;
}
/**
* #return the scaled position of the joystick thumb pad
*/
Point getOutputPos(){
Point result = new Point();
result.x = outputMax * (thumbPos.x - panelWidth/2) / (panelWidth/2-thumbDiameter/2);
result.y = -outputMax * (thumbPos.y - panelWidth/2) / (panelWidth/2-thumbDiameter/2);
return result;
}
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
//joystick background border
g.setColor(Color.BLACK);
g.fillOval(thumbRadius, thumbRadius, panelWidth-thumbDiameter, panelWidth-thumbDiameter);
//joystick background color
g.setColor(Color.GRAY);
g.fillOval(thumbRadius+BORDER_THICKNESS, thumbRadius+BORDER_THICKNESS, panelWidth-thumbDiameter-BORDER_THICKNESS*2, panelWidth-thumbDiameter-BORDER_THICKNESS*2);
//joystick background arrows
g.setColor(Color.BLACK);
int[] left_x = {thumbDiameter-arrowRadius,thumbDiameter+arrowRadius,thumbDiameter+arrowRadius};
int[] left_y = {panelWidth/2,panelWidth/2+arrowRadius,panelWidth/2-arrowRadius};
g.fillPolygon(left_x, left_y,3);
int[] right_x = {panelWidth-thumbDiameter+arrowRadius,panelWidth-thumbDiameter-arrowRadius,panelWidth-thumbDiameter-arrowRadius};
int[] right_y = {panelWidth/2,panelWidth/2+arrowRadius,panelWidth/2-arrowRadius};
g.fillPolygon(right_x, right_y,3);
int[] up_x = left_y;
int[] up_y = left_x;
g.fillPolygon(up_x, up_y,3);
int[] down_x = right_y;
int[] down_y = right_x;
g.fillPolygon(down_x, down_y,3);
//thumb pad border
g.setColor(Color.BLACK);
g.fillOval(thumbPos.x - thumbRadius - BORDER_THICKNESS, thumbPos.y - thumbRadius - BORDER_THICKNESS, thumbRadius*2+BORDER_THICKNESS*2, thumbRadius*2+BORDER_THICKNESS*2);
//thumb pad color
g.setColor(Color.GRAY);
g.fillOval(thumbPos.x - thumbRadius, thumbPos.y - thumbRadius, thumbRadius*2, thumbRadius*2);
}
#Override
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertySupporter.addPropertyChangeListener(listener);
}
#Override
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertySupporter.removePropertyChangeListener(listener);
}
}
and one called JoystickOutputContainer.java (used to make example pictures)
import java.awt.BorderLayout;
import java.awt.Point;
import java.beans.PropertyChangeListener;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class JoystickOutputContainer extends JPanel {
private Joystick myJoystick = new Joystick(100, 140);
private JLabel lblPosition = new JLabel("x=0 y=0");
public JoystickOutputContainer() {
setLayout(new BorderLayout(0, 0));
add(myJoystick, BorderLayout.CENTER);
add(lblPosition, BorderLayout.SOUTH);
myJoystick.addPropertyChangeListener(updateConsoleListener);
}
private final PropertyChangeListener updateConsoleListener = (evt) -> {
updateConsoleCallback( (Point) evt.getNewValue() );
};
private void updateConsoleCallback(Point p){
lblPosition.setText("x="+p.x+" y="+p.y);
}
}
Related
In the code below, when I move the wheel of the mouse, the square is zooming in or out and I always detect the square position everywhere in the screen if my mouse go inside it, even if the map is scrolling.
That's really great :)
If I change the boolean "centerScreen" to true, I can zoom and the square is always in the center of the screen.
It is really very great ...
... but the mouse detection is not good :(
How can I modify my code to zoom in or out with the Render still in center of screen but with a perfect mouse detection with the square ?
Thanks for your help !
Here is my full "clean code" -- work with copy paste (in 3 class) --
Main Class
package main;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ZoomDemo extends JPanel
{
private static final long serialVersionUID = 1L;
private Rectangle2D square;
private Mouse mouse;
private ScrollMap scrollMap;
private static int screenWidth;
private static int screenHeight;
private static int centerScreenWidth;
private static int centerScreenHeight;
private static boolean centerScreen;
private List<Point> listStar = new ArrayList<Point>();
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 800);
frame.setLocationRelativeTo(null);
screenWidth = frame.getWidth();
screenHeight = frame.getHeight();
centerScreenWidth = screenWidth / 2;
centerScreenHeight = screenHeight / 2;
centerScreen = false;
ZoomDemo zoomDemo = new ZoomDemo();
frame.add(zoomDemo);
frame.setVisible(true);
}
public ZoomDemo()
{
mouse = new Mouse();
addMouseListener(mouse);
addMouseMotionListener(mouse);
addMouseWheelListener(mouse);
scrollMap = new ScrollMap(mouse, screenWidth, screenHeight);
int centerX = (int) getCenter(0, screenWidth, 50, true);
int centerY = (int) getCenter(0, screenHeight, 50, true);
square = new Rectangle2D.Double(centerX, centerY, 50, 50);
Random rand = new Random();
for (int i = 0; i < 50; i++)
{
int x = rand.nextInt(screenWidth);
int y = rand.nextInt(screenHeight);
listStar.add(new Point(x, y));
}
}
#Override
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D) g.create();
/* Optimization */
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
/* Background */
g2d.setColor(Color.GRAY);
g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
/* Translatation center -- it is working, but mouse detection is not good */
if (centerScreen)
g2d.translate(centerScreenWidth, centerScreenHeight);
/* Listener Wheel Mouse for zooming and translation */
mouse.zoom(g2d);
/* Restore Translatation center */
if (centerScreen)
g2d.translate(-centerScreenWidth, -centerScreenHeight);
render(g2d);
g2d.dispose();
//#formatter:off
try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
//#formatter:on
repaint();
}
public void render(Graphics2D g2d)
{
/* Ask for scroll */
isScrolling(g2d);
/* Mouse Detection */
int xMouse = (int) (mouse.pMousePosition.x - scrollMap.getMapX());
int yMouse = (int) (mouse.pMousePosition.y - scrollMap.getMapY());
boolean detection = square.contains(xMouse, yMouse);
/* Graphic render */
g2d.setColor(Color.WHITE);
for (int i = 0; i < listStar.size(); i++)
g2d.fillRect(listStar.get(i).x, listStar.get(i).y, 4, 4);
g2d.setColor(detection ? Color.red : Color.black);
g2d.fill(square);
/* End of Scroll */
endScrolling(g2d);
}
private void isScrolling(Graphics2D g2d)
{
/* If Mouse on edge, so change value for scrolling */
scrollMap.scrolling();
/* Translation scrolling */
g2d.translate(scrollMap.getMapX(), scrollMap.getMapY());
}
private void endScrolling(Graphics2D g2d)
{
g2d.translate(-scrollMap.getMapX(), -scrollMap.getMapY());
}
public float getCenter(float startXZone, float longueurZone, float longueurElem, boolean round)
{
float s = startXZone + (longueurZone - longueurElem) / 2f;
return (round) ? Math.round(s) : s;
}
}
Mouse Class
package main;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
public class Mouse implements MouseMotionListener, MouseWheelListener, MouseListener
{
public Point pMousePosition = new Point();
public double scaleCoef;
public double inverseScaleCoef;
public boolean clic;
private AffineTransform affineScale;
private AffineTransform inverseScale;
public Mouse()
{
scaleCoef = 1;
inverseScaleCoef = 1;
transform();
}
private void transform()
{
affineScale = AffineTransform.getScaleInstance(scaleCoef, scaleCoef);
//#formatter:off
try { inverseScale = affineScale.createInverse(); } catch (NoninvertibleTransformException e) { }
//#formatter:on
}
public void zoom(Graphics2D g2)
{
transform();
g2.transform(affineScale);
}
#Override
public void mouseWheelMoved(MouseWheelEvent e)
{
if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL)
{
scaleCoef -= (0.1 * e.getWheelRotation());
scaleCoef = Math.max(0.1, scaleCoef);
inverseScaleCoef = 1d / scaleCoef;
}
}
#Override
public void mouseMoved(MouseEvent e)
{
pMousePosition = e.getPoint();
inverseScale.transform(pMousePosition, pMousePosition);
}
#Override
public void mousePressed(MouseEvent e)
{
clic = true;
}
#Override
public void mouseReleased(MouseEvent e)
{
clic = false;
}
#Override
public void mouseClicked(MouseEvent e)
{
}
#Override
public void mouseEntered(MouseEvent e)
{
}
#Override
public void mouseExited(MouseEvent e)
{
}
#Override
public void mouseDragged(MouseEvent e)
{
}
}
(Optional)
Scroll class
package main;
public class ScrollMap
{
private float mapX = 0;
private float mapY = 0;
private int speedScroll = 1;
private int edgeDetection = 70;
private int screenWidth;
private int screenHeight;
private Mouse mouse;
public ScrollMap(Mouse mouse, int screenWidth, int screenHeight)
{
this.mouse = mouse;
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
}
/**
* Scrolling ecran
*/
public void scrolling()
{
double scale = mouse.inverseScaleCoef;
/* Mouse to Right */
if ((mouse.pMousePosition.x > screenWidth * scale - edgeDetection))
{
for (int k = 0; k < speedScroll; k++)
moveMapToRight();
}
/* Mouse to Left */
else if ((mouse.pMousePosition.x < edgeDetection * scale))
{
for (int k = 0; k < speedScroll; k++)
moveMapToLeft();
}
/* Mouse to Down */
if ((mouse.pMousePosition.y < edgeDetection * scale))
{
for (int k = 0; k < speedScroll; k++)
moveMaptoDown();
}
/* Mouse to Up */
else if ((mouse.pMousePosition.y > screenHeight * scale - edgeDetection))
{
for (int k = 0; k < speedScroll; k++)
moveMapToUp();
}
}
public float moveMapToRight()
{
return mapX--;
}
public float moveMapToLeft()
{
return mapX++;
}
public float moveMapToUp()
{
return mapY--;
}
public float moveMaptoDown()
{
return mapY++;
}
public float getMapY()
{
return mapY;
}
public float getMapX()
{
return mapX;
}
}
So I'm new at java and need some help with my breakout game. My JFrame is just blank and i don't know how to fix it?
So I have a ball class, paddle class, canvas class and a brick class as well as a main class. In my canvas class I set all functions the ball, paddle and bricks has etc. In brick class I draw the bricks. And in my main I do the JFrame but it's blank
Main class :
public class Main {
public static void main(String[] args){
JFrame frame = new JFrame();
Canvas c = new Canvas();
frame.add(c);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
I expect the JFrame to show the game instead of just blank window
package breakout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.KeyEvent;
import breakout.Bricks.Type;
public class Canvas extends JPanel implements ActionListener, MouseMotionListener, MouseListener, KeyListener {
/**
*
*/
private static final long serialVersionUID = 1L;
private static final int HEIGHT = 600;
public static final int WIDTH = 720;
private int horizontalCount;
private BufferedImage image;
private Graphics2D bufferedGraphics;
private Timer time;
private static final Font endFont = new Font(Font.SANS_SERIF, Font.BOLD, 20);
private static final Font scoreFont = new Font(Font.SANS_SERIF, Font.BOLD, 15);
private Paddle player;
private Ball ball;
ArrayList<ArrayList<Bricks>> bricks;
public Canvas() {
super();
setPreferredSize( new Dimension(WIDTH, HEIGHT));
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
bufferedGraphics = image.createGraphics();
time = new Timer (15, this);
player = new Paddle((WIDTH/2)-(Paddle.PADDLE_WIDTH/2));
ball = new Ball (((player.getX() + (Paddle.PADDLE_WIDTH / 2 )) - (Ball.DIAMETER / 2)), (Paddle.Y_POS - (Ball.DIAMETER + 10 )), -5, -5);
bricks = new ArrayList<ArrayList<Bricks>>();
horizontalCount = WIDTH / Bricks.BRICK_WIDTH;
for(int i = 0; i < 8; ++i) {
ArrayList<Bricks> temp = new ArrayList<Bricks>();
#SuppressWarnings("unused")
Type rowColor = null;
switch(i) {
case 0 :
case 2:
rowColor = Type.LOW;
break;
case 1 :
case 3 :
case 5 :
rowColor = Type.MEDIUM;
break;
case 4 :
case 6 :
rowColor = Type.HIGH;
break;
case 7 :
default :
rowColor = Type.ULTRA;
break;
}
for(int j = 0; j < horizontalCount; ++j) {
Bricks tempBrick = new Bricks();
temp.add(tempBrick);
}
bricks.add(temp);
addMouseMotionListener(this);
addMouseListener(this);
addKeyListener(this);
requestFocus();
}
}
public void actionPerformed(ActionEvent e) {
checkCollisions();
ball.Move();
for(int i = 0; i < bricks.size(); ++i) {
ArrayList<Bricks> al = bricks.get(i);
for(int j = 0; j < al.size(); ++j) {
Bricks b = al.get(j);
if(b.dead()) {
al.remove(b);
}
}
}
repaint();
}
private void checkCollisions() {
if(player.hitPaddle(ball)) {
ball.setDY(ball.getDY() * -1);
return;
}
if(ball.getX() >= (WIDTH - Ball.DIAMETER) || ball.getX() <= 0) {
ball.setDX(ball.getDX() * -1);
}
if(ball.getY() > (Paddle.Y_POS + Paddle.PADDLE_HEIGHT + 10)) {
resetBall();
}
if(ball.getY() <= 0) {
ball.setDY(ball.getDY() * -1);
}
int brickRowActive = 0;
for(ArrayList<Bricks> alb : bricks) {
if(alb.size() == horizontalCount) {
++brickRowActive;
}
}
for(int i = (brickRowActive==0) ? 0 : (brickRowActive - 1); i < bricks.size(); ++i) {
for(Bricks b : bricks.get(i)) {
if(b.hitBy(ball)) {
player.setScore(player.getScore() + b.getBrickType().getPoints());
b.decrementType();
}
}
}
}
private void resetBall() {
if(gameOver()) {
time.stop();
return;
}
ball.setX(WIDTH/2);
ball.setDY((HEIGHT/2) + 80);
player.setLives(player.getLives() -1);
player.setScore(player.getScore() <= 1);
}
private boolean gameOver() {
if(player.getLives() <= 1) {
return true;
}
return false;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
bufferedGraphics.clearRect(0, 0, WIDTH, HEIGHT);
player.drawPaddle(bufferedGraphics);
player.drawBall(bufferedGraphics);
for(ArrayList<Bricks> row : bricks) {
for(Bricks b : row) {
b.drawBrick(bufferedGraphics);
}
}
bufferedGraphics.setFont(scoreFont);
bufferedGraphics.drawString("Score: " + player.getScore(), 10, 25);
if(gameOver() && ball.getY() >= HEIGHT) {
bufferedGraphics.setColor(Color.black);
bufferedGraphics.setFont(endFont);
bufferedGraphics.drawString("Game Over Score: " + player.getScore(), (WIDTH /2) -85, (HEIGHT/2));
}
if(empty()) {
bufferedGraphics.setColor(Color.black);
bufferedGraphics.setFont(endFont);
bufferedGraphics.drawString("You won. Score: " + player.getScore(), (WIDTH /2) -85, (HEIGHT /2));
time.stop();
}
g.drawImage(image, 0, 0, this);
Toolkit.getDefaultToolkit().sync();
}
private boolean empty() {
for(ArrayList<Bricks> al : bricks) {
if(al.size() != 0) {
return false;
}
}
return true;
}
#Override
public void mouseMoved(MouseEvent e) {
player.setX(e.getX() - (Paddle.PADDLE_WIDTH / 2));
}
#Override
public void mouseClicked(MouseEvent e) {
if(time.isRunning()) {
return;
}
time.start();
}
#Override
public void mouseDragged(MouseEvent e) { }
#Override
public void mouseEntered(MouseEvent arg0) {}
#Override
public void mouseExited(MouseEvent arg0) {}
#Override
public void mousePressed(MouseEvent arg0) {}
#Override
public void mouseReleased(MouseEvent arg0) {}
#Override
public void keyPressed(KeyEvent arg0) {}
#Override
public void keyReleased(KeyEvent arg0) {}
#Override
public void keyTyped(KeyEvent arg0) {}
}
Preparing an MCVE, as required in SO, not only it makes helping much easier.
In many case, while preparing one, you are likely to find the problem, so it is a good debugging tool.
To answer "why is my JFrame blank ?" you could create the minimal code example like the following (copy-paste the entire code into GameBoard.java and run):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameBoard extends JPanel {
static final int HEIGHT = 600, WIDTH = 720, BRICK_ROWS = 8;
private final int horizontalCount;
private static final Font scoreFont = new Font(Font.SANS_SERIF, Font.BOLD, 15);
private final Paddle player;
private final Ball ball;
ArrayList<ArrayList<Brick>> bricks;
public GameBoard() {
super();
setPreferredSize( new Dimension(WIDTH, HEIGHT));
player = new Paddle(WIDTH/2-Paddle.PADDLE_WIDTH/2);
ball = new Ball (player.getX() + Paddle.PADDLE_WIDTH / 2 - Ball.DIAMETER / 2,
Paddle.Y_POS - (Ball.DIAMETER + 10 ));
bricks = new ArrayList<>();
horizontalCount = WIDTH / Brick.BRICK_WIDTH;
for(int i = 0; i < BRICK_ROWS; ++i) {
ArrayList<Brick> temp = new ArrayList<>();
for(int j = 0; j < horizontalCount; ++j) {
Brick tempBrick = new Brick(j*Brick.BRICK_WIDTH , Brick.BRICK_YPOS + i*Brick.BRICK_HEIGHT);
temp.add(tempBrick);
}
bricks.add(temp);
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D)g;
g2D.clearRect(0, 0, WIDTH, HEIGHT);
player.drawPaddle(g2D);
ball.drawBall(g2D);
for(ArrayList<Brick> row : bricks) {
for(Brick b : row) {
b.drawBrick(g2D);
}
}
g2D.setFont(scoreFont);
g2D.drawString("Score: " + player.getScore(), 10, 25);
}
}
class Paddle{
public final static int PADDLE_WIDTH = 100, PADDLE_HEIGHT= 30, Y_POS = GameBoard.HEIGHT - 2* PADDLE_HEIGHT;
private int xPos, score;
Paddle(int xPos) {
this.xPos = xPos;
}
void setX(int xPos) {this.xPos = xPos;}
int getX() {return xPos;}
String getScore() {
return String.valueOf(score);
}
void drawPaddle(Graphics2D g2D) {
g2D.setColor(Color.GREEN);
g2D.fillRect(xPos, Y_POS, PADDLE_WIDTH, PADDLE_HEIGHT);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(400,250);
frame.add(new GameBoard());
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
}
}
class Brick{
final static int BRICK_WIDTH = 80, BRICK_HEIGHT = 15, BRICK_YPOS = 50;
int xPos, yPos;
Brick(int xPos, int yPos) {
this.xPos = xPos;
this.yPos = yPos;
}
void drawBrick(Graphics2D g2D) {
g2D.setColor(Color.RED);
g2D.fillRect(xPos, yPos, BRICK_WIDTH, BRICK_HEIGHT);
g2D.setColor(Color.BLACK);
g2D.drawRect(xPos, yPos, BRICK_WIDTH, BRICK_HEIGHT);
}
}
class Ball{
final static int DIAMETER = 40;
int xPos, yPos;
Ball(int xPos, int yPos) {
this.xPos = xPos;
this.yPos = yPos;
}
void drawBall(Graphics2D g2D) {
g2D.setColor(Color.BLUE);
g2D.fillOval(xPos, yPos, DIAMETER, DIAMETER);
}
}
This produces the following result, which I believe can serve as the basis of what you wanted to achieve:
Now start adding the missing functionality and see what breaks it.
I'm having issues drawing some circles to my JFrame. I originally had it using the default layout and realized this was only adding the most recent circle, so I changed the layout to null, and now nothing gets drawn. I've also tried frame.setLayout(new FlowLayout()) which also doesn't draw anything. Any help would be appreciated!
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
/**
* #author Christopher Nielson
*
*/
public class Main {
private static JFrame frame;
private static Random rand;
private static Jiggler jiggler;
private static ArrayList<JComponent> circles;
private static int fps;
public static void main(String[] args) {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
frame.setBounds(100, 100, 450, 450);
rand = new Random();
circles = new ArrayList<JComponent>();
int x = frame.getWidth();
int y = frame.getHeight();
for (int i = 0; i < Integer.parseInt(args[0]); i++) {
circles.add(new Circle(rand.nextInt(frame.getWidth()), rand.nextInt(frame.getHeight()),
rand.nextInt(frame.getWidth() / 10) + 100, rand.nextInt(frame.getHeight() / 10) + 100, null));
}
circles.forEach(current -> {
frame.add(current);
});
frame.setVisible(true);
jiggler = new Jiggler(circles, new JLabel("FPS: ")); // TODO add fps
jiggler.run();
}
}
And this is one reason you'll see us recommending time and time again to avoid using null layouts like the plague.
Having said that, your main problem is a design problem, not a layout problem, and that problem being that your Circle class shouldn't extend JComponent or any component for that matter, since if you want to draw multiple circles, you should have only one component, probably a JPanel doing the drawing, and the Circles should be logical classes, classes that have a public void draw(Graphics g) method, not component classes. You would pass the List of Circles to your drawing JPanel, and it would draw the Circles in its paintComponent method by calling the draw(g) methods of each Circle in the list.
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.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawChit extends JPanel {
private static final int PREF_W = 900;
private static final int PREF_H = 700;
private static final int MAX_SHAPES = 30;
private List<MyShape> shapes = new ArrayList<>();
public DrawChit() {
setBackground(Color.WHITE);
for (int i = 0; i < MAX_SHAPES; i++) {
double x = (PREF_W - 100) * Math.random();
double y = (PREF_H - 100) * Math.random();
double w = 100 + (Math.random() * PREF_W) / 10;
double h = 100 + (Math.random() * PREF_H) / 10;
Ellipse2D ellipse = new Ellipse2D.Double(x, y, w, h);
float hue = (float) Math.random();
double delta = 0.3;
float saturation = (float) (Math.random() * delta + (1 - delta));
float brightness = (float) (Math.random() * delta + (1 - delta));
Color color = Color.getHSBColor(hue, saturation, brightness);
shapes.add(new MyShape(ellipse, color));
}
// we'll throw a black square in the middle!
int rectW = 200;
int rectX = (PREF_W - rectW) / 2;
int rectY = (PREF_H - rectW) / 2;
shapes.add(new MyShape(new Rectangle(rectX, rectY, rectW, rectW), Color.BLACK));
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// use anti-aliasing to make graphics smooth
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// iterate through the shapes list, filling all
for (MyShape shape : shapes) {
shape.fill(g2);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class MyMouse extends MouseAdapter {
private Point p0 = null;
private MyShape shape = null;
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
// iterate *backwards* so get top-most Shape
for (int i = shapes.size() - 1; i >= 0; i--) {
if (shapes.get(i).contains(e.getPoint())) {
p0 = e.getPoint();
shape = shapes.get(i);
// move selected shape to the top!
shapes.remove(shape);
shapes.add(shape);
repaint();
return;
}
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (p0 != null) {
moveShape(e.getPoint());
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (p0 != null) {
moveShape(e.getPoint());
p0 = null;
shape = null;
}
}
// translates the shape
private void moveShape(Point p1) {
int deltaX = p1.x - p0.x;
int deltaY = p1.y - p0.y;
shape.translate(deltaX, deltaY);
p0 = p1;
repaint();
}
}
private static void createAndShowGui() {
DrawChit mainPanel = new DrawChit();
JFrame frame = new JFrame("Draw Chit");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class MyShape {
private Path2D path = new Path2D.Double();
private Color color;
public MyShape(Shape shape, Color color) {
path.append(shape, true);
this.color = color;
}
public boolean contains(Point p) {
return path.contains(p);
}
public void draw(Graphics2D g2) {
g2.setColor(color);
g2.draw(path);
}
public void fill(Graphics2D g2) {
g2.setColor(color);
g2.fill(path);
}
public void translate(int deltaX, int deltaY) {
path.transform(AffineTransform.getTranslateInstance(deltaX, deltaY));
}
}
I am creating a moving ball program that features a ball moving and bouncing off the walls of a rectangle with go and stop buttons at the bottom.
What I am having problems with is I want the ball to start off moving when the program is run and bouncing off the lines inside the rectangle which is my main problem. Below is my code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
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.Timer;
//Does the drawing
class MyDrawing extends JPanel {
private int xpos;
private int ypos;
public void setXPos(final int x) {
this.xpos = x;
}
public void setYPos(final int y) {
this.ypos = y;
}
public int getXpos() {
return xpos;
}
public int getYpos() {
return ypos;
}
#Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g;
g.setColor(Color.red);
final Ellipse2D.Double circle = new Ellipse2D.Double(xpos, ypos, 50, 50);
g2.draw(circle);
g2.fill(circle);
final Rectangle box1 = new Rectangle(10, 10, 380, 300);
g.setColor(Color.BLACK);
g2.draw(box1);
}
}
public class ControlledBall extends JFrame {
private final JButton flash = new JButton("Go");
private final JButton steady = new JButton("Stop");
private final JPanel panel = new JPanel(new GridBagLayout());
private final MyDrawing drawing = new MyDrawing();
private final Timer timer;
//direction
private int dx = 3;
private int dy = 2;
public ControlledBall() {
panel.add(flash);
panel.add(steady);
this.add(panel, BorderLayout.SOUTH);
this.add(drawing, BorderLayout.CENTER);
drawing.setXPos(300);
drawing.setYPos(150);
steady.addActionListener(new SteadyListener());
final MoveListener ml = new MoveListener();
flash.addActionListener(ml);
timer = new Timer(15, ml);
}
class MoveListener implements ActionListener {
#Override
public void actionPerformed(final ActionEvent event) {
if (!timer.isRunning()){
timer.start();
}
move();
}
}
class SteadyListener implements ActionListener {
#Override
public void actionPerformed(final ActionEvent event) {
if (timer.isRunning()){
timer.stop();
}
}
}
private void move() {
int x = drawing.getXpos();
int y = drawing.getYpos();
final int dia = 30;
if (x + dx < 0 || x + dia + dx > getWidth()) {
dx *= -1;
}
if (y + dy < 0 || y + dia + dy > getHeight()) {
dy *= -1;
}
x += dx;
y += dy;
drawing.setXPos(x);
drawing.setYPos(y);
repaint();
}
public static void main(final String[] args) {
final JFrame window = new ControlledBall();
window.setSize(400, 400);
window.setTitle("Controlled Ball");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
}
The problem is the boundaries you check against. If you want to check against the Rectangle you have to implement the size as variables and enter them in your "bounce"-check or you add it manually like (take the size of the rectangle from your code):
if (x + dx < 10 || x + dia + dx > 380) {
dx *= -1;
}
if (y + dy < 10 || y + dia + dy > 300) {
dy *= -1;
}
If you like to take the real distanz use an offset of the size of hallf of the ball and add it to this code. i think this is better than the speed-vector dxand dyonly.
I'm working on a game project. The aim is clicking the balls and drop them into the basket which is below in the JPanel. I created some ways to do that but I can't achieve it. In my opinion, when the user click the ball's center with a margin of error (because the program can't catch the real point of balls), the program understands the action and runs the function of this issue. After clicking the ball it should be dropped down straight but the other balls should be continued. I use MouseListener#mousePressed method, but it doesn't work or I'm missing some parts. In addition, I made some changes in my source code, but I want to listen your advices so I am writing this topic.
I wrote a method which finds the mouse and ball coordinates. So when I make some changes to it, I can achieve my project goal. You can see the editing in the picture.
This is my source code ;
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.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Game {
public Game() {
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("MultipleBallApp");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BallControl());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BallControl extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private BallPanel ballPanel = new BallPanel();
private JButton Suspend = new JButton("Suspend");
private JButton Resume = new JButton("Resume");
private JButton Add = new JButton("+1");
private JButton Subtract = new JButton("-1");
private JScrollBar Delay = new JScrollBar();
public BallControl() {
// Group buttons in a panel
JPanel panel = new JPanel();
panel.add(Suspend);
panel.add(Resume);
panel.add(Add);
panel.add(Subtract);
// Add ball and buttons to the panel
ballPanel.setBorder(new javax.swing.border.LineBorder(Color.red));
Delay.setOrientation(JScrollBar.HORIZONTAL);
ballPanel.setDelay(Delay.getMaximum());
setLayout(new BorderLayout());
add(Delay, BorderLayout.NORTH);
add(ballPanel, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
this.addMouseListener(new MouseListener() {
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent clickEvent) {
// TODO Auto-generated method stub
System.out.println("X coordinate =" + clickEvent.getX());
System.out.println("Y coordinate = " + clickEvent.getY());
double radius1;
int x = 0;
double y = 0;
int radius = 15;
double xM = clickEvent.getX();
double yM = clickEvent.getY();
radius1 = Math.sqrt((xM - x) * (xM - x) + (yM - y)
* (yM - y));
System.out.println("Radius1 =" + radius1);
// ballPanel.list.get(0).setD
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseClicked(MouseEvent arg0) {
// TODO Auto-generated method stub
}
});
// Register listeners
Suspend.addActionListener(new Listener());
Resume.addActionListener(new Listener());
Add.addActionListener(new Listener());
Subtract.addActionListener(new Listener());
Delay.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
ballPanel.setDelay(Delay.getMaximum() - e.getValue());
}
});
}
class Listener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == Suspend) {
ballPanel.suspend();
} else if (e.getSource() == Resume) {
ballPanel.resume();
} else if (e.getSource() == Add) {
ballPanel.add();
} else if (e.getSource() == Subtract) {
ballPanel.subtract();
}
}
}
}
class BallPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private int delay = 30;
public ArrayList<AnimatedShape> list = new ArrayList<AnimatedShape>();
private AnimatedRectange rectangle;
public BallPanel() {
this.rectangle = new AnimatedRectange(-25, 373, 50, 25, Color.BLACK);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
// Create a timer with the initial delay
protected Timer timer = new Timer(delay, new ActionListener() {
/**
* Handle the action event
*/
#Override
public void actionPerformed(ActionEvent e) {
for (AnimatedShape ball : list) {
ball.update(getBounds());
}
rectangle.update(getBounds());
repaint();
}
});
public void add() {
int radius = 15;
// Randomised position
int x = (int) (Math.random() * (getWidth() - (radius * 2)))
+ radius;
int y = (int) (Math.random() * (getHeight() - (radius * 2)))
+ radius;
Color color = new Color((int) (Math.random() * 256),
(int) (Math.random() * 256), (int) (Math.random() * 256));
AnimatedBall ball = new AnimatedBall(x, y, radius, color);
list.add(ball);
}
// public void formula(MouseEvent clickEvent) {
// double radius1;
// int x = 0;
// double y = 0;
// int radius = 15;
// double xM = clickEvent.getX();
// double yM = clickEvent.getY();
// radius1 = Math.sqrt((xM - x) * (xM - x) + (yM - y) * (yM - y));
// System.out.println("Radius1 =" + radius1);
// }
public void subtract() {
if (list.size() > 0) {
list.remove(list.size() - 1); // Remove the last ball
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (AnimatedShape ball : list) {
ball.paint(this, g2d);
}
rectangle.paint(this, g2d);
}
public void suspend() {
timer.stop();
}
public void resume() {
timer.start();
}
public void setDelay(int delay) {
this.delay = delay;
timer.setDelay(delay);
}
}
public interface AnimatedShape {
public void update(Rectangle bounds);
public void paint(JComponent parent, Graphics2D g2d);
}
public abstract class AbstractAnimatedShape implements AnimatedShape {
private Rectangle bounds;
private int dx, dy;
public AbstractAnimatedShape() {
}
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
public Rectangle getBounds() {
return bounds;
}
public int getDx() {
return dx;
}
public int getDy() {
return dy;
}
public void setDx(int dx) {
this.dx = dx;
}
public void setDy(int dy) {
this.dy = dy;
}
#Override
public void update(Rectangle parentBounds) {// ball
Rectangle bounds = getBounds();
int dx = getDx();
int dy = getDy();
bounds.x += dx;
bounds.y += dy;
if (bounds.x < parentBounds.x) {
bounds.x = parentBounds.x;
setDx(dx *= -1);
} else if (bounds.x + bounds.width > parentBounds.x
+ parentBounds.width) {
bounds.x = parentBounds.x + (parentBounds.width - bounds.width);
setDx(dx *= -1);
}
if (bounds.y < parentBounds.y) {
bounds.y = parentBounds.y;
setDy(dy *= -1);
} else if (bounds.y + bounds.height > parentBounds.y
+ parentBounds.height) {
bounds.y = parentBounds.y
+ (parentBounds.height - bounds.height);
setDy(dy *= -1);
}
}
}
public class AnimatedBall extends AbstractAnimatedShape {
private Color color;
public AnimatedBall(int x, int y, int radius, Color color) {
setBounds(new Rectangle(x, y / 2, radius * 2, radius * 2));
this.color = color;
setDx(Math.random() > 0.5 ? 2 : -2);
// setDy(Math.random() > 0.5 ? 2 : -2);
}
public Color getColor() {
return color;
}
#Override
public void paint(JComponent parent, Graphics2D g2d) {
Rectangle bounds = getBounds();
g2d.setColor(getColor());
g2d.fillOval(bounds.x, bounds.y, bounds.width, bounds.height);
}
}
public class AnimatedRectange extends AbstractAnimatedShape {
private Color color;
public AnimatedRectange(int x, int y, int width, int height, Color color) {
setBounds(new Rectangle(x, y, width, height));
this.color = color;
setDx(2);
}
// Don't want to adjust the vertical speed
#Override
public void setDy(int dy) {
}
#Override
public void paint(JComponent parent, Graphics2D g2d) {
Rectangle bounds = getBounds();
g2d.setColor(color);
g2d.fill(bounds);
}
}
/**
* Main method
*/
public static void main(String[] args) {
new Game();
}
}
At the end of your mousePressed method, you can go though all AnimatedShape objects, check whether they are an AnimatedBall. When the object is a ball, you can test whether the mouse click hits the ball. The mouse click hits the ball when the distance between the center of the ball and the mouse position is smaller than the ball radius. When the ball is hit, you can set its horizontal speed to 0, and the vertical speed to 5 or so.
for (AnimatedShape as : ballPanel.list)
{
if (as instanceof AnimatedBall)
{
AnimatedBall ball = (AnimatedBall)as;
Rectangle b = ball.getBounds();
int ballCenterX = b.x + b.width / 2;
int ballCenterY = b.y + b.height / 2;
Point p = new Point(ballCenterX, ballCenterY);
double d = p.distance(clickEvent.getPoint());
if (d < radius)
{
ball.setDx(0);
ball.setDy(5);
}
}
}
Note
In order to make this work properly, you have to attach this listener to the ballPanel. Originally, you had the line
this.addMouseListener(new MouseListener() {
in your code. You have to change this to
ballPanel.addMouseListener(new MouseListener() {
Otherwise, the mouse coordinates will refer to the wrong component!
Concerning the question from the comment
how can I stop and disapper clickedball's when they crash the bound
You may probably insert method to check this, and call this method in the actionPerformed method of your timer. Further explainations are probably beyond the scope of an anser on a Q&A site. Stackoverflow is not a homework-solution-generator.