I am working on a project, develop a game called
Don't get mad bro.
I have a JPanel with shapes (circles) draw on it and JLabel components that contains images.I need that whenever I click "Throw dice" (which in background return a number between 1 and 6) I should wait for current player to click on one of his pawns, and that pawn should move after n positions, where n is equal with number that dice returned.
My question is, should I create a new thread in which I wait for mouseClick event? And how to get coordinates of mouseClick?
Here is my class that inherits panel and draw circles and add labels.
public class ImagePanel extends JPanel{
private static final long serialVersionUID = 1L;
ImageMatrix imageMatrix;
BufferedImage[] images;
public static JLabel[][] labels;
DrawGameBoard board = new DrawGameBoard();
List<GameFigure> gameCircles;
List<FinishFigure> finishCircles;
int initialHeight = 528;
int initialWidth = 596;
ThreadForPawnsClick labelsClick;
public ImagePanel(){
labels = new JLabel[4][4];
images = new BufferedImage[4];
setBackground(new Color(255,255,153));
gameCircles = new ArrayList<GameFigure>();
finishCircles = new ArrayList<FinishFigure>();
imageMatrix = new ImageMatrix(initialWidth,initialHeight);
try {
images[0] = ImageIO.read(new File("C:\\Users\\babii\\.eclipse\\DontGetMad\\resource\\red.png"));
images[1] = ImageIO.read(new File("C:\\Users\\babii\\.eclipse\\DontGetMad\\resource\\green.png"));
images[2] = ImageIO.read(new File("C:\\Users\\babii\\.eclipse\\DontGetMad\\resource\\blue.png"));
images[3] = ImageIO.read(new File("C:\\Users\\babii\\.eclipse\\DontGetMad\\resource\\yellow.png"));
} catch (IOException e) {
e.printStackTrace();
}
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
labels[i][j] = new JLabel(new ImageIcon(images[i]));
}
setLayout(null);
board.DrawHomeBoard(imageMatrix, labels);
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
add(labels[i][j]);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
int width = this.getWidth();
int height = this.getHeight();
imageMatrix.update(width, height);
setLayout(null);
gameCircles = board.DrawMainBoard(g, imageMatrix);
//labels = board.DrawHomeBoard(g, imageMatrix, labels);
//board.DrawHomeBoard(imageMatrix, labels);
finishCircles = board.DrawFinishBoard(g, imageMatrix);
/*for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
add(labels[i][j]);
*/
}
}
Also, why my imageMatrix doesn't extend on whole screen, even if I call update matrix in paintComponent()?
My question is, should I create a new thread in which I wait for mouseClick event?
No, absolutely not. You need instead to change the state of the GUI somehow to wait-for-mouse-click mode, and then alter the behavior of the GUI's response to mouse clicks depending on its state. Usually state is represented by instance fields of the class. So when you need to wait, you change one of these state fields, and on mouse click, you check the state of the field and vary what happens depending on that. For instance in a turn based game of chess, one state field could be private boolean blackTurn, and then base what mouse does based on its state.
And how to get coordinates of mouseClick?
In the MouseListener the MouseEvent parameter gives you the x and y position of the mouse relative to the listened to component and to the screen. If your MouseListener is attached to the JLabels, then you can get a reference to the clicked JLabel via the MouseEvent's getSource() method, and then can get the location of the JLabel relative to its container JPanel (if needed) by calling getLocation() on it.
Side note: in a Swing GUI where you're moving around sprites, it's usually better not to put the sprites into JLabels but rather to simply draw them directly in the paintComponent method of a drawing JPanel.
As an example of what I mean, here is a program that draws 4 colored circles, circles that are draggable, but only draggable when the corresponding JRadioButton has been selected with the JRadioButton setting the "state" of the GUI. Here the state is represented by an enum called ColorState that holds 4 colors and corresponding text. Here is this enum:
import java.awt.Color;
public enum ColorState {
RED("Red", Color.RED),
GREEN("Green", Color.GREEN),
BLUE("Blue", Color.BLUE),
ORANGE("Orange", Color.ORANGE);
private String text;
private Color color;
private ColorState(String text, Color color) {
this.text = text;
this.color = color;
}
public String getText() {
return text;
}
public Color getColor() {
return color;
}
}
Then we create a drawing JPanel, one that holds four Ellipse2D Shape objects in a Map,
private Map<ColorState, Shape> colorStateMap = new EnumMap<>(ColorState.class);
Within a for loop, we create the JRadioButtons, give them ActionListeners that set the object's state, and populate the Map with Ellipse2D Shape objects
for (final ColorState state : ColorState.values()) {
// create the JRadioButton
JRadioButton radioButton = new JRadioButton(state.getText());
add(radioButton); // add to GUI
buttonGroup.add(radioButton); // add to ButtonGroup
// give it an ActionListener that changes the object's state
radioButton.addActionListener(e -> {
colorState = state;
});
// create a randomly placed Ellipse2D and place into Map:
double x = Math.random() * (W - CIRCLE_WIDTH);
double y = Math.random() * (H - CIRCLE_WIDTH);
Ellipse2D ellipse = new Ellipse2D.Double(x, y, CIRCLE_WIDTH, CIRCLE_WIDTH);
colorStateMap.put(state, ellipse);
}
We draw the ellipses within paintComponent:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// make for smooth graphics
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// iterate through the enum, extracting the ellipse and drawing it
for (ColorState state : ColorState.values()) {
Shape shape = colorStateMap.get(state);
if (shape != null) {
g2.setColor(state.getColor());
g2.fill(shape); // draw the ellipse
}
}
}
Finally in a MouseAdapter (both MouseListener and MouseMotionListener), we listen for mouse presses and register success if the left mouse is clicked and if it is clicked within the appropriate Shape:
private class MyMouse extends MouseAdapter {
private Shape selectedShape = null;
private Point2D offset = null;
#Override
public void mousePressed(MouseEvent e) {
// check that correct button pressed
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
// has our colorState been set yet? If not, exit
if (colorState == null) {
return;
}
// is an appropriate Shape held by the Map? If so, get it
Shape shape = colorStateMap.get(colorState);
if (shape == null) {
return;
}
// does this shape contain the point where the mouse was pressed?
if (!shape.contains(e.getPoint())) {
return;
}
// Get the selected shape, get the mouse point location relative to this shape
selectedShape = shape;
double x = e.getX() - shape.getBounds2D().getX();
double y = e.getY() - shape.getBounds2D().getY();
offset = new Point2D.Double(x, y);
}
#Override
public void mouseDragged(MouseEvent e) {
// drag shape to new location
if (selectedShape != null) {
double x = e.getX() - offset.getX();
double y = e.getY() - offset.getY();
Rectangle2D bounds = selectedShape.getBounds2D();
bounds.setFrame(new Rectangle2D.Double(x, y, bounds.getWidth(), bounds.getHeight()));
((Ellipse2D) selectedShape).setFrame(bounds);
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e) {
selectedShape = null;
}
}
Note that the mouse dragging code is thanks to MadProgrammer's answer here. Please up-vote this answer.
The entire class looks like so:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.EnumMap;
import java.util.Map;
import javax.swing.*;
#SuppressWarnings("serial")
public class StateDependentMouseListener extends JPanel {
private static final int W = 800;
private static final int H = 650;
private static final double CIRCLE_WIDTH = 60.0;
private ButtonGroup buttonGroup = new ButtonGroup();
private ColorState colorState = null;
private Map<ColorState, Shape> colorStateMap = new EnumMap<>(ColorState.class);
public StateDependentMouseListener() {
setPreferredSize(new Dimension(W, H));
for (final ColorState state : ColorState.values()) {
// create the JRadioButton
JRadioButton radioButton = new JRadioButton(state.getText());
add(radioButton); // add to GUI
buttonGroup.add(radioButton); // add to ButtonGroup
// give it an ActionListener that changes the object's state
radioButton.addActionListener(e -> {
colorState = state;
});
// create a randomly placed Ellipse2D and place into Map:
double x = Math.random() * (W - CIRCLE_WIDTH);
double y = Math.random() * (H - CIRCLE_WIDTH);
Ellipse2D ellipse = new Ellipse2D.Double(x, y, CIRCLE_WIDTH, CIRCLE_WIDTH);
colorStateMap.put(state, ellipse);
}
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// make for smooth graphics
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// iterate through the enum, extracting the ellipse and drawing it
for (ColorState state : ColorState.values()) {
Shape shape = colorStateMap.get(state);
if (shape != null) {
g2.setColor(state.getColor());
g2.fill(shape); // draw the ellipse
}
}
}
private class MyMouse extends MouseAdapter {
private Shape selectedShape = null;
private Point2D offset = null;
#Override
public void mousePressed(MouseEvent e) {
// check that correct button pressed
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
// has our colorState been set yet? If not, exit
if (colorState == null) {
return;
}
// is an appropriate Shape held by the Map? If so, get it
Shape shape = colorStateMap.get(colorState);
if (shape == null) {
return;
}
// does this shape contain the point where the mouse was pressed?
if (!shape.contains(e.getPoint())) {
return;
}
// Get the selected shape, get the mouse point location relative to this shape
selectedShape = shape;
double x = e.getX() - shape.getBounds2D().getX();
double y = e.getY() - shape.getBounds2D().getY();
offset = new Point2D.Double(x, y);
}
#Override
public void mouseDragged(MouseEvent e) {
// drag shape to new location
if (selectedShape != null) {
double x = e.getX() - offset.getX();
double y = e.getY() - offset.getY();
Rectangle2D bounds = selectedShape.getBounds2D();
bounds.setFrame(new Rectangle2D.Double(x, y, bounds.getWidth(), bounds.getHeight()));
((Ellipse2D) selectedShape).setFrame(bounds);
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e) {
selectedShape = null;
}
}
private static void createAndShowGui() {
StateDependentMouseListener mainPanel = new StateDependentMouseListener();
JFrame frame = new JFrame("StateDependentMouseListener");
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());
}
}
How can I implement Marquee effect in Java Swing
Here's an example using javax.swing.Timer.
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
/** #see http://stackoverflow.com/questions/3617326 */
public class MarqueeTest {
private void display() {
JFrame f = new JFrame("MarqueeTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String s = "Tomorrow, and tomorrow, and tomorrow, "
+ "creeps in this petty pace from day to day, "
+ "to the last syllable of recorded time; ... "
+ "It is a tale told by an idiot, full of "
+ "sound and fury signifying nothing.";
MarqueePanel mp = new MarqueePanel(s, 32);
f.add(mp);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
mp.start();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new MarqueeTest().display();
}
});
}
}
/** Side-scroll n characters of s. */
class MarqueePanel extends JPanel implements ActionListener {
private static final int RATE = 12;
private final Timer timer = new Timer(1000 / RATE, this);
private final JLabel label = new JLabel();
private final String s;
private final int n;
private int index;
public MarqueePanel(String s, int n) {
if (s == null || n < 1) {
throw new IllegalArgumentException("Null string or n < 1");
}
StringBuilder sb = new StringBuilder(n);
for (int i = 0; i < n; i++) {
sb.append(' ');
}
this.s = sb + s + sb;
this.n = n;
label.setFont(new Font("Serif", Font.ITALIC, 36));
label.setText(sb.toString());
this.add(label);
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
#Override
public void actionPerformed(ActionEvent e) {
index++;
if (index > s.length() - n) {
index = 0;
}
label.setText(s.substring(index, index + n));
}
}
I know this is a late answer, but I just saw another question about a marquee that was closed because it was considered a duplicate of this answer.
So I thought I'd add my suggestion which takes a approach different from the other answers suggested here.
The MarqueePanel scrolls components on a panel not just text. So this allows you to take full advantage of any Swing component. A simple marquee can be used by adding a JLabel with text. A fancier marquee might use a JLabel with HTML so you can use different fonts and color for the text. You can even add a second component with an image.
Basic answer is you draw your text / graphic into a bitmap and then implement a component that paints the bitmap offset by some amount. Usually marquees / tickers scroll left so the offset increases which means the bitmap is painted at -offset. Your component runs a timer that fires periodically, incrementing the offset and invalidating itself so it repaints.
Things like wrapping are a little more complex to deal with but fairly straightforward. If the offset exceeds the bitmap width you reset it back to 0. If the offset + component width > bitmap width you paint the remainder of the component starting from the beginning of the bitmap.
The key to a decent ticker is to make the scrolling as smooth and as flicker free as possible. Therefore it may be necessary to consider double buffering the result, first painting the scrolling bit into a bitmap and then rendering that in one go rather than painting straight into the screen.
Here is some code that I threw together to get you started. I normally would take the ActionListener code and put that in some sort of MarqueeController class to keep this logic separate from the panel, but that's a different question about organizing the MVC architecture, and in a simple enough class like this it may not be so important.
There are also various animation libraries that would help you do this, but I don't normally like to include libraries into projects only to solve one problem like this.
public class MarqueePanel extends JPanel {
private JLabel textLabel;
private int panelLocation;
private ActionListener taskPerformer;
private boolean isRunning = false;
public static final int FRAMES_PER_SECOND = 24;
public static final int MOVEMENT_PER_FRAME = 5;
/**
* Class constructor creates a marquee panel.
*/
public MarqueePanel() {
this.setLayout(null);
this.textLabel = new JLabel("Scrolling Text Here");
this.panelLocation = 0;
this.taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
MarqueePanel.this.tickAnimation();
}
}
}
/**
* Starts the animation.
*/
public void start() {
this.isRunning = true;
this.tickAnimation();
}
/**
* Stops the animation.
*/
public void stop() {
this.isRunning = false;
}
/**
* Moves the label one frame to the left. If it's out of display range, move it back
* to the right, out of display range.
*/
private void tickAnimation() {
this.panelLocation -= MarqueePanel.MOVEMENT_PER_FRAME;
if (this.panelLocation < this.textLabel.getWidth())
this.panelLocaton = this.getWidth();
this.textLabel.setLocation(this.panelLocation, 0);
this.repaint();
if (this.isRunning) {
Timer t = new Timer(1000 / MarqueePanel.FRAMES_PER_SECOND, this.taskPerformer);
t.setRepeats(false);
t.start();
}
}
}
Add a JLabel to your frame or panel.
ScrollText s= new ScrollText("ello Everyone.");
jLabel3.add(s);
public class ScrollText extends JComponent {
private BufferedImage image;
private Dimension imageSize;
private volatile int currOffset;
private Thread internalThread;
private volatile boolean noStopRequested;
public ScrollText(String text) {
currOffset = 0;
buildImage(text);
setMinimumSize(imageSize);
setPreferredSize(imageSize);
setMaximumSize(imageSize);
setSize(imageSize);
noStopRequested = true;
Runnable r = new Runnable() {
public void run() {
try {
runWork();
} catch (Exception x) {
x.printStackTrace();
}
}
};
internalThread = new Thread(r, "ScrollText");
internalThread.start();
}
private void buildImage(String text) {
RenderingHints renderHints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
renderHints.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
BufferedImage scratchImage = new BufferedImage(1, 1,
BufferedImage.TYPE_INT_RGB);
Graphics2D scratchG2 = scratchImage.createGraphics();
scratchG2.setRenderingHints(renderHints);
Font font = new Font("Serif", Font.BOLD | Font.ITALIC, 24);
FontRenderContext frc = scratchG2.getFontRenderContext();
TextLayout tl = new TextLayout(text, font, frc);
Rectangle2D textBounds = tl.getBounds();
int textWidth = (int) Math.ceil(textBounds.getWidth());
int textHeight = (int) Math.ceil(textBounds.getHeight());
int horizontalPad = 600;
int verticalPad = 10;
imageSize = new Dimension(textWidth + horizontalPad, textHeight
+ verticalPad);
image = new BufferedImage(imageSize.width, imageSize.height,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = image.createGraphics();
g2.setRenderingHints(renderHints);
int baselineOffset = (verticalPad / 2) - ((int) textBounds.getY());
g2.setColor(Color.BLACK);
g2.fillRect(0, 0, imageSize.width, imageSize.height);
g2.setColor(Color.GREEN);
tl.draw(g2, 0, baselineOffset);
// Free-up resources right away, but keep "image" for
// animation.
scratchG2.dispose();
scratchImage.flush();
g2.dispose();
}
public void paint(Graphics g) {
// Make sure to clip the edges, regardless of curr size
g.setClip(0, 0, imageSize.width, imageSize.height);
int localOffset = currOffset; // in case it changes
g.drawImage(image, -localOffset, 0, this);
g.drawImage(image, imageSize.width - localOffset, 0, this);
// draw outline
g.setColor(Color.black);
g.drawRect(0, 0, imageSize.width - 1, imageSize.height - 1);
}
private void runWork() {
while (noStopRequested) {
try {
Thread.sleep(10); // 10 frames per second
// adjust the scroll position
currOffset = (currOffset + 1) % imageSize.width;
// signal the event thread to call paint()
repaint();
} catch (InterruptedException x) {
Thread.currentThread().interrupt();
}
}
}
public void stopRequest() {
noStopRequested = false;
internalThread.interrupt();
}
public boolean isAlive() {
return internalThread.isAlive();
}
}
This is supposed to be an improvement of #camickr MarqueePanel. Please see above.
To map mouse events to the specific components added to MarqueePanel
Override add(Component comp) of MarqueePanel in order to direct all mouse events of the components
An issue here is what do do with the MouseEvents fired from the individual components.
My approach is to remove the mouse listeners form the components added and let the MarqueePanel redirect the event to the correct component.
In my case these components are supposed to be links.
#Override
public Component add(Component comp) {
comp = super.add(comp);
if(comp instanceof MouseListener)
comp.removeMouseListener((MouseListener)comp);
comp.addMouseListener(this);
return comp;
}
Then map the component x to a MarqueePanel x and finally the correct component
#Override
public void mouseClicked(MouseEvent e)
{
Component source = (Component)e.getSource();
int x = source.getX() + e.getX();
int y = source.getY();
MarqueePanel2 marqueePanel = (MarqueePanel2) ((JComponent)e.getSource()).getParent();
double x2 = marqueePanel.getWidth();
double x1 = Math.abs(marqueePanel.scrollOffset);
if(x >= x1 && x <= x2)
{
System.out.println("Bang " + x1);
Component componentAt = getComponentAt(x+marqueePanel.scrollOffset, y);
if(comp instanceof MouseListener)
((MouseListener) componentAt).mouseClicked(e);
System.out.println(componentAt.getName());
}
else
{
return;
}
//System.out.println(x);
}
This is not a duplicate. All other solutions I tried were outdated.
So first look at this Image
I made that in eclipse today in Java.
It looks like a Visual Novel.
The point is I want to draw some text on the screen but don't know how to.
At first I only want to know:
How to draw text on screen and change it
Make something, such as an image or some text, clickable to move to
the next scene
Here is my current code:
package textboxes;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.URL;
public class test extends Applet implements Runnable, KeyListener {
private Image Image, Background;
private Image actor1, actor2;
private Image textbox;
private Graphics graphics;
private URL base;
private static testbg bg;
#Override
public void init(){
setSize(960, 540);
setBackground(Color.LIGHT_GRAY);
setFocusable(true);
Frame frame = (Frame)this.getParent().getParent();
frame.setTitle("School Scene");
try{
base = getDocumentBase();
}catch(Exception e){};
//getImages from disk
Background = getImage(base, "res/background.jpg");
actor1 = getImage(base, "res/actor1.jpg");
actor2 = getImage(base, "res/actor2.jpg");
textbox = getImage(base, "res/textbox.jpg");
}
public test(){
}
#Override
public void start(){
bg = new testbg();
Thread thread = new Thread(this);
thread.start();
}
#Override
public void keyPressed(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void run() {
bg.update();
repaint();
try{
Thread.sleep(17);
}catch(InterruptedException e){
e.printStackTrace();
}
}
#Override
public void update(Graphics g){
if(Image == null){
Image = createImage(this.getWidth(), this.getHeight());
graphics = Image.getGraphics();
}
graphics.setColor(getBackground());
graphics.fillRect(0, 0, getWidth(), getHeight());
graphics.setColor(getForeground());
paint(graphics);
g.drawImage(Image, 0, 0, this);
}
#Override
public void paint(Graphics g){
super.paint(g);
g.drawImage(Background, bg.getBgX(), bg.getBgY(), this);
g.drawImage(actor2, 40, 20, this);
g.drawImage(textbox, 80, 350, this);
}
public static testbg getBg() {
return bg;
}
}
This piece of code above is what I call test.java
if you are wondering about the Background part
the following piece of code is what I call testbg.java
package textboxes;
public class testbg {
private int bgX, bgY;
public testbg(){
bgX = 0;
bgY = 0;
}
public void update(){
}
public int getBgX(){
return bgX;
}
public int getBgY(){
return bgY;
}
public void setBgX(int bgX) {
this.bgX = bgX;
}
public void setBgY(int bgY) {
this.bgY = bgY;
}
}
Thanks for reading this much till the end...Now so can I know how to do it ??
As for the text, I have two solutions, but for what you want (and for what I know of CG games), I guess the first is the best.
This first solution is one that I found a long time ago for a problem of mine in StackOverflow (I don't remember where, sorry), in which includes de use of the several classes together to draw directly in the panel.
private final String message;
private final java.awt.geom.Rectangle2D.Float aboutMessageBounds;
private final AttributedString aboutMessageAttributedString;
private final AttributedCharacterIterator paragraph;
// The LineBreakMeasurer used to line-break the paragraph.
private java.awt.font.LineBreakMeasurer lineMeasurer;
// index of the first character in the paragraph.
private final int paragraphStart;
// index of the first character after the end of the paragraph.
private final int paragraphEnd;
#Override
public void init(){
(...)
java.util.Hashtable<TextAttribute, Object> textAtributMap =
new java.util.Hashtable<TextAttribute, Object>();
textAtributMap.put(TextAttribute.FAMILY, "Serif");
textAtributMap.put(TextAttribute.SIZE, new Float(26.0));
textAtributMap.put(TextAttribute.JUSTIFICATION, TextAttribute.JUSTIFICATION_FULL );
textAtributMap.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_DEMIBOLD );
textAtributMap.put(TextAttribute.LIGATURES, TextAttribute.LIGATURES_ON );
message = "This is a sample of a message.";
aboutMessageAttributedString = new AttributedString( aboutMessage, textAtributMap );
paragraph = aboutMessageAttributedString.getIterator();
paragraphStart = paragraph.getBeginIndex();
paragraphEnd = paragraph.getEndIndex();
(...)
}
#Override
protected void paintComponent( Graphics g ) {
super.paintComponent( g ); //To change body of generated methods, choose Tools | Templates.
Graphics2D g2 = (Graphics2D)g.create();
try {
g2.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
// Create a new LineBreakMeasurer from the paragraph.
// It will be cached and re-used.
if (lineMeasurer == null) {
FontRenderContext frc = g2.getFontRenderContext();
lineMeasurer = new java.awt.font.LineBreakMeasurer(paragraph, frc);
}
//You can scale it like I did. this part is not part of the code that I found.
g2.scale( ratio.scaleDx, ratio.scaleDy );
// Set break width to width of Component.
//these were the measures I used for a something in a game;
float breakWidth = 734.0f;
float drawPosY = 90.0f;
float posX0 = 30.0f;
// Set position to the index of the first character in the paragraph.
lineMeasurer.setPosition(paragraphStart);
// Get lines until the entire paragraph has been displayed.
while (lineMeasurer.getPosition() < paragraphEnd) {
int next = lineMeasurer.nextOffset(breakWidth);
int limit = next;
if (limit <= message.length()) {
for (int i = lineMeasurer.getPosition(); i < next; ++i) {
char c = aboutMessage.charAt(i);
if (c == '\n') {
limit = i + 1;
break;
}
}
}
java.awt.font.TextLayout layout = lineMeasurer.nextLayout( breakWidth, limit, false );
// Retrieve next layout. A cleverer program would also cache
// these layouts until the component is re-sized.
// Compute pen x position. If the paragraph is right-to-left we
// will align the TextLayouts to the right edge of the panel.
// Note: this won't occur for the English text in this sample.
// Note: drawPosX is always where the LEFT of the text is placed.
float drawPosX = layout.isLeftToRight()
? posX0 : breakWidth - layout.getAdvance();
// Move y-coordinate by the ascent of the layout.
drawPosY += layout.getAscent();
// Draw the TextLayout at (drawPosX, drawPosY).
layout.draw(g2, drawPosX, drawPosY);
// Move y-coordinate in preparation for next layout.
drawPosY += layout.getDescent() + layout.getLeading();
}
}
finally {
g2.dispose();
}
}
As for the second solution, you could use a JEditorPane or a JTextPane. See the Oracle tutorial for this mater:
https://docs.oracle.com/javase/tutorial/uiswing/components/editorpane.html
I hop I have helped.
Have a nice day. :)
edit 2: I decided that it would be easier to understand if I just put the entire code up, so that you can test it.
edit: I realize that what I said was unclear, so I will explain this as best as I can. Basically, I am drawing rectangles on a Graphics page using the fillRect method. The problem is that when I change the size of one, they all change, as they are all being redrawn everytime a new one is drawn. To correct this, I added an array that stores all of the sizes which are input via the scrollwheel in another part of the problem. Anyways, I know that the problem is isolated to the loop that supposedly draws them all a certain size, so I added a loop that in theory should give me a temporary variable each time to use that redraws all of the rectangle's sizes starting at 0 each time the main loop is run. The problem is that this does not in fact redraw the rectangles to their individual sizes, and instead draws them to the current size. I have updated the code part as well.
I am having trouble with a project in Java. What it is supposed to do is change the size of each individual rectangle object by storing it in an array, and then recreating the rectangles based off the length from the array. I (at least I think) do this by creating a variable that should be equal to the SIZE that is changed in another part of the program, and then set that equal to the particular element in the array at i. Anyhow, when I do this, I change all of the lengths to whatever the current length is when I draw a rectangle. I know that the problem is by me using i in the size part, but what would I use? Thanks in advance for any help!
Here is the code:
public class Dots
{
public static void main(String[] args)
{
JFrame frame = new JFrame("Array Rectangles");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DotsPanel dotsPanel = new DotsPanel();
frame.getContentPane().add(dotsPanel);
//buttons
JButton btnNewButton = new JButton("RED");
btnNewButton.setHorizontalAlignment(SwingConstants.LEFT);
btnNewButton.setVerticalAlignment(SwingConstants.BOTTOM);
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
}
});
btnNewButton.setForeground(Color.RED);
dotsPanel.add(btnNewButton);
JButton btnNewButton_1 = new JButton("GREEN");
btnNewButton_1.setForeground(Color.GREEN);
btnNewButton_1.setVerticalAlignment(SwingConstants.BOTTOM);
dotsPanel.add(btnNewButton_1);
JButton btnNewButton_2 = new JButton("BLUE");
btnNewButton_2.setForeground(Color.BLUE);
dotsPanel.add(btnNewButton_2);
JButton btnNewButton_3 = new JButton("BLACK");
btnNewButton_3.setForeground(new Color(0, 0, 0));
dotsPanel.add(btnNewButton_3);
frame.pack();
frame.setVisible(true);
}
}
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JPanel;
import java.awt.*;
import java.awt.event.*;
public class DotsPanel extends JPanel
{
// radius of each dot
private int SIZE = 25;
private int SIZEAccess;
private static final Random generator = new Random();
//used to count amount of dots
private ArrayList<Point> pointList;
int[] sizes = new int [10000];
//Sets up this std. sized panel to listen for mouse events.
public DotsPanel()
{
pointList = new ArrayList<Point>();
addMouseListener (new DotsListener());
addMouseMotionListener(new DotsListener());
addMouseWheelListener(new DotsListener());
setBackground(Color.white);
setPreferredSize(new Dimension(1024, 768));
}
//used to generate a random color
public static Color randomColor() {
return new Color(generator.nextInt(256), generator.nextInt(256), generator.nextInt(256));
}
// Draws all of the dots stored in the list.
public void paintComponent(Graphics page)
{
super.paintComponent(page);
//draws a centered dot of random color
int i = 0;
for (Point spot : pointList)
{
sizes[i] = SIZE;
//SIZEAccess = SIZE;
//sizes[i] = SIZEAccess;
//page.fillRect(spot.x-SIZE, spot.y-SIZE, SIZE*2, SIZE*2);
for (int temp = 0; temp <= i; temp++)
page.fillRect(spot.x-sizes[temp], spot.y-sizes[temp], sizes[temp]*2, sizes[temp]*2);
//page.fillRect(spot.x-SIZE, spot.y-SIZE, SIZE*2, SIZE*2);
//page.setColor(randomColor());
//page.setColor(c)
i++;
}
//displays the amount of rectangles drawn at the top left of screen
page.drawString("Count: " + pointList.size(), 5, 15);
page.drawString("To change the size of the squares, use mouse scroll wheel.", 350, 15);
page.drawString("Size: " + SIZE, 950, 15);
}
// Represents the listener for mouse events.
private class DotsListener implements MouseListener, MouseMotionListener, MouseWheelListener
{
// Adds the current point to the list of points and redraws
// the panel whenever the mouse button is pressed.
public void mousePressed(MouseEvent event)
{
pointList.add(event.getPoint());
repaint();
}
// Provide empty definitions for unused event methods.
public void mouseClicked(MouseEvent event) {
}
public void mouseReleased(MouseEvent event) {
}
public void mouseEntered(MouseEvent event) {
}
public void mouseExited(MouseEvent event) {}
// Adds the current point to the list of points and redraws
// the panel whenever the mouse button is dragged.
public void mouseDragged(MouseEvent event) {
pointList.add(event.getPoint());
repaint();
}
public void mouseMoved(MouseEvent event) {
}
public void mouseWheelMoved(MouseWheelEvent event)
{
int notches = 0;
notches = event.getWheelRotation();
//int
if (notches > 0)
{
SIZE = SIZE + notches;
notches = 0;
}
else if (notches < 0)
{
int tempSIZE = SIZE;
tempSIZE = tempSIZE + notches;
//prevents the program from having dots that increase due to multiplying negatives by negatives
//by making anything less than 1 equal 1
if(tempSIZE < 1)
tempSIZE = 1;
SIZE = tempSIZE;
notches = 0;
}
}
}
//SIZE = SIZE + notches;
}
You appear to have ArrayList's interacting with arrays in a confusing mix that makes it hard for us to follow your logic. This suggests that your logic may be too complex for your own good and that your code might benefit from simplification. Why not instead create a List<Rectangle> such as an ArrayList<Rectangle>, and then simply loop through this list in your paintComponent method, and draw each Rectangle using the Graphics2D object's draw(...) or fill(...) method:
private List<Rectangle> rectangleList = new ArrayList<>();
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
for (Rectangle rectangle : rectangleList) {
g2.fill(rectangle);
}
}
For example:
import java.awt.BasicStroke;
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.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class Foo extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final Color BACKGROUND = Color.black;
private static final Color FILL_COLOR = Color.pink;
private static final Color DRAW_COLOR = Color.red;
private static final Stroke STROKE = new BasicStroke(3);
private List<Rectangle> rectangleList = new ArrayList<>();
private Point pressPoint = null;
private Point dragPoint = null;
public Foo() {
setBackground(BACKGROUND);
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Stroke oldStroke = g2.getStroke();
g2.setStroke(STROKE);
for (Rectangle rectangle : rectangleList) {
g2.setColor(FILL_COLOR);
g2.fill(rectangle);
g2.setColor(DRAW_COLOR);
g2.draw(rectangle);
}
g2.setStroke(oldStroke);
if (pressPoint != null && dragPoint != null) {
g2.setColor(FILL_COLOR.darker());
int x = Math.min(pressPoint.x, dragPoint.x);
int y = Math.min(pressPoint.y, dragPoint.y);
int width = Math.abs(pressPoint.x - dragPoint.x);
int height = Math.abs(pressPoint.y - dragPoint.y);
g2.drawRect(x, y, width, height);
}
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
pressPoint = e.getPoint();
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
dragPoint = e.getPoint();
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
dragPoint = e.getPoint();
int x = Math.min(pressPoint.x, dragPoint.x);
int y = Math.min(pressPoint.y, dragPoint.y);
int width = Math.abs(pressPoint.x - dragPoint.x);
int height = Math.abs(pressPoint.y - dragPoint.y);
Rectangle rect = new Rectangle(x, y, width, height);
rectangleList.add(rect);
pressPoint = null;
dragPoint = null;
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new Foo());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I'm developing a Java Game. I'm stuck at a point,where I need to restart the whole game again after GameOver. Here is the skeleton of my program:
package projectflappy;
import java.awt.*;
public final class TheGame extends JFrame implements MouseListener{
JPanel jp;
//declaration of the varibles
int x_width = 500;
int y_height = 500;
int count = 5 ;
Ellipse2D Ball;
int x_ball;
int y_ball;
int cord_xup1,cord_xdown1;
int cord_xup2,cord_xdown2;
int cord_xup3,cord_xdown3;
int cord_xup4,cord_xdown4;
int cord_xup5,cord_xdown5;
Boolean flag = true;
RoundRectangle2D up1,down1,up2,down2,up3,down3,up4,down4;
Font font = new Font("Matura MT Script Capitals",Font.ROMAN_BASELINE,40);
Font font1 = new Font("Matura MT Script Capitals",Font.ROMAN_BASELINE,20);
Font font3 = new Font("Matura MT Script Capitals",Font.ROMAN_BASELINE,20);
float das[] = {10.0f};
BasicStroke color = new BasicStroke(10,BasicStroke.CAP_ROUND,BasicStroke.JOIN_BEVEL,20.0f,das,0.0f);
GradientPaint gp2 = new GradientPaint(20, 0,
Color.DARK_GRAY, 0, 10, Color.GRAY, true);
GradientPaint gp3 = new GradientPaint(30, 0,
Color.BLACK, 0, 20, Color.GREEN, true);
Toolkit kit = Toolkit.getDefaultToolkit();
//Getting the "background.jpg" image we have in the folder
Image background = kit.getImage("D:\\College\\Programs\\ProjectFLAPPY\\src\\projectflappy\\1.png");
JLabel a = new JLabel("Get Ready ! Click to Start.");
JLabel retry = new JLabel(new ImageIcon("D:\\College\\Programs\\ProjectFLAPPY\\src\\projectflappy\\unnamed.png"));
int score = 0;
//constructor
public TheGame() throws IOException
{
super("Simple Drawing");
setSize(x_width, y_height);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
jp = new DrawingPanel();
add(jp);
addMouseListener(this);
}
ActionListener action = new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
update();
repaint();
}
};
Timer t = new Timer(50,action);
public void init()
{
x_ball = 30;
y_ball = 200;
cord_xup1 = 175; cord_xdown1 = 175;
cord_xup2 = 320; cord_xdown2 = 320;
cord_xup3 = 460; cord_xdown3 = 460;
cord_xup4 = 585; cord_xdown4 = 585;
cord_xup5 = 700; cord_xdown5 = 700;
retry.setVisible(false);
retry.setBounds(175,260,46,46);
a.setForeground(Color.YELLOW);
a.setFont(font1);
a.setVisible(true);
a.setBounds(105,200,300,100);
}
#Override
public void mouseClicked(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
a.setVisible(false);
if( flag == false)
{
t.stop();
}
else
{
t.start();
}
y_ball = y_ball - 40;
count--;
}
#Override
public void mousePressed(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void mouseReleased(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void mouseEntered(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void mouseExited(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
// for drawing on the panel
class DrawingPanel extends JPanel{
private static final long serialVersionUID = 1L;
public DrawingPanel() {
setPreferredSize(new Dimension(300, 300));
setLayout(null);
init();
add(a);
add(retry);
// addMouseListener(this);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D d = (Graphics2D)g;
d.drawImage(background, -270,-30, this);
Ball = new Ellipse2D.Double(x_ball,y_ball,30,30);
d.setColor(Color.green);
d.setFont(font3);
up1 = new RoundRectangle2D.Double(cord_xup1,-5,30,175,20,20);
down1 = new RoundRectangle2D.Double(cord_xdown1,310,30,155,20,20);
up2 = new RoundRectangle2D.Double(cord_xup2,-5,30,200,20,20);
down2 = new RoundRectangle2D.Double(cord_xdown2,310,30,175,20,20);
up3 = new RoundRectangle2D.Double(cord_xup3,-5,30,230,20,20);
down3 = new RoundRectangle2D.Double(cord_xdown3,350,30,135,20,20);
up4 = new RoundRectangle2D.Double(cord_xup4,-5,30,115,20,20);
down4 = new RoundRectangle2D.Double(cord_xdown4,240,30,115,20,20);
d.setPaint(gp2);
d.setStroke(color);
d.fill(up1);
d.fill(down1);
d.fill(up2);
d.fill(down2);
d.fill(up3);
d.fill(down3);
d.fill(up4);
d.fill(down4);
d.setPaint(gp3);
d.setStroke(color);
d.fill(Ball);
d.setColor(Color.BLACK);
d.setFont(font1);
d.drawString(""+score ,200,50);
if( Ball.intersects(up1.getBounds()) || Ball.intersects(down1.getBounds()) || Ball.intersects(up2.getBounds()) || Ball.intersects(down2.getBounds()) || Ball.intersects(up3.getBounds()) || Ball.intersects(down3.getBounds()) || Ball.intersects(up4.getBounds()) || Ball.intersects(down4.getBounds()))
{
t.stop();
flag = false;
d.setColor(Color.red);
d.setFont(font);
d.drawString("Game Over : "+score ,100,250);
retry.setVisible(true);
}
retry.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent event) {
init(); //reset properties
}
//...
#Override
public void mousePressed(MouseEvent e) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void mouseReleased(MouseEvent e) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void mouseEntered(MouseEvent e) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void mouseExited(MouseEvent e) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
});
}
}
public void update()
{
cord_xdown1 -= 5;
cord_xup1 -= 5;
cord_xdown2 -= 5;
cord_xup2 -= 5;
cord_xdown3 -= 5;
cord_xup3 -= 5;
cord_xdown4 -= 5;
cord_xup4 -= 5;
cord_xdown5 -= 5;
cord_xup5 -= 5;
if( cord_xup1 <=-20)
{
cord_xup1 = 500;
cord_xdown1 = 500;
}
if( cord_xup2 <=-20)
{
cord_xup2 = 500;
cord_xdown2 = 500;
}
if( cord_xup3 <=-20)
{
cord_xup3 = 500;
cord_xdown3 = 500;
}
if( cord_xup4 <=-20)
{
cord_xup4 = 500;
cord_xdown4 = 500;
}
if( cord_xup5 <=-20)
{
cord_xup5 = 500;
cord_xdown5 = 500;
}
if(count >= 0)
{
y_ball = y_ball - 7;
count--;
if( y_ball == y_height)
{
t.stop();
}
}
else
{
y_ball = y_ball + 7;
if( y_ball == y_height-70)
{
t.stop();
}
}
if(cord_xdown1 == x_ball || cord_xdown2 == x_ball || cord_xdown3 == x_ball || cord_xdown4 == x_ball)
{
score = score+1;
}
}
public static void main(String[] args) throws IOException {
new TheGame();
}
}
here retry is a JLabel in which I'm using a MouseListener to do things.
When I run,the JPanel gets completely removed from the JFrame but the new JPanel really doesn't seem to work. But only one component i.e, a.setVisble(true) works.
This is the frame when the Players gets out.
This Frames when the Player clicks on the retry button.
The reason your new panel is not showing is due to the component hierarchy being invalid. You attempt to revalidate, but you did it before adding the panel. You need to do it AFTER you add a component to an already visible container. Check out invalidate():
This method is called automatically when any layout-related information changes (e.g. setting the bounds of the component, or adding the component to a container).
So you must validate after adding the component, not before. revalidate() invalidates then revalidates the component hierarchy.
The proper way to handle this would be to revert your game back to it's original form; just change everything back to how it was. No need to create a new panel.
You could create a method, init(), which sets your game to how it should be:
//Contains the properties that will change during gameplay
void init() {
retry.setVisible(false);
a.setForeground(Color.YELLOW);
//...
}
Which you can then call when you create the board (in the constructor) and when you press retry (in the listener):
public DrawingPanel() {
setPreferredSize(new Dimension(300, 300));
setPreferredSize(new Dimension(300, 300));
setLayout(null);
init(); //sets properties
a.setFont(font1);
a.setVisible(true);
a.setBounds(105,200,300,100);
add(a);
retry.setBounds(175,260,46,46);
retry.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent event) {
init(); //reset properties
}
//...
});
add(retry);
}
You shouldn't add a listener to a component in your update() method, since update() will be called multiple times. Add it in your constructor.
If retry is a JButton, you should use an ActionListener. I wasn't sure, so I kept it as a mouse listener.
You should avoid using null layout (absolute positioning). Layout Managers position and size components using specific calculations to ensure your resulting GUI looks the same on all platforms. There are a few uses where absolute positioning is a viable option, as mentioned in the tutorials, but it's always best to prefer a Layout Manager. IMO, null layout is bad practice, and the only reason one would use it is if they didn't understand layout managers, which is a problem in itself.
To learn more about layout managers, check out the Visual Guide to Layout Managers trail. Not only does the JDK come bundled with layouts, but you can also create your own or use a third party layout, like MigLayout
EDIT:
Post Swing code to the Event Dispatch Thread. Swing event handlers (painting, listeners) are executed on the Event Dispatch Thread. To ensure the Swing code you write is in sync with the EDT, post any Swing code that isn't already being executed on the EDT to the EDT by using invokeLater or invokeAndWait.
Do not size your frame directly. Allow your frame to size based off the contents inside of it. Your DrawingPanel (the game canvas) should determine the size of the frame.
TheGame should not extend JFrame, since it's not a frame itself, rather than something contained within a frame. Having it extend JPanel would be a little easier on you (you won't be forced to create a new class to override the paint method). Although, TheGame shouldn't extend anything, it should HAVE these things (has-a relationship, not is-a). But since you're still a beginner, I don't wanna overwhelm you with a completely new design, so I considered TheGame to be the actual game canvas (where things will be draw; TheGame will extend JPanel), so you'll no longer need DrawingBoard.
As mentioned before, you should NOT add listeners (or do any task that is only needed once) in the paint method. Keep in mind that the paint method is for painting, not initializing or setting values. You should attempt to keep logic out of that method if possible.
Stay consistent. You use a JLabel for "Click to start!", yet you use drawString for "Game Over". Pick one or the other. This choice is really up to you. For this example, I chose to use drawString, since it's consistent with the rest of your rendering methods (how you paint the background, ball and obstacles)
DO NOT CREATE NEW OBJECTS IN YOUR PAINT METHOD. You're creating a ton of new objects every 50 milliseconds. This is NOT needed, and will harm performance critically. When you use the new keyword, you create a new object. Instead of creating a new object to change it (or revert it back), just change it's state.
Take advantage of Object Orientation. It'll help keep you organized, and allow you to easily manage and scale up your application. Don't shove a bunch of variables into one class to represent a ton of different things (cordx_up1, cordx_up2... it's definitely not scalable).
Look into some of the Adapter classes like MouseAdapter and KeyAdapter; they allow you to handle events without needing to declare methods you might not use.
Use access modifiers. If you aren't familiar with them, get to know them. It makes managing code a lot easier if you know where it can be used ahead of time.
Your paths point to a specific drive with a specific name. This should not be the case, since not everyone uses that drive and/or folder name. Package your images with your project, then refer to them locally.
With that said, you have a lot of studying to do.
What I did was create a Ball class and an Obstacle class, to get a little more organized. ball_x and ball_y are now inside the Ball class, as well as the gradient for it. The objects we create from this class will now have these properties (states). I create 1 ball object for your game.
Instead of creating new variables for each pole (cordx_up1), the Obstacle class has 2 RoundRectangle2D, top and bottom, which are the poles your ball is supposed to avoid. Each obstacle also has a gradient, which is used for both top and bottom. Now I can create 1 obstacle object for 2 aligned poles. You can change the starting x position of the obstacle (although I don't recommend allowing this; x should be set dynamically based on other obstacles' positions), as well as the size for top and bottom. I create 5 obstacle objects.
To keep your game labels organized (by color, message, location, font) while using drawString instead of JLabel, I created a GameLabel class.
I separated the main method into it's own class, named Launcher, which creates a JFrame and adds your game to it; all on the Event Dispatch Thread:
public class Launcher {
public static void main(String[] args) throws IOException {
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.add(new TheGame());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
});
}
}
Your Game class now extends JPanel, so you can override the paint method to render the game. I created 1 Ball and a LinkedList for your obstacles. I chose a LinkedList since inserting/removing from front/end is guaranteed constant time, meaning it'll take the same amount of time to remove/insert no matter how many obstacles are in the list. When the ball passes an obstacle, I remove it from the front of the list and add it to the back. The first obstacle in the list is always the next obstacle.
I saw how some Strings were being re-used, so I created final variables for them, which you can easily change. There's also the currentlyPlaying and isAlive booleans. currentlyPlaying is set to true when the user first clicks (to start the game), and set to false once the user has clicked to restart the game (after he lost).
readyToJump is the flag I use to forward mouse events to your update() method (technically your updatePlayerPostion() method, but it's still "centeralized" within your update() method). It's best to keep all your logic in one place. readyToJump = true would have been the only statement in your listener's method if you weren't relying on calling timer.start() in it. Since update() can't be called unless the timer has started, and mouseEvent starts the timer, we must still handle starting the game in your listener's method.
#SuppressWarnings("serial")
public final class TheGame extends JPanel implements MouseListener, ActionListener {
public static final int WIDTH = 500, HEIGHT = 500;
private final String START_SCORE = "0",
START_MESSAGE = "Get Ready ! Click to Start.",
BACKGROUND_URL = "/res/flappy.png";
private boolean currentlyPlaying, readyToJump, isAlive = true;
private int score;
private Timer timer;
private Image background;
private GameLabel messageLabel, scoreLabel;
private Collection<Obstacle> obstaclesInOrder;
private LinkedList<Obstacle> obstacles;
private Ball ball;
public TheGame() {
setPreferredSize(new Dimension(WIDTH, HEIGHT));
addMouseListener(this);
timer = new Timer(50, this);
background = loadBackgroundImage();
messageLabel = new GameLabel(START_MESSAGE, 150, 240);
scoreLabel = new GameLabel(START_SCORE, 250, 60);
obstacles = new LinkedList<>();
obstacles.removeAll(obstacles);
obstaclesInOrder = Arrays.asList(new Obstacle(175, 20, 45), new Obstacle(320), new Obstacle(460), new Obstacle(585), new Obstacle(700));
obstacles.addAll(obstaclesInOrder);
ball = new Ball(30, 100);
}
#Override
public void mouseClicked(MouseEvent e) {
if (!currentlyPlaying) {
startGame();
} else if (!isAlive) {
reset();
}
readyToJump = true;
}
private void startGame() {
currentlyPlaying = true;
messageLabel.update("");
timer.start();
}
private void endGame() {
isAlive = false;
scoreLabel.update("");
messageLabel.update("Game Over. Your score was " + Integer.toString(score));
timer.stop();
}
private void reset() {
ball.reset();
for (Obstacle obstacle : obstacles)
obstacle.reset();
messageLabel.update(START_MESSAGE, 150, 240);
scoreLabel.update(START_SCORE, 250, 60);
obstacles.removeAll(obstacles);
obstacles.addAll(obstaclesInOrder);
score = 0;
isAlive = true;
currentlyPlaying = false;
repaint();
}
#Override
public void actionPerformed(ActionEvent ae) {
update();
repaint();
}
private void update() {
if (isAlive) {
updateBallPosition();
updateObstaclePositions();
if(ballOutOfBounds() || playerCollidedWithObstacle()) {
endGame();
} else if(ballPassedObstacle()) {
addToScore();
setupNextObstacle();
}
}
}
private void updateBallPosition() {
if (readyToJump) {
readyToJump = false;
ball.jump();
} else {
ball.fall();
}
}
private void updateObstaclePositions() {
for (Obstacle obstacle : obstacles) {
if (obstacle.getX() <= -obstacle.getWidth()) {
obstacle.moveToBack();
continue;
}
obstacle.moveForward();
}
}
private void addToScore() {
scoreLabel.update(Integer.toString(++score));
}
private void setupNextObstacle() {
obstacles.addLast(obstacles.removeFirst());
}
private boolean ballOutOfBounds() {
return ball.getY() >= HEIGHT || ball.getY() <= 0;
}
private boolean ballAtObstacle() {
Obstacle currentObstacle = obstacles.getFirst();
return ball.getX() + ball.getWidth() >= currentObstacle.getX() && ball.getX() <= currentObstacle.getX() + currentObstacle.getWidth();
}
private boolean ballPassedObstacle() {
Obstacle currentObstacle = obstacles.getFirst();
return ball.getX() >= (currentObstacle.getX() + currentObstacle.getWidth());
}
private boolean playerCollidedWithObstacle() {
boolean collided = false;
if(ballAtObstacle()) {
for (Obstacle obstacle : obstacles) {
RoundRectangle2D top = obstacle.getTop();
RoundRectangle2D bottom = obstacle.getBottom();
if (ball.intersects(top.getX(), top.getY(), top.getWidth(), top.getHeight()) || ball.intersects(bottom.getX(), bottom.getY(), bottom.getWidth(), bottom.getHeight())) {
collided = true;
}
}
}
return collided;
}
private Image loadBackgroundImage() {
Image background = null;
URL backgroundPath = getClass().getResource(BACKGROUND_URL);
if(backgroundPath == null) {
background = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
} else {
try {
background = ImageIO.read(backgroundPath);
} catch (IOException e) {
e.printStackTrace();
}
}
return background;
}
#Override
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
Graphics2D g = (Graphics2D) graphics;
g.drawImage(background, 0, 0, null);
ball.paint(g);
for (Obstacle obstacle : obstacles)
obstacle.paint(g);
scoreLabel.paint(g);
messageLabel.paint(g);
}
//...
}
loadBackgroundImage() loads and returns an image. If a problem occurs (image probably isn't there), it returns a black image.
(Most of) The game logic is in the update() method. Although it should all be in there, we can't due to a design flaw (from you using Timer which manages update(), and you start the timer in a listener, therefore we needed some logic in the listener). Your logic should be easy to read, and every execution step should be monitored and set by highest priority to lowest.
First, I check to make sure the ball hasn't collided with anything or gone out of bounds. If one of those things occur, I end the game.
If not, I check to see if the player has passed an obstacle. If the player passes an obstacle, I add to the score:
private void update() {
if (ballOutOfBounds() || playerCollidedWithObstacle()) {
endGame();
} else if (ballPassedObstacle()) {
addToScore();
setupNextObstacle();
}
updateBallPosition();
updateObstaclePositions();
}
I then finally update the positions.
To avoid constantly comparing the player's position and the obstacle's position (to see if the player has passed it), I created a boolean ballAtObstacle() method, which checks if the player is at an obstacle. Only then do I compare positions:
private boolean playerCollidedWithObstacle() {
boolean collided = false;
if (ballAtObstacle()) {
for (Obstacle obstacle : obstacles) {
RoundRectangle2D top = obstacle.getTop();
RoundRectangle2D bottom = obstacle.getBottom();
if (ball.intersects(top.getX(), top.getY(), top.getWidth(), top.getHeight()) || ball.intersects(bottom.getX(), bottom.getY(), bottom.getWidth(), bottom.getHeight())) {
collided = true;
}
}
}
return collided;
}
The ball.intersects method call is a bit messy. I did this so the shape didn't have to be specific, although you could declare the intersects method as boolean intersects(Shape shape).
Finally, I gave it a more flappy bird feel by increasing the fall speed as you fall. When you jump again, it goes back to normal. The longer it takes you to jump, the faster you'll fall. If you don't like this feature, and don't know how to remove it, let me know and I'll show you how.
The other classes involved:
GameLabel.java
public class GameLabel {
private String message;
private Font font;
private Color color;
private int x, y;
public GameLabel(String message, int x, int y, Color color, Font font) {
update(message, x, y, color, font);
}
public GameLabel(String message, int x, int y) {
this(message, x, y, Color.BLACK, new Font("Matura MT Script Capitals", Font.ROMAN_BASELINE, 20));
}
public GameLabel() {
this("", 0, 0);
}
public void setMessage(String message) {
this.message = message;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setColor(Color color) {
this.color = color;
}
public void setFont(Font font) {
this.font = font;
}
public final void update(String message, int x, int y, Color color, Font font) {
this.message = message;
this.x = x;
this.y = y;
this.color = color;
this.font = font;
}
public void update(String message, int x, int y) {
update(message, x, y, color, font);
}
public void update(String message) {
update(message, x, y);
}
public void paint(Graphics2D g) {
g.setFont(font);
g.setColor(color);
g.drawString(message, x, y);
}
public Font getFont() {
return font;
}
public Color getColor() {
return color;
}
public String getMessage() {
return message;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
Obstacle.java
public class Obstacle {
public static final int DEFAULT_TOP_HEIGHT = 175;
public static final int DEFAULT_BOTTOM_HEIGHT = 175;
public static final int DEFAULT_WIDTH = 30;
public static final int DEFAULT_ARCH_WIDTH = 20;
public static final int DEFAULT_ARCH_HEIGHT = 20;
public static final int DEFAULT_TOP_INSET = -5;
public static final int DEFAULT_BOTTOM_INSET = TheGame.HEIGHT + 5;
private RoundRectangle2D top, bottom;
private BasicStroke stroke;
private GradientPaint gradient;
private int initialX, x, width;
public Obstacle(int x, int width, int topHeight, int bottomHeight) {
this.x = initialX = x;
this.width = width;
top = new RoundRectangle2D.Double(x, DEFAULT_TOP_INSET, width, topHeight, DEFAULT_ARCH_WIDTH, DEFAULT_ARCH_HEIGHT);
bottom = new RoundRectangle2D.Double(x, DEFAULT_BOTTOM_INSET-bottomHeight, width, bottomHeight, DEFAULT_ARCH_WIDTH, DEFAULT_ARCH_HEIGHT);
stroke = new BasicStroke(10, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL, 20.0f, new float[] { 10.0f }, 0.0f);
gradient = new GradientPaint(20, 0, Color.DARK_GRAY, 0, 10, Color.GRAY, true);
}
public Obstacle(int x, int topHeight, int bottomHeight) {
this(x, DEFAULT_WIDTH, topHeight, bottomHeight);
}
public void reset() {
x = initialX;
top.setRoundRect(initialX, top.getY(), top.getWidth(), top.getHeight(), top.getArcWidth(), top.getArcHeight());
bottom.setRoundRect(initialX, bottom.getY(), bottom.getWidth(), bottom.getHeight(), bottom.getArcWidth(), bottom.getArcHeight());
}
public Obstacle(int x, int width) {
this(x, width, DEFAULT_TOP_HEIGHT, DEFAULT_BOTTOM_HEIGHT);
}
public Obstacle(int x) {
this(x, DEFAULT_WIDTH);
}
public void moveToBack() {
x = 600;
}
public void moveForward() {
x -= 5;
top.setRoundRect(x, top.getY(), top.getWidth(), top.getHeight(), top.getArcWidth(), top.getArcHeight());
bottom.setRoundRect(x, bottom.getY(), bottom.getWidth(), bottom.getHeight(), bottom.getArcWidth(), bottom.getArcHeight());
}
public RoundRectangle2D getTop() {
return top;
}
public RoundRectangle2D getBottom() {
return bottom;
}
public int getX() {
return x;
}
public int getWidth() {
return width;
}
public void paint(Graphics2D g) {
g.setPaint(gradient);
g.setStroke(stroke);
g.fill(top);
g.fill(bottom);
}
}
Ball.java
public class Ball {
public static final int DEFAULT_DROP_SPEED = 7;
private Ellipse2D ball;
private GradientPaint gradient;
private int initialY, x, y, width = 30, height = 30, dropSpeed = DEFAULT_DROP_SPEED;
public Ball(int x, int y) {
this.x = x;
this.y = initialY = y;
width = height = 30;
ball = new Ellipse2D.Double(x, y, width, height);
gradient = new GradientPaint(30, 0, Color.BLACK, 0, 20, Color.GREEN, true);
}
public void reset() {
y = initialY;
updateBall();
}
public void jump() {
dropSpeed = DEFAULT_DROP_SPEED;
y -= 40;
updateBall();
}
public void fall() {
y += dropSpeed++;
updateBall();
}
private void updateBall() {
ball.setFrame(x, y, width, height);
}
public boolean intersects(double x, double y, double w, double h) {
return ball.intersects(x, y, w, h);
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return width;
}
public void paint(Graphics2D g) {
g.setPaint(gradient);
g.fill(ball);
}
public void moveForward() {
x += 7;
}
}
Reinitialize all the components by adding the initialization code to one method(resetAll) and call that method when u want to reinitailize
Below is an example:
import java.awt.*;
import java.awt.event.*;
public class ResetTest extends Frame{
Button b;
TextField tf;
Frame f;
Panel p;
public ResetTest(){
f=this; //intialize frame f to this to have access to our frame in event handler methods
resetAll(); //first time call to resetAll will initialize all the parameters of the frame
f.pack();
addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent we){
System.exit(0);
}
});
this.setLayout(new FlowLayout(FlowLayout.CENTER,20,20));
this.setVisible(true);
}
/**
This method will be called on click of button to
reset all the parameters of the frame so that we
get fresh new frame, everything as it was before.
**/
public void resetAll(){
b=new Button("Reset");
p=new Panel(new FlowLayout(FlowLayout.CENTER,20,20));
p.add(b);
tf = new TextField("Edit And Reset");
p.add(tf);
b.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae){
remove(p); //remove the panel that contains all our components
resetAll(); // reset all the components
f.pack(); //refreshes the view of frame when everything is reset.
}
});
add(p);
}
}
class NewClass{
public static void main(String[] args) {
ResetTest fReset = new ResetTest();
}
}