I'm working on my first java game for a school project, and I'm having some problems drawing the graphics based on information in an array.
What I'm basically trying to do is to create a 2D array (matrix) which will store all the information about the world in which the player can move. So some elements in the array will contain a wall, others open space for the player to move in, and so on...
I have this sample code which I'm working from:
/**
*
* #author Rasztemberg
*/
package simpleGame;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
/**
* GameBoard is a part of JPanel.
* It has a Graphical Object {#link Graphics}
* that can be used to render shapes etc. Pass it's reference to any object you
* want to display in the gameBoard panel.
*/
public class GameBoard extends JPanel implements KeyListener{
Player player;
Player enemy;
public GameBoard(){
// SETUP PLAYER ON THE BOARD;
int xPos = 0;
int yPos = 0;
int width = 20;
int height = 20;
Color playerC = Color.BLUE;
player = new Player(xPos, yPos, width, height, playerC);
// SETUP ENEMY ON THE BOARD;
enemy = new Player(100, 100, width, height, Color.RED);
addKeyListener(this);
}
/*
*
* JPanel function to display all gameBoard object graphics.
*/
#Override
public void paint(Graphics g){
super.paint(g); // Call it's parent for proper rendering.
player.display(g);
enemy.display(g);
}
#Override
public void keyTyped(KeyEvent e) {}
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_LEFT) {
player.moveLeft();
}
if (keyCode == KeyEvent.VK_RIGHT) {
player.moveRight();
}
if (keyCode == KeyEvent.VK_DOWN) {
player.moveDown();
}
if (keyCode == KeyEvent.VK_UP) {
player.moveUp();
}
}
#Override
public void keyReleased(KeyEvent e) {}
/**
* Set's focus on the panel so key events are catch.
*/
public boolean isFocusTraversable() {
return true;
}
}
And,
/**
* #author Rasztemberg
*/
package simpleGame;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import javax.swing.JPanel;
/**
* This is main player Class.
*
*/
public class World {
// Snake parameters
private int x;
private int y;
private int width;
private int height;
private Color color;
/**
* Class constructor. Called when instantiated.
* Assigns x and y coordinates to position the player.
* Sets width, height and color to the rendered object.
*
*/
public World(int x, int y, int w, int h, Color c){
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.color = c;
}
/**
* Accepts Graphics object to render
* player 1 shape
*/
public void display(Graphics g) {
// This is player rendered graphics.
Graphics2D walls = (Graphics2D) g; // Graphical library to render shapes.
walls.setColor(color);
walls.drawRect(x, y, width, height);
walls.fillRect(x, y, width, height);
}
}
Now, I made this for loop to populate a test Array:
int[][] wallArray = new int[800][600];
for (int x = 0; x < wallArray.length; x++) {
for (int y = 0; y < wallArray.length; y++) {
wallArray[x][y] = 1;
}
}
wallArray[100][100] = 0;
greatWall = new World(wallArray);
Do you know how I could draw this array? I apologize for the length of the code...
Just paint something based on the array in your GameBoard's paint:
#Override
public void paint(Graphics g){
super.paint(g); // Call it's parent for proper rendering.
for (int i = 0; i<wallArray.length; i++)
for (int j = 0; j<wallArray[0].length; j++){
//do something for every field in the array
//i.e. g.setColor(Color.getColor(wallArray[i][j], 50, 50));
//g.drawLine(i,j,i,j);
}
player.display(g);
enemy.display(g);
}
But you should first go through some tutorial on Painting in Java:
http://www.oracle.com/technetwork/java/painting-140037.html
It is better to have the walls as
List<Rectangle> walls = new ArrayList<>();
That is more optimal, as you can simply do:
for (Rectangle rect: walls) {
g.fillRect(rect.x, rect.y, rect.width, rect.height);
}
Wall detection goes alike.
Related
So I am trying to generate random rectangles that the player must avoid. My collision method was working with a single, randomly generated rectangle. I want to draw 10 or so of these and then I will add a finish line and a timer.
Right now, I understand my problem, but I am not sure how to fix it. The ball/player's movement is executed by changing the x or y coordinates by 10 and then repainting the circle. I currently have the rectangles in the same paint method, so each time the player moves the rectangles are regenerated. I would like them to stay in the same place after the initial random generation. I don't really know how to do this though...
Also, if I can get the rectangles to stay in the same place, will my collision method still work with multiple rectangles? or would I need to revise that as well?
I am just going to post the whole program's code because I'm not sure which parts will need to be revised.
import java.awt.*;
import java.awt.Rectangle;
import java.awt.Shape;
import javafx.scene.shape.*;
import java.awt.event.*;
import java.util.Random;
import java.awt.geom.Area;
import javax.swing.JOptionPane;
import java.applet.Applet;
public class Main extends Applet
implements ActionListener{
boolean end = false;
private Rectangle rectangle;
//creates buttons to move player
private Button run = new Button("Run");
private Button jump = new Button("Jump");
private Button fall = new Button("Fall");
//creates player and obstacles
private Circle player = new Circle(110,110,20);
private makeRect block = new makeRect(150, 120, 30, 10);
//initiates the buttons with actionListener
public void init(){
add(run);
add(jump);
add(fall);
run.addActionListener(this);
jump.addActionListener(this);
fall.addActionListener(this);
}
//draws the player and blocks on the screen
public void paint(Graphics g){
for(int numBlocks = 0; numBlocks<11; numBlocks++){
block.draw(g);}
player.draw(g);
}
//if methods to be control movement
public void actionPerformed(ActionEvent e){
if(e.getSource() instanceof Button){
if(e.getSource() == run)
player.horiz(10);
else if (e.getSource()== jump){
player.vert(-10);
}
else if (e.getSource()== fall){
player.down(10);
}
repaint();
collision();
}
}
public void collision(){
if(player.getBounds().intersects(block.getBounds())){
JOptionPane.showMessageDialog(this, "Game Over", "Game Over", JOptionPane.YES_NO_OPTION);
System.exit(ABORT);
end = true;
}
}
class Circle{
private final Color theColor = Color.BLUE;
private int radius;
private int x,y;
public Circle(){
x = 110; y = 110;
radius = 20;
}
public Circle(int x0, int y0, int rad){
x = x0; y = y0; radius = rad;
}
public void draw(Graphics g){
g.fillOval(x - radius, y-radius, 2*radius, 2*radius);
g.setColor(theColor);
}
public void horiz(int val){
for(int c = 0; c<val+1; c++){
x++;
repaint();}
}
public void vert(int val){
y += val;
}
public void down(int val){
y += val;
}
public Rectangle getBounds(){
return new Rectangle(x-radius, y-radius, 2*radius, 2*radius);
}
}
class makeRect{
private int Xmax = 250;
private int Xmin = 140;
private int Wmax = 50;
private int Hmax = 25;
private int Wmin = 10;
private int Hmin = 5;
Random rand = new Random();
private int randx;
private int randh;
private int x, y, width, height;
public makeRect(){
x = 150; y = 120;
width = 30; height = 10;
}
public makeRect(int x0, int y0, int w0, int h0){
x = x0; y = y0; width = w0; height = h0;
}
public void draw(Graphics g) {
int randx = rand.nextInt((Xmax-Xmin)+1)+Xmin;
int randh = rand.nextInt((Hmax-Hmin)+1)+Hmin;
int randw = rand.nextInt((Wmax-Wmin)+1)+Wmin;
g.drawRect(randx, 110+randh, randh, randw);
}
public Rectangle getBounds(){
return new Rectangle(randx, 110+randh, 30, 10);
}
}
}
Thanks!
For that you will need to construct 10 rects (consider using array) first upon initialization, with random postition for each. I mean, postition randomization occurs when the rects are constructed, not when it's drawn.
What you have there is a same rectangle drawn 10 times at different places each time the paint() gets called.
I am trying to draw circle objects with each click and then store every circle object into an Arraylist, I don't know why my program is not working! If I removed the arraylist and the line that create a new circle object, the program will work. How would I make my program store all circuit objects into an Arraylist ?
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Random;
public class CircleObj extends JPanel {
private int rColor;
private int gColor;
private int bColor;
private int radius;
private Random rand = new Random();
private int xStart;
private int yStart;
ArrayList <Circle> xxx ;
public CircleObj () {
xxx = new ArrayList<Circle>();
addMouseListener(new MouseAdapter() {
public void mouseClicked (MouseEvent e) {
xStart = e.getX();
yStart = e.getY();
rColor = rand.nextInt(256);
gColor = rand.nextInt(256);
bColor = rand.nextInt(256);
radius = rand.nextInt(20);
repaint();
}
}); // end addMouseListener
}
public void paintComponent (Graphics g) {
super.paintComponent(g);
g.setColor(new Color(rColor, gColor, bColor));
g.fillOval(xStart, yStart, radius, radius);
xxx.add(new Circle());
}
private class Circle {
private int x;
private int y;
private int r;
private int rcol;
private int gcol;
private int bcol;
public Circle()
{
x=xStart;
y=yStart;
r=radius;
rcol= rColor;
gcol= gColor;
bcol= bColor;
}
}
}
======
import javax.swing.JFrame;
import java.awt.BorderLayout;
public class HW3 {
public static void main (String[] arg) {
JFrame frame = new JFrame("Circles");
CircleObj canvas = new CircleObj();
frame.add(canvas, BorderLayout.CENTER);
frame.setBounds(250, 98, 600, 480);
//frame.setLayout(new BorderLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
} // end main
} //end HW3
Don't add the new shape inside the paintComponent method, paintComponent can be called for any number of reasons, many of which you don't control, instead, create it when the mouseClicked event is triggered...
public void mouseClicked (MouseEvent e) {
xStart = e.getX();
yStart = e.getY();
rColor = rand.nextInt(256);
gColor = rand.nextInt(256);
bColor = rand.nextInt(256);
radius = rand.nextInt(20);
xxx.add(new Circle(xStart, yStart, new Color(rColor, gColor, bColor), radius));
repaint();
}
And then in your paintComponent, loop through the ArrayList and paint the circles...
public void paintComponent (Graphics g) {
super.paintComponent(g);
for (Circle c : xxx) {
g.setColor(c.getColor());
g.fillOval(c.getX(), c.getY(), c.getRadius(), c.getRadius());
}
}
Now, you're going to have to modify you Circle class to provide getters which the CircleObj can use in order to actually paint the circles...
Alternatively, you could make use of the Shapes API provided within Java...Have a look at Working with Geometry for more details...
Alright, so I've been trying to move my polygon so it bounces around the screen. Right now it does so but leave a trail the entire way. I have a GUI class, extended by an Aquarium class, which fills the screen with the color blue, creates a "creature" object (from Creature class) and uses update to move it down the screen. When it updates however, a trail is left behind. I have tried many methods mentioned in similar cases on the site but nothing works. Can somebody please help me?
GUI class (that holds JPanel etc)
package artilife;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class AquariumGUI {
private GooPanel gooPanel;
private boolean loop = true;
protected int width, height;
private int frameTimeInMillis = 300;
private RenderingHints renderingHints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
#SuppressWarnings("serial")
class GooPanel extends JPanel {
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHints(renderingHints);
draw(g2d);
}
}
public AquariumGUI() {
this(800, 500);
}
public AquariumGUI(int w, int h) {
width = w;
height = h;
JFrame frame = new JFrame();
frame.setSize(width, height);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gooPanel = new GooPanel();
gooPanel.setPreferredSize(new Dimension(w, h));
frame.getContentPane().add(gooPanel);
frame.pack();
frame.setVisible(true);
}
public void go() {
while (loop) {
gooPanel.repaint();
update();
try {
Thread.sleep(frameTimeInMillis);
} catch (InterruptedException e) {
}
}
}
public void draw(Graphics2D g) {
}
public void update(){
}
public void setFrameTime(int millis){
frameTimeInMillis = millis;
}
}
Aquarium class that extended this and attempts to color the screen and place a Polygon creature on it:
package artilife;
import java.awt.Color;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.Random;
public class MyAquarium extends AquariumGUI {
Creature tester;
public MyAquarium()
{
tester = new Creature();
}
public void draw(Graphics2D g) {
// Fill background
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
tester.draw(g);
}
public void update(){
tester.move(width, height);
}
public static void main(String[] args) {
MyAquarium aquarium = new MyAquarium();
aquarium.go();
}
}
And the creature that is being drawn, and holds the move method:
package artilife;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Creature
{
// Position and radius of the drop
private double x, y, r, Vx, Vy;
Polygon p = new Polygon();
int numSides;
public Creature() {
x = 800 / 2;
r = 10;
y = 50;
Vx = 10;
Vy = 3;
numSides = 3;
//creatureShape(numSides);
}
public void creatureShape (int sides)
{
if (sides <= 10){
for (int i = 0; i < sides; i++){
p.addPoint((int) (x + 50 * Math.cos((i) * 2 * Math.PI / sides)),
(int) (y + 50 * Math.sin((i) * 2 * Math.PI / sides)));
}
}
else{
for (int i = 0; i < 360; i++) {
double value = i / 360.0;
p.addPoint((int) (90 + 50 * value * Math.cos(8 * value * Math.PI)),
(int) (50 + 50 * value * Math.sin(8 * value * Math.PI)));
}
}
}
public void draw(Graphics2D g) {
creatureShape(numSides);
g.setColor(Color.BLUE);
g.fillPolygon(p);
}
public void move(int width, int height){
// Move the drop
x = x + Vx;
y = y +Vy;
if (y >= height){
Vy = -Vy;
y = height;
}
else if(y <= 0){
Vy = -Vy;
y = 0;
}
if(x >= width){
Vx = -Vx;
x = width;
}
if(x <= 0){
Vx = -Vx;
x = 0;
}
}
/*
public void attraction ()
{
if (
}*/
/*
public boolean check4Creatures ()
{
boolean isThere = false;
//if there is an another create object within a 60 point radius
isThere = true;
return isThere;
}*/
}
SOLUTION:
So fiddle for what feels like forever and it works now.
When drawing polygons in motion, some java functions store bits of information regarding the vertices. The best way to move them cleanly is to use translate() instead of manipulating the vertices directly (by updating x and y values) when updating the information, and then draw the fresh polygon.
Thank everyone for taking the time to look at my problem though!
You need to clear the screen between two drawing operations. You can either leave it to the framework, by calling the super.paintComponent() method, or you can do it your way, by filling the screen with something, like a g.fillRect() call.
The first method will actually make a call the the fillRect function, using the panel background color.
I'm writing a program that lets the user draw a hollow rectangle on the screen on top of an image. They can also click on the image and the clicks are joined up to form polygons.
Joining up points works fine, but when the user drags to draw a rectangle, the previously drawn rectangles and polygons disappear. The runnable code is below;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ImageLabeller extends JFrame {
static boolean drawRectangle = false;
/**
* some java stuff to get rid of warnings
*/
private static final long serialVersionUID = 1L;
/**
* main window panel
*/
JPanel appPanel = null;
/**
* toolbox - put all buttons here
*/
JPanel toolboxPanel = null;
/**
* image panel - displays image and editing area
*/
static ImagePanel imagePanel;
/**
* handles New Object button action
*/
public ImageLabeller(String imageFilename) {
try {
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent event) {
System.out.println("Bye bye!");
System.exit(0);
}
});
// Create and set up the image panel.
// setup main window panel
setExtendedState(Frame.MAXIMIZED_BOTH);
appPanel = new JPanel();
appPanel.setLayout(new BoxLayout(appPanel, BoxLayout.Y_AXIS));
this.setContentPane(appPanel);
imagePanel = new ImagePanel(imageFilename);
imagePanel.setOpaque(true); // content panes must be opaque
imagePanel.setBorder(BorderFactory.createLineBorder(Color.green));
// create toolbox panel
toolboxPanel = new JPanel();
toolboxPanel.setBorder(BorderFactory.createLineBorder(Color.black));
JButton newPolyButton = new JButton("New object");
newPolyButton.setMnemonic(KeyEvent.VK_N);
// newPolyButton.setSize(50, 20);
newPolyButton.setToolTipText("Click to add new object");
newPolyButton.addActionListener(new DrawListener());
JButton newSquareButton = new JButton("New Square");
newSquareButton.setMnemonic(KeyEvent.VK_S);
// newPolyButton.setSize(50, 20);
newSquareButton.setEnabled(true);
newSquareButton.setToolTipText("Click to add new square");
newSquareButton.addActionListener(new SquareListener());
// add all buttons to toolboxPanel
toolboxPanel.add(newPolyButton);
toolboxPanel.add(newSquareButton);
// add all panels to appPanel
appPanel.add(toolboxPanel);
appPanel.add(imagePanel);
// appPanel.add(Box.createRigidArea(new Dimension(0,10)));
// display all the stuff
this.pack();
this.setVisible(true);
} catch (Exception e) {
System.err.println("Image: ");
e.printStackTrace();
}
}
public static void addNewPolygon() {
imagePanel.addNewPolygon();
}
public static void addNewRectangle() {
//imagePanel.addNewRectangle();
}
static class DrawListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
addNewPolygon();
}
}
static class SquareListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
drawRectangle = true;
imagePanel.drawingRectangle = true;
System.out.println(imagePanel.drawingRectangle);
}
}
public static void main (String args []) {
new ImageLabeller("/change to/a photo/ of your choice.jpg");
}
}
_
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class ImagePanel extends JPanel implements MouseListener,
MouseMotionListener {
Rectangle currentRectangle = null;
boolean drawingRectangle = false;
/**
* some java stuff to get rid of warnings
*/
private static final long serialVersionUID = 1L;
/**
* image to be tagged
*/
BufferedImage image = null;
/**
* list of current polygon's vertices
*/
ArrayList<Point> currentPolygon = null;
/**
* list of polygons
*/
ArrayList<ArrayList<Point>> polygonsList = null;
ArrayList<Rectangle> rectangleList = null;
/**
* extended constructor - loads image to be labelled
*
* #param imageName
* - path to image
* #throws Exception
* if error loading the image
*/
public ImagePanel(String imageName) throws Exception {
currentPolygon = new ArrayList<Point>();
polygonsList = new ArrayList<ArrayList<Point>>();
rectangleList = new ArrayList<Rectangle>();
image = ImageIO.read(new File(imageName));
Dimension panelSize = new Dimension(image.getWidth(), image.getHeight());
this.setSize(panelSize);
this.setMinimumSize(panelSize);
this.setPreferredSize(panelSize);
this.setMaximumSize(panelSize);
setBounds(0, 0, image.getWidth(), image.getHeight());
addMouseListener(this);
addMouseMotionListener(this);
this.setVisible(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Paint Component");
Graphics2D g2d = (Graphics2D) g;
// Paint image on screen
g2d.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
// display all the completed polygons
for (ArrayList<Point> polygon : polygonsList) {
drawPolygon(polygon);
finishPolygon(polygon);
System.out.println("Polly");
}
// Display all completed squares
for (Rectangle r : rectangleList) {
drawRectangle(r);
System.out.println("Square");
}
// display current polygon
if (currentPolygon != null) {
drawPolygon(currentPolygon);
}
// display current square
if (currentRectangle != null) {
drawRectangle(currentRectangle);
}
}
/**
* displays a polygon without last stroke
*
* #param polygon
* to be displayed
*/
public void drawPolygon(ArrayList<Point> polygon) {
Graphics2D g = (Graphics2D) this.getGraphics();
// set to red so I can see when it's being redrawn
g.setColor(Color.RED);
g.setStroke(new BasicStroke(3));
for (int i = 0; i < polygon.size(); i++) {
Point currentVertex = polygon.get(i);
if (i != 0) {
Point prevVertex = polygon.get(i - 1);
g.drawLine(prevVertex.getX(), prevVertex.getY(),
currentVertex.getX(), currentVertex.getY());
}
g.fillOval(currentVertex.getX() - 5, currentVertex.getY() - 5, 10,
10);
}
}
public void drawRectangle(Rectangle r) {
Graphics2D g = (Graphics2D) this.getGraphics();
g.setStroke(new BasicStroke(3));
g.setColor(Color.BLUE);
g.drawLine(r.getX1(), r.getY1(), r.getX2(), r.getY1());
g.drawLine(r.getX1(), r.getY1(), r.getX1(), r.getY2());
g.drawLine(r.getX2(), r.getY2(), r.getX2(), r.getY1());
g.drawLine(r.getX2(), r.getY2(), r.getX1(), r.getY2());
System.out.println(r.getX1() + " " + r.getY1() + " " + r.getX2());
System.out.println("Drawn rectangle");
}
/**
* displays last stroke of the polygon (arch between the last and first
* vertices)
*
* #param polygon
* to be finished
*/
public void finishPolygon(ArrayList<Point> polygon) {
// if there are less than 3 vertices than nothing to be completed
if (polygon.size() >= 3) {
Point firstVertex = polygon.get(0);
Point lastVertex = polygon.get(polygon.size() - 1);
Graphics2D g = (Graphics2D) this.getGraphics();
g.setColor(Color.GREEN);
g.setStroke(new BasicStroke(3));
g.drawLine(firstVertex.getX(), firstVertex.getY(),
lastVertex.getX(), lastVertex.getY());
}
}
/**
* moves current polygon to the list of polygons and makes pace for a new
* one
*/
public void addNewPolygon() {
// finish the current polygon if any
if (currentPolygon != null) {
finishPolygon(currentPolygon);
polygonsList.add(currentPolygon);
}
currentPolygon = new ArrayList<Point>();
}
public void mouseClicked(MouseEvent e) {
if (!drawingRectangle) {
int x = e.getX();
int y = e.getY();
// check if the cursor is within image area
if (x > image.getWidth() || y > image.getHeight()) {
// if not do nothing
return;
}
Graphics2D g = (Graphics2D) this.getGraphics();
// if the left button than we will add a vertex to poly
if (e.getButton() == MouseEvent.BUTTON1) {
g.setColor(Color.GREEN);
if (currentPolygon.size() != 0) {
Point lastVertex = currentPolygon
.get(currentPolygon.size() - 1);
g.setStroke(new BasicStroke(3));
g.drawLine(lastVertex.getX(), lastVertex.getY(), x, y);
}
g.fillOval(x - 5, y - 5, 10, 10);
currentPolygon.add(new Point(x, y));
System.out.println(x + " " + y + " polygon point");
}
}
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
if (drawingRectangle) {
currentRectangle = new Rectangle(arg0.getX(), arg0.getY(),
arg0.getX(), arg0.getY(), Color.BLACK);
}
}
public void mouseReleased(MouseEvent arg0) {
if (drawingRectangle) {
rectangleList.add(new Rectangle(currentRectangle.getX1(),
currentRectangle.getY1(), arg0.getX(), arg0.getY(),
currentRectangle.getColor()));
System.out.println(currentRectangle.getX1() + " "
+ currentRectangle.getY1() + " " + arg0.getX() + " "
+ arg0.getY() + " rectangle point");
// unnecessary when mouseDragged calls paintComponent directly?
drawRectangle(new Rectangle(currentRectangle.getX1(),
currentRectangle.getY1(), arg0.getX(), arg0.getY(),
currentRectangle.getColor()));
currentRectangle = null;
drawingRectangle = false;
}
}
public void mouseDragged(MouseEvent arg0) {
if (drawingRectangle) {
currentRectangle = new Rectangle(currentRectangle.getX1(),
currentRectangle.getY1(), arg0.getX(), arg0.getY(),
currentRectangle.getColor());
System.out.println(currentRectangle.getX1() + " "
+ currentRectangle.getY1() + " " + arg0.getX() + " "
+ arg0.getX() + " " + "Dragging");
repaint();
// It works better using this instead on repaint()
// Graphics g = this.getGraphics();
// paintComponent(g);
}
}
public void mouseMoved(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}
-
public class Point {
private int x = 0;
private int y = 0;
public Point() {
}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
-
import java.awt.Color;
import java.awt.Graphics2D;
public class Rectangle {
// Initialize variables
private int x1; // x coordinate of first endpoint
private int y1; // y coordinate of first endpoint
private int x2; // x coordinate of second endpoint
private int y2; // y coordinate of second endpoint
private Color colour; // colour of the shape
// A no-parameter constructor that sets all the coordinates of the shape to
// 0 and the
// colour to Color.BLACK
public Rectangle() {
x1 = 0;
y1 = 0;
x2 = 0;
y2 = 0;
colour = Color.BLACK;
}
// A constructor that initializes the coordinates and colour to the values
// of the
// parameters supplied.
public Rectangle(int x1, int y1, int x2, int y2, Color col) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.colour = col;
}
public void setX1(int x1) {
this.x1 = x1;
}
public void setY1(int y1) {
this.y1 = y1;
}
public void setX2(int x2) {
this.x2 = x2;
}
public void setY2(int y2) {
this.y2 = y2;
}
public void setColor(Color colour) {
this.colour = colour;
}
public int getX1() {
return this.x1;
}
public int getY1() {
return this.y1;
}
public int getX2() {
return this.x2;
}
public int getY2() {
return this.y2;
}
public Color getColor() {
return this.colour;
}
public int getWidth() {
return (Math.abs(x2 - x1));
}
public int getHeight() {
return (Math.abs(y2 - y1));
}
public int getUpperLeftX() {
return (Math.min(x1, x2));
}
public int getUpperLeftY() {
return (Math.min(y1, y2));
}
}
Sorry for the mass of code, I tried to trim out as much as I could.
Clicking on the image draws points which are joined up to create lines. When the user clicks the "New Object" button, the first and last points are joined to create a polygon. This all works fine, but if you click "New Square" and drag on the image, all previous shapes flicker as the mouse is moved and disappear when the mouse button is released. If "New Square" is clicked again (a necessity of poor coding on my behalf so far) and another square drawn, the 'disappeared' shapes can be seen flickering again, but then disappear once the mouse is released.
I'm calling repaint() in the mouseDragged(...) event, which I thought was all that was necessary. It actually works (almost) as wanted when I change
repaint();
for
Graphics g = this.getGraphics();
paintComponent(g);
but every book and article I've read said I should never call paintComponent myself, ever. An issue with calling paintComponent however is that the background image tends to flicker alot.
If repaint() calls paintComponent, why do they lead to different results?
I also don't understand why, when using repaint() in the mouseDragged event, I must , I must also call drawRectangle(...) in mouseReleased for the square to be seen at all, but when using paintComponent, I don't?
Any advice or pointers are greatly appreciated, thankyou.
In your drawRectangle and drawPolygon you are re-getting a graphics object, but you are calling them from paintComponent. this is what's causing the odd behaviour, you should pass the graphics from painComponent in to those methods.
I am seeing other odd behaviour though, like the polygons stay green until I've finished painting a square, when they then turn red, but one of the lines stays green and eventually vanishes. I didn't look in to that too much.
Also, polygon drawing and Point and Rectangle are supported in awt, is there any reason you chose to create your own classes for those?
package test;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class ImagePanel extends JPanel implements MouseListener,
MouseMotionListener {
Rectangle currentRectangle = null;
boolean drawingRectangle = false;
/**
* some java stuff to get rid of warnings
*/
private static final long serialVersionUID = 1L;
/**
* image to be tagged
*/
BufferedImage image = null;
/**
* list of current polygon's vertices
*/
ArrayList<Point> currentPolygon = null;
/**
* list of polygons
*/
ArrayList<ArrayList<Point>> polygonsList = null;
ArrayList<Rectangle> rectangleList = null;
/**
* extended constructor - loads image to be labelled
*
* #param imageName
* - path to image
* #throws Exception
* if error loading the image
*/
public ImagePanel(String imageName) throws Exception {
currentPolygon = new ArrayList<Point>();
polygonsList = new ArrayList<ArrayList<Point>>();
rectangleList = new ArrayList<Rectangle>();
image = ImageIO.read(new File(imageName));
Dimension panelSize = new Dimension(image.getWidth(), image.getHeight());
this.setSize(panelSize);
this.setMinimumSize(panelSize);
this.setPreferredSize(panelSize);
this.setMaximumSize(panelSize);
setBounds(0, 0, image.getWidth(), image.getHeight());
addMouseListener(this);
addMouseMotionListener(this);
this.setVisible(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Paint Component");
Graphics2D g2d = (Graphics2D) g;
// Paint image on screen
g2d.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
// display all the completed polygons
for (ArrayList<Point> polygon : polygonsList) {
drawPolygon(polygon,g);
finishPolygon(polygon);
System.out.println("Polly");
}
// Display all completed squares
for (Rectangle r : rectangleList) {
drawRectangle(r,g);
System.out.println("Square");
}
// display current polygon
if (currentPolygon != null) {
drawPolygon(currentPolygon, g);
}
// display current square
if (currentRectangle != null) {
drawRectangle(currentRectangle, g);
}
}
/**
* displays a polygon without last stroke
*
* #param polygon
* to be displayed
*/
public void drawPolygon(ArrayList<Point> polygon, Graphics gr) {
Graphics2D g = null;
if (gr instanceof Graphics2D) {
g = (Graphics2D) gr;
}
else{ return; }
// set to red so I can see when it's being redrawn
g.setColor(Color.RED);
g.setStroke(new BasicStroke(3));
for (int i = 0; i < polygon.size(); i++) {
Point currentVertex = polygon.get(i);
if (i != 0) {
Point prevVertex = polygon.get(i - 1);
g.drawLine(prevVertex.getX(), prevVertex.getY(),
currentVertex.getX(), currentVertex.getY());
}
g.fillOval(currentVertex.getX() - 5, currentVertex.getY() - 5, 10,
10);
}
}
public void drawRectangle(Rectangle r, Graphics gr) {
Graphics2D g = null;
if (gr instanceof Graphics2D) {
g = (Graphics2D) gr;
}
else{ return; }
g.setStroke(new BasicStroke(3));
g.setColor(Color.BLUE);
g.drawLine(r.getX1(), r.getY1(), r.getX2(), r.getY1());
g.drawLine(r.getX1(), r.getY1(), r.getX1(), r.getY2());
g.drawLine(r.getX2(), r.getY2(), r.getX2(), r.getY1());
g.drawLine(r.getX2(), r.getY2(), r.getX1(), r.getY2());
System.out.println(r.getX1() + " " + r.getY1() + " " + r.getX2());
System.out.println("Drawn rectangle");
}
/**
* displays last stroke of the polygon (arch between the last and first
* vertices)
*
* #param polygon
* to be finished
*/
public void finishPolygon(ArrayList<Point> polygon) {
// if there are less than 3 vertices than nothing to be completed
if (polygon.size() >= 3) {
Point firstVertex = polygon.get(0);
Point lastVertex = polygon.get(polygon.size() - 1);
Graphics2D g = (Graphics2D) this.getGraphics();
g.setColor(Color.GREEN);
g.setStroke(new BasicStroke(3));
g.drawLine(firstVertex.getX(), firstVertex.getY(),
lastVertex.getX(), lastVertex.getY());
}
}
/**
* moves current polygon to the list of polygons and makes pace for a new
* one
*/
public void addNewPolygon() {
// finish the current polygon if any
if (currentPolygon != null) {
finishPolygon(currentPolygon);
polygonsList.add(currentPolygon);
}
currentPolygon = new ArrayList<Point>();
}
public void mouseClicked(MouseEvent e) {
if (!drawingRectangle) {
int x = e.getX();
int y = e.getY();
// check if the cursor is within image area
if (x > image.getWidth() || y > image.getHeight()) {
// if not do nothing
return;
}
Graphics2D g = (Graphics2D) this.getGraphics();
// if the left button than we will add a vertex to poly
if (e.getButton() == MouseEvent.BUTTON1) {
g.setColor(Color.GREEN);
if (currentPolygon.size() != 0) {
Point lastVertex = currentPolygon
.get(currentPolygon.size() - 1);
g.setStroke(new BasicStroke(3));
g.drawLine(lastVertex.getX(), lastVertex.getY(), x, y);
}
g.fillOval(x - 5, y - 5, 10, 10);
currentPolygon.add(new Point(x, y));
System.out.println(x + " " + y + " polygon point");
}
}
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
if (drawingRectangle) {
currentRectangle = new Rectangle(arg0.getX(), arg0.getY(),
arg0.getX(), arg0.getY(), Color.BLACK);
}
}
public void mouseReleased(MouseEvent arg0) {
if (drawingRectangle) {
rectangleList.add(new Rectangle(currentRectangle.getX1(),
currentRectangle.getY1(), arg0.getX(), arg0.getY(),
currentRectangle.getColor()));
System.out.println(currentRectangle.getX1() + " "
+ currentRectangle.getY1() + " " + arg0.getX() + " "
+ arg0.getY() + " rectangle point");
// unnecessary when mouseDragged calls paintComponent directly?
/*drawRectangle(new Rectangle(currentRectangle.getX1(),
currentRectangle.getY1(), arg0.getX(), arg0.getY(),
currentRectangle.getColor()));*/
currentRectangle = null;
drawingRectangle = false;
}
}
public void mouseDragged(MouseEvent arg0) {
if (drawingRectangle) {
currentRectangle = new Rectangle(currentRectangle.getX1(),
currentRectangle.getY1(), arg0.getX(), arg0.getY(),
currentRectangle.getColor());
System.out.println(currentRectangle.getX1() + " "
+ currentRectangle.getY1() + " " + arg0.getX() + " "
+ arg0.getX() + " " + "Dragging");
repaint();
// It works better using this instead on repaint()
// Graphics g = this.getGraphics();
// paintComponent(g);
}
}
public void mouseMoved(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}
I'm attempting to teach myself some Java AWT and simple graphics but have had difficulty with using the contains and intersects method.
The problem is that it seems to detect the collision several pixels up from where the mouse is clicked and the actual shape.
GameDemo.java
package uk.co.mhayward.games.sandbox;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameDemo extends JFrame {
GamePanel gamePanel = new GamePanel();
public static void main(String[] args) {
new GameDemo();
}
public GameDemo() {
super("click me");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(gamePanel);
this.setSize(200, 200);
this.setVisible(true);
this.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {}
public void mousePressed(MouseEvent e) {
System.out.println(e.getPoint().toString());
if (gamePanel.shape.contains(e.getPoint())) {
System.out.println("IN");
} else {
System.out.println("out");
}
}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
});
}
public class GamePanel extends JPanel {
Shape shape = new RegularPolygon(100, 100, 100, 6, 0);
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(1));
g2d.setPaint(Color.WHITE);
g2d.fill(shape);
g2d.setPaint(Color.BLACK);
g2d.draw(shape);
}
}
public static class RegularPolygon extends Polygon {
private static final long serialVersionUID = 8828151557263250246L;
/**
* #param x
* #param y
* #param r
* #param vertexCount
*/
public RegularPolygon(int x, int y, int r, int vertexCount) {
this(x, y, r, vertexCount, 0);
}
/**
* #param x
* #param y
* #param r
* #param vertexCount
* #param startAngle
* 360deg = PI
*/
public RegularPolygon(int x, int y, int r, int vertexCount, double startAngle) {
super(getXCoordinates(x, y, r, vertexCount, startAngle),
getYCoordinates(x, y, r, vertexCount, startAngle),
vertexCount);
}
protected static int[] getXCoordinates(int x, int y, int r, int vertexCount, double startAngle) {
int res[] = new int[vertexCount];
double addAngle = 2 * Math.PI / vertexCount;
double angle = startAngle;
for (int i = 0; i < vertexCount; i++) {
res[i] = (int) Math.round(r * Math.cos(angle)) + x;
angle += addAngle;
}
return res;
}
protected static int[] getYCoordinates(int x, int y, int r, int vertexCount, double startAngle) {
int res[] = new int[vertexCount];
double addAngle = 2 * Math.PI / vertexCount;
double angle = startAngle;
for (int i = 0; i < vertexCount; i++) {
res[i] = (int) Math.round(r * Math.sin(angle)) + y;
angle += addAngle;
}
return res;
}
}
}
EDITS
04/Jan/12 - changed Override paint(g) to paintComponent(g) - still doesn't detect collision properly.
05/Jan/12 - created a SSCCE to more easily demonstrate the problem.
For reference, this short example examines a transformed Polygon using the contains() method. The result seems correct to the nearest pixel. You might compare it to your result.
Listen on the panel, rather than the JFrame. The offset you are seeing is from the titlebar.
you have override paintComponent(Graphics g) for Swing JComponents instead of method valid for AWT paint(Graphics g), more in tutorials 2D Graphics and Performing Custom Painting
you have override paintComponent(Graphics g) for Swing JComponents instead of method valid for AWT paint(Graphics g)