I'm making a drawing board, and I have a few questions.
Whenever I try to draw on it, it doesn't automatically update. Ad I usually have to resize the screen for it to update.
How can I do something like a mouseDragged function, in which i can continually get the x and y coords?
Here is the code:
import java.awt.geom.*;
class griddedInput extends JComponent implements MouseListener
{
int SIZE = 10;
int scSize = 300;
int sSize = scSize/SIZE;
boolean [][] grid = new boolean[sSize][sSize];
public griddedInput(boolean grid[][])
{
grid=grid;
setPreferredSize(new Dimension(scSize,scSize));
addMouseListener(this);
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
int x, y;
for(y = 0; y < sSize; y ++) {
for(x = 0; x < sSize; x ++) {
if(grid[y][x])
g2.setColor(Color.BLACK);
else
g2.setColor(Color.WHITE);
g2.fillRect((x * SIZE), (y * SIZE), sSize, sSize);
}
}
}
#Override
public void mouseClicked(MouseEvent e) {
int squareX = (int)e.getX() / SIZE;
int squareY = (int)e.getY() / SIZE;
grid[squareY][squareX] = !grid[squareY][squareX];
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
}
You'll want to call repaint() on the drawing component any time you want to suggest to the JVM that it be painted -- most likely in your MouseListener method(s).
e.g.,
#Override
public void mouseClicked(MouseEvent e) {
int squareX = (int)e.getX() / SIZE;
int squareY = (int)e.getY() / SIZE;
grid[squareY][squareX] = !grid[squareY][squareX];
repaint();
}
To speed up repainting, you can also call the overload method that allows you to repaint a select rectangle of your GUI, but I'll bet that you don't need to do that for this GUI.
You'll also want to be a little less "creative" with your code indentation if you want others to better be able to understand it and help you.
Edit
Regarding:
2.How can I do something like a mouseDragged function, in which i can continually get the x and y coords?
Also add a MouseMotionListener. It can be the same class, and in fact usually I use an anonymous inner class for this, one that extends MouseAdapter, and one whose single instance I use for both MouseListener and MouseMotionListener. I have examples of using this in several posts in this very forum.
I would structure this a bit differently:
private BufferedImage bi = new BufferedImage(getWidth(), getHeight());
private Graphics2D big = bi.createGraphics();
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(bi, 0, 0, this);
}
#Override
public void mouseClicked(MouseEvent e) {
int squareX = (int)e.getX() / SIZE;
int squareY = (int)e.getY() / SIZE;
boolean b = !grid[squareY][squareX];
grid[squareY][squareX] = b;
if(b)
big.setColor(Color.BLACK);
else
big.setColor(Color.WHITE);
big.fillRect((x * SIZE), (y * SIZE), sSize, sSize);
repaint();
}
The mouseClicked performs a single fillRect and the paintComponent a single drawImage. Compare this with the original code that performs 900 fillRects on every repaint.
You will also need to detect changes to the size and recreate the BufferedImage at that time.
Related
I can't animate spaceship.png nor update the image to animate it.
Need to animate it using paint method (could animate it using ImageIcons and labels) in order to rotate.
So, rewritten it but now i can't even update its' current position.
How can i make animation with a key listener ?
Why can't i animate the image that's being painted?
public class MyPanel extends JPanel implements KeyListener{
final int PANEL_WIDTH = 1920;
final int PANEL_HEIGHT = 1080;
Image spaceship;
Image spaceship2;
Image spaceship3;
Image Alien1;
Image Alien2;
Image AlienBoss;
Image Asteroid1;
Image Asteroid2;
Timer timer;
int xVelocity = 1;
int yVelocity = 10;
int x = 0;
int y = 0;
MyPanel(){
this.setPreferredSize(new Dimension(PANEL_WIDTH,PANEL_HEIGHT));
this.setBackground(new Color(0x080e12));
this.addKeyListener(this);
spaceship = new ImageIcon("D:\\Users\\Xigmatek\\eclipse-workspace\\SpaceGameRev\\src\\spaceship.png").getImage();
}
public void paint(Graphics g){
super.paint(g);
Graphics2D g2D = (Graphics2D) g;
g2D.drawImage(spaceship, x, y, null);
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
switch(e.getKeyCode()) {
case 87: x -=xVelocity;
repaint();
break;
case 83: x +=xVelocity;
repaint();
break;
}
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
}
Sorry if i have pasted too much of code but am kind of newbie so, sorry :/
The problem with the JPanel you created is that it is not focusable, i.e it does not register key events, it can be fixed with a simple line of code setFocusable(true), and I noticed that you update the frame only once the space ship moves, but it is always a good practice to update it once every 60'th of a second, or any frames per second, you can do this by implementing ActionListenerand passing the class to a Timer.
and I changed the keys to their respective variable(VK_W and VK_S)
Here is the updated code
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
// in order to repeat after set delay, the class shall implement Action Listener and pass it to a timer(shown below)
public class MyPanel extends JPanel implements KeyListener, ActionListener {
final int PANEL_WIDTH = 1920;
final int PANEL_HEIGHT = 1080;
Image spaceship;
Image spaceship2;
Image spaceship3;
Image Alien1;
Image Alien2;
Image AlienBoss;
Image Asteroid1;
Image Asteroid2;
Timer timer;
int xVelocity = 1;
int yVelocity = 10;
int x = 0;
int y = 0;
MyPanel(){
this.setPreferredSize(new Dimension(PANEL_WIDTH,PANEL_HEIGHT));
this.setBackground(new Color(0x080e12));
this.addKeyListener(this);
// set this JComponent focusable so that it can register keyEvents
setFocusable(true);
spaceship = new ImageIcon("res\\spaceship.png").getImage();
// pass this class to the timer to repeat after 1000/60 eth of a second(60 fps)
Timer timer = new Timer(1000/60, this);
timer.start();
}
public void paint(Graphics g){
super.paint(g);
Graphics2D g2D = (Graphics2D) g;
g2D.drawImage(spaceship, x, y, null);
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
// I changed 87 and 83 to their respective VK value
switch(e.getKeyCode()) {
case KeyEvent.VK_W: x -=xVelocity;
repaint();
break;
case KeyEvent.VK_S: x +=xVelocity;
repaint();
break;
}
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
// Override this method from ActionListener
#Override
public void actionPerformed(ActionEvent e){
repaint();
}
}
Please watch this video, I demonstrated the output in this video.
By the way, you used w and s for horizontal movement(gamedev advice).
I'm following a Java 2D tutorial and have got stuck on a program written to display a rectangle and ellipse which should fade in colour when clicked ...only nothing happens and I cannot work out why. I modified the original code slightly by passing the Jpanel instance to the HitTest class constructor. By tracing through with the debugger I've established that the program does detect a mouse click in the displayed shape and creates a thread to adjust the colour - but for some reason the colour does not adjust and stays the same. I thought the problem might be to do with where the repaint method is being called but not sure? Thanks in advance for any help received.
Here's some of the code:
public class Surface extends JPanel {
private Ellipse2D ellipse;
private float alpha_ellipse; //This variable will be used to control the transparency of the ellipse
public Surface() {
initSurface();
}
private void initSurface() {
addMouseListener(new HitTestAdapter(this)); //pass instance of surface to mouse adapter class
ellipse = new Ellipse2D.Float(120f, 30f, 60f, 60f);
alpha_ellipse = 1f;
}
private void doDrawing(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setPaint(new Color(50, 50, 50));
RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHints(rh);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, // set the transparency using
alpha_ellipse)); //alpha_ellipse value calculated in a thread
g2d.fill(ellipse);
g2d.dispose();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
public Ellipse2D getEllipse() {
return ellipse;
}
public float getAlphaEllipse() {
return alpha_ellipse;
}
}
public class HitTestAdapter extends MouseAdapter
implements Runnable{
private RectRunnable rectAnimator;
private Thread ellipseAnimator;
private Surface surf;
float alpha_ellipse;
public HitTestAdapter(Surface surface) { // I passed surface as a parameter to get to Surface class methods from this class
surf = surface;
}
#Override
public void mousePressed(MouseEvent e) {
int x = e.getX();
int y = e.getY();
if (surf.getEllipse().contains(x, y)) { //if we press inside the ellipse
ellipseAnimator = new Thread(this); // a new thread is created
ellipseAnimator.start();
}
}
#Override
public void run() {
alpha_ellipse= surf.getAlphaEllipse();
while (alpha_ellipse >= 0) {
surf.repaint();
alpha_ellipse += -0.01f;
if (alpha_ellipse < 0) {
alpha_ellipse = 0;
}
try {
Thread.sleep(50);
} catch (InterruptedException ex) {
Logger.getLogger(Surface.class.getName()).log(Level.SEVERE,
null, ex);
}
}
}
}
See the commented changes needed in run:
#Override
public void run() {
alpha_ellipse= surf.getAlphaEllipse();
while (alpha_ellipse >= 0) {
surf.repaint();
//if alpha_ellipse becomes negative it will never become positive again
//so you can simply stop
alpha_ellipse += -0.01f;
if (alpha_ellipse < 0) {
alpha_ellipse = 0;
}
//need to set the new alpha in Surface. Add a setter to Surface
//a change to swing gui must be done on the EDT hence the use of invokeLater
SwingUtilities.invokeLater(()->surf.setAlphaEllipse(alpha_ellipse));
try {
Thread.sleep(50);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
Side note: consider using Swing Timer instead of a thread.
I am designing a picture lab project for school and I can't figure out how to modify the rgb of pixels. My project is an eye test game where players choose the one color that is different from the rest. Should I modify the pixels of the background or of a blank photo? Also, how do I implement mouseClickedAction (given method that runs when mouse is clicked)?
Here's some bare stuff I have so far:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.image.ImageObserver;
import java.util.ArrayList;
public class EagleEye extends FlexiblePictureExplorer {
public static Picture background = new Picture(1003,1003);
//settings
private int gameDimensions = 4;
private int difficulty = 30;
private final int statsHeight = 80;
public EagleEye(DigitalPicture Picture) {
super(background);
setTitle("Eagle Eye");
int xGrid = gameDimensions;
int yGrid = gameDimensions;
int r1 = (int)(Math.random() * gameDimensions + 1);
int r2 = (int)(Math.random() * gameDimensions + 1);
colorSetter(xGrid,yGrid);
}
private void colorSetter(int x,int y) {
for (int i=0;i<x;i++) {
for (int j=0;j<y;j++) {
fillBox(i,j);
}
}
}
private void fillBox(int x, int y) {
int r = (int)(Math.random() * 255 + 1);
int g = (int)(Math.random() * 255 + 1);
int b = (int)(Math.random() * 255 + 1);
int x1;
int x2;
if (x==0) {
x1 = 0;
x2 = 250;
}
else if (x==1) {
x1 = 252;
x2 = 501;
}
else if (x==2) {
x1 = 503;
x2 = 752;
}
else {
x1 = 754;
x2 = 1003;
}
int y1;
int y2;
if (y==0) {
y1 = 0;
y2 = 250;
}
else if (y==1) {
y1 = 252;
y2 = 501;
}
else if (y==2) {
y1 = 503;
y2 = 752;
}
else {
y1 = 754;
y2 = 1003;
}
int rgb = calculateColors(r,g,b);
for (int i=x1;i<=x2;i++) {
for (int j=y1;j<=y2;j++) {
background.setBasicPixel(x1,y1,rgb);
}
}
setImage(background);
}
private int calculateColors(int r, int g, int b) {
int r1 = r * 65536;
int g1 = g * 256;
int b1 = b;
return r1 + g1 + b1;
}
private void drawStats(Picture img){
Picture statsImg = new Picture(statsHeight, imageWidth);
Graphics g = statsImg.getGraphics();
g.setFont(new Font("Times New Roman", Font.PLAIN, 16));
g.setColor(Color.blue);
}
private void updateImage() {
}
public void mouseClickedAction(DigitalPicture pict, Pixel pix) {
}
private void endGame() {
}
public boolean imageUpdate(Image arg0, int arg1, int arg2, int arg3, int arg4, int arg5) {
// TODO Auto-generated method stub
return false;
}
public static void main(String[] args) {
Picture white = new Picture(100,100);
EagleEye game = new EagleEye(white);
}
}
I think the easiest way that you will find for drawing and graphics will not be drawing to an image, but instead drawing directly to the JPanel. If you have a class that extends JPanel, you can implement
public void paintComponent(Graphics g) {
//Code to draw whatever you like, e.g.
g.setColor(new Color(255, 255, 255));
g.drawRect(0, 0, width, height);
}
With this, you will not have to worry about handling images,
If you would still like to use images, you can use the BufferedImage, which has great docs explaining how to use it:
https://docs.oracle.com/javase/tutorial/2d/images/drawonimage.html
(You will still need to display these images, most likely using the paintComponent method above anyways, and if you want to draw in a loop, you will have to put that loop on another thread, be aware)
As for the mouseClicked, you will need to implement a MouseListener, which is quite simple:
Create a MouseListener object in your class, you will have to implement a number of methods inside it, most of which will likely go unused.
Somewhere in your code (probably the constructor) you will need to add the MouseListener to whatever component you want to wait for the clicks (probably the panel you are drawing on)
When that component is clicked on, the mouseClicked method will be called and from there you can do whatever you need to, like calling the other methods you have there to handle mouse clicks.
The MouseEvent object in the listener has all the useful information yu will need, like it's position (relative to the component you added the listener to)
public class YourClass {
public YourClass() {
this.addMouseListener(ml);
}
//code
private MouseListener ml = new MouseListener() {
#Override
public void mouseClicked(MouseEvent arg0) {
// TODO Auto-generated method stub
System.out.println(arg0.getX() + ", " + arg0.getY());
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}
}
I need to make a program that will draw multiple circles/squares, and when they are clicked, the colours change to another random colour. I am unsure how to do this. At the moment I have one circle on a JPanel which has a mouse listener to repaint when the panel is clicked within the bounds of the circle (although this creates a rectangular area to click in, not circular) and I need to extend this to add more shapes that have their own area to be clicked. Any help appreciated. Thanks.
public class CircleGUI extends JFrame {
int ovalWidth = 100;
int ovalHeight = 100;
int ovalX = 100;
int ovalY = 100;
public CircleGUI() {
super("Circle GUI");
this.setSize(500, 500);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
go();
this.setVisible(true);
}
public void go() {
CPanel panel = new CPanel();
Container container = getContentPane();
container.add(panel);
panel.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
int radius = ovalWidth / 2;
int centerX = ovalX + radius;
int centerY = ovalY + radius;
if (((e.getX() >= centerX - radius && e.getX() <= centerX + radius) && e.getX() >= centerX - radius
&& e.getX() <= centerX + radius)) {
repaint();
}
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
});
}
public class CPanel extends JPanel {
public void paint(Graphics g) {
// random colour
g.setColor(new Color(Math.round(Math.random()), Math.round(Math.random()), Math.round(Math.random())));
g.fillOval(ovalX, ovalY, ovalWidth, ovalHeight);
}
}
}
I need to extend this to add more shapes that have their own area to be clicked
You need to keep a List of Objects that you want to paint. That object would contain information like shape and color.
In your paintComponent() method you iterate through the List and paint each shape.
You then add a MouseListener to your panel. When the mouse is clicked you then iterate through the List to find a shape that contains the point that was generated and you update the color of that object and then repaint the panel.
Check out the Draw On Component example from Custom Painting Approaches for an example of this approach. The example only paints Rectangles but should get you started.
I'm trying to write a program that draws a new square every second. This is my code for my JPannel class. I have two other classes but I beleive they are irrelivant to my question. One has the main method where it creates an object of the other class that contains the JFrame. I just cant figure out how to get the timer to work and how it works.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Drawing extends JPanel implements ActionListener{
Drawing(){
super();
setBackground(Color.WHITE);
startTimer();
}
public void startTimer() {
Timer timer = new Timer(1000, this);
timer.start();
}
public void paintComponent(Graphics g) {
int width = getWidth();
int height = getHeight();
super.paintComponent(g);
g.setColor(Color.BLUE);
for(int x = 0; x < 999; x ++) {
for(int y = 0; y < 999; y ++) {
g.drawRect(0,0, x, y);
}
}
}
public void actionPerformed(ActionEvent e) {
repaint();
}
}
Get rid of the for loops in your paintComponent method. Note that painting methods should not change object state. Instead the timer's actionPerformed method gets repeatedly called -- advance your counter or x/y variables there, and then call repaint().
e.g.,
private int x;
private int y;
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
// RECT_WIDTH etc are constants
g.drawRect(x, y, RECT_WIDTH, RECT_HEIGHT);
}
public void actionPerformed(ActionEvent e) {
int width = getWidth();
int height = getHeight();
// code here to change the object's state -- here to change
// location of the rectangle
x++;
y++;
// TODO: check that x and y are not beyond bounds and if so,
// then re-position them
repaint();
}