Graphics loop glitches, how to fix? - java

I've been trying to make this work so that there are 20 boxes each with 3 different sizes and 3 different colours that chosen at random, but i cant make them come out at different times and they just glitch into eachother and the colours are glitching together and stuff like that, anyone know how to fix it? Heres what i got so far:
import java.awt.*;
import javax.swing.*;
public class testwork extends JPanel { //JPanel is a class
int l = 0;
private int x = 10;
private int y = 500;
private void move()
{
x++;
}
boolean red = false;
boolean blue = false;
boolean green = false;
#Override
public void paint(Graphics g) { //JPanel is a class defined in
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Color rectColor = new Color(0, 66, 89);
g2d.setColor(rectColor);
//belt
g2d.setColor(Color.lightGray);
g2d.fillRect(0,450,1500,200);
g2d.fillRect(700,0,200,1000);
g2d.setColor(Color.orange);
for (int i = -10000; i<10000; i=i+50) {
int m= i++;
g2d.fillRect(m, 450, 25, 200);
}
g2d.setColor(Color.DARK_GRAY);
g2d.fillRect(700, 450, 200, 200);
//boxes
while (l<=20) {
if (Math.random() < 0.5)
{g2d.setColor(Color.RED);;}
else if (Math.random() < 0.5) {g2d.setColor(Color.GREEN);}
else {g2d.setColor(Color.BLUE);}
if (Math.random() < 0.5)
{g2d.fillRect(x,y,50,50);}
else if (Math.random() < 0.5) {g2d.fillRect(x,y,50,100);}
else {g2d.fillRect(x,y,100,50);}
l++;
}
}
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame("Frame"); //Add our JPanel to the frame
frame.add(new attempt());//instantiate a new object
frame.setSize(1500, 1000);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
testwork p = new testwork();
frame.add(p);
while (true)
{
p.move(); //Updates the coordinates
p.repaint();
Thread.sleep(10); //Pauses for a moment
}}
}

Unfortunately, you are doing a number of things incorrectly.
Override paintComponent and not paint.
Don't use Thread.sleep. Use a Swing timer and an ActionListener
You are doing too much in the painting method. All event handling including calls to repaint() is done on the Event Dispatch Thread(EDT). So all your updating are done inside of paintComponent so only the last painted objects will be shown when you exit. Update your coordinates, data structure and anything else that needs to be painted outside of your paint method.
Put your boiler plate code inside your Testwork class constructor. Here is an example.
public Testwork() {
setPreferredSize(new Dimension(1000, 700));
Timer timer = new Timer(0, this);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
// sizes the frame and jpanel and organizes the components.
frame.pack();
// centers the window in the screen
frame.setLocationRelativeTo(null);
// sets the delay in milliseconds
timer.setDelay(100);
// starts the timer
timer.start();
}
Here would be your actionListener code.
public void actionPerformed(ActionEvent ae) {
// update any variables that need to be used int he
// paint routine here. That means if you want to move something
// update the coordinates here and then use them in the paint method.
}
when you start up, your app, do it like this
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new Testwork());
}
There is much more to painting. I recommend checking out Custom Painting in the Java tutorials. Here is another example on this (SO) site.

Related

Multiple panels / canvas

I am looking for a way to dinamiclly switch between panels / between a panel or a canvas.
More specific: I am developing a game. In my code there is a class that extends canvas and implements
Runnable, and in the constructor of Game, it creates a new instance of a class called window. That is window class:
public class Window extends Canvas {
private static final long serialVersionUID = -299686449326748512L;
public static JFrame frame = new JFrame();
public Window(int width, int height, String title, Game game) {
// JFrame frame = new JFrame();
frame.setPreferredSize(new Dimension(width, height));
frame.setMaximumSize(new Dimension(width, height));
frame.setMinimumSize(new Dimension(width, height));
frame.setTitle(title);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.add(game);
frame.setVisible(true);
game.start();
}
}
I want to be able to remove game from the frame, activate another panel, and stop the execution of Game.
I have already tried:
game.stop();
Window.frame.remove(game);
but it makes the program to crash. Those are start() & stop() methods:
/**
* starts the game.
*/
public synchronized void start() {
thread = new Thread(this);
thread.start();
running = true;
}
/**
* tries to stop the game.
*/
public synchronized void stop() {
try {
thread.join();
running = false;
} catch (Exception e) {
e.printStackTrace();
}
}
My main goal is to be able to play a cutscene if some event happend and I am trying to use vlcj for that purpose. If anyone has an idea that will allow me to execute this goal that would be great too.
I've made an example, doing what I think you want without a card layout, and using a thread. I think this is a proof of concept, that what your asking is possible. Below, I will include a few things I would do to improve it.
import javax.swing.*;
import java.awt.*;
public class SwapCards{
Thread gameLoop;
volatile boolean running = false;
double x = 0;
double y = 0;
double theta = 0;
JFrame frame = new JFrame("swapped");
Canvas gamePanel = new Canvas(){
public void paint(Graphics g){
super.paint(g);
g.setColor(Color.BLACK);
g.drawOval((int)x, (int)y, 25, 25);
}
};
Canvas nonGame = new Canvas(){
public void paint(Graphics g){
super.paint(g);
g.setColor(Color.BLUE);
g.fillRect(0,0,200, 200);
}
};
public void step(){
x = 100 + 50*Math.sin( theta );
y = 100 + 50*Math.cos( theta );
theta += 0.02;
if(theta > 6.28) theta = 0;
}
public void startGameLoop(){
frame.remove(nonGame);
frame.add(gamePanel, BorderLayout.CENTER);
frame.validate();
running = true;
gameLoop = new Thread(()->{
while(running){
step();
gamePanel.repaint();
try{
Thread.sleep(30);
}catch (Exception e){
running = false;
throw new RuntimeException(e);
}
}
});
gameLoop.start();
}
public void stopGameLoop(){
frame.remove(gamePanel);
frame.add(nonGame, BorderLayout.CENTER);
running = false;
try{
gameLoop.join();
} catch(Exception e){
throw new RuntimeException(e);
}
}
public void buildGui(){
JButton button = new JButton("action");
button.addActionListener( evt->{
if(!running){
startGameLoop();
} else{
stopGameLoop();
}
});
frame.add(nonGame, BorderLayout.CENTER);
frame.add(button, BorderLayout.SOUTH);
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args){
EventQueue.invokeLater( new SwapCards()::buildGui );
}
}
First off, Canvas is somewhat outdated, use a JPane and override paintComponent. That gives you more access to the power of swing.
In this example I am doing trivial work so the thread is absolutely overkill I could replace it with a javax.swing.Timer.
Timer timer = new Timer(30, evt->{
step();
gamePanel.repaint();
});
Then in the start and stop methods, I just call timer.start() or timer.stop() respectively.
Using a CardLayout makes it a bit clearer what you want to do, plus it has methods for navigating the cards. Eg. If you have a cut scene with a series of components you want to show, you can use cardLayout.next(parent).
When we create the layout:
cards = new CardLayout();
swap = new JPanel(cards);
swap.add(gamePanel, "game");
swap.add(nonGame, "nogame");
cards.last(swap);
frame.add(swap, BorderLayout.CENTER);
This will add the cards to swap and make it show "nogame". Then in the start/stop methods we just switch to the respective card.
cards.show(swap, "game");

The Animation is not showing

I'm trying this very simple code. It runs but doesn't show the animation. I'm new to animations, so I don't know what I'm missing.
package sample;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Sample extends JPanel implements ActionListener {
Timer tm = new Timer(5, this);
int x = 0, Velx = 5;
public void paint(Graphics g) {
g.setColor(Color.RED);
g.fillRect(x, 30, 50, 50);
tm.start();
}
public void actionPerformed(ActionEvent e) {
x = x + Velx;
repaint();
}
public static void main(String[] args) {
Sample X = new Sample();
JFrame a = new JFrame();
a.setTitle("Rectangle RED");
a.setSize(500,500);
a.setVisible(true);
}
}
Sample X = new Sample();
X is never added to the frame. See first tip (the bold part) for how to add X to the frame.
Other tips:
Sample should #Override the getPreferredSize() method to return a sensible size for the canvas. Then we can dispense with a.setSize(500,500); and instead a.add(X); a.pack(); to get the frame to be the exact right size to display the rendering.
The Timer should be started in some place other than the paint methods! I'd go for the constructor.
Custom painting in any JComponent should be done in the paintComponent(Graphics) method.
In all custom painting, we should immediately call the super method to ensure that previous drawings are erased by painting the BG and border of the container.
Please learn common Java nomenclature (naming conventions - e.g. EachWordUpperCaseClass, firstWordLowerCaseMethod(), firstWordLowerCaseAttribute unless it is an UPPER_CASE_CONSTANT) and use it consistently.
JFrame a = new JFrame(); a.setTitle("Rectangle RED"); could be shortened to JFrame a = new JFrame("Rectangle RED");

How to add start button to Simon Says Game in Java?

I want to draw an inscribed circle. A circle that is empty with no fill but making it have a full stroke. My code :
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package simon;
import java.awt.*;
import java.awt.event.*; //This allows us to detect user input
import java.awt.image.BufferedImage;
import javax.swing.*; //This allows us to use graphical elements, colors, etc.
/**
*
* #author User
*/
public class SimonA {
//First, we create all the elements of our program
//Here are the variables...
public static int score = 0;
public static int color = 0; //0 = yellow, 1 = red, 2 = blue, 3 = green
public static Boolean flash = false; //This is used to make the panel blink
public static Boolean running = false;
//Here are the widgets (objects)....
public static JFrame frame = new JFrame();
public static JPanel buttons = new JPanel();
public static JPanel buttons1 = new JPanel();//These buttons will all be square (the default). Different packages can be used to change the shape
public static JPanel controls = new JPanel();
public static JButton red = new JButton();
public static JButton yellow = new JButton();
public static JButton green = new JButton();
public static JButton blue = new JButton();
public static JButton toggle = new JButton("Start"); //Click this button to see a sample flash
public static JLabel scoreTxt = new JLabel("Score: " + score, SwingConstants.CENTER); //This object (a label element) displays the score variable's value
public static Timer blink = new Timer(600,new Ticker()); //This is used to time the duration of the flash
public static BufferedImage img = new BufferedImage(400, 400, BufferedImage.TYPE_INT_ARGB);
public static Graphics2D g = img.createGraphics();
public static JLabel space = new JLabel();
JPanel panelBgImg = new JPanel() {
public void paintComponent(Graphics g){
g.setStroke(new BasicStroke(8));
g.fillOval(0, 0, 400, 400);
}
};
/*Timers are important for any program in which something "moves" at set durations. In this case, every tenth of
a second, the timer will generate an event. In this case, we are using it to determine that the active tile
will flash for 600ms, or 6/10 of a second. Obvously, then, 1000 makes the timer generate an event once-per-second. */
public static void main (String[] args)
{
frame.setBackground(Color.gray);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //This means that when we close the window with [x] the program ends
frame.setSize(new Dimension(400,400));
frame.setForeground(Color.black);
frame.setTitle("Simon");
frame.setLayout(new BorderLayout()); //Remember this from the LayoutManager test?
buttons.setLayout(new GridLayout(1,2));
buttons1.setLayout(new GridLayout(1,2));//The buttons are placed in this panel, which is set as a 2x2 grid
yellow.setBackground(Color.yellow);
yellow.addActionListener(new YellowPressed()); //This triggers the "procedure" that runs when the yellow button is pressed
yellow.setPreferredSize(new Dimension(200,200));
//Note, the other buttons will take their cue for size from the above statement, since they are all in the same grid
//We do not need to specify dimensions again
red.setBackground(Color.red);
red.addActionListener(new RedPressed());
red.setPreferredSize(new Dimension(200,200));
blue.setBackground(Color.blue);
blue.addActionListener(new BluePressed());
green.setBackground(Color.green);
green.addActionListener(new GreenPressed());
green.setPreferredSize(new Dimension(200,200));
//Adding the four buttons to the panel called "buttons"
buttons1.add(yellow);
buttons.add(red);
buttons.add(green);
buttons1.add(blue);
//The control panel on the bottom is a gride of one row and two columns
controls.setLayout(new GridLayout(2,1));
controls.add(scoreTxt);
controls.add(toggle);
toggle.addActionListener(new ToggleOn());
//We now add the panels to the frame according to the border layout
frame.add(buttons,BorderLayout.NORTH);
frame.add(controls,BorderLayout.CENTER);
frame.add(buttons1,BorderLayout.SOUTH);
//This .pack() method removes any excess whitespace around your elements
//Sometimes it results in a better look, and sometimes not.
frame.pack();
frame.setVisible(true);
}
public static class ToggleOn implements ActionListener {
public void actionPerformed(ActionEvent event)
{
//This toggles the main button between Start and Stop
//If it's running, stop it from running
//If it's not running, start it running
running = !running;
if (running)
{
toggle.setText("Stop");
//To demonstrate how the code might work, a sample flash
color = 3; //Change the color to "active"
blink.start(); //Starts the 6/10 second timer
score += 10;
scoreTxt.setText("Score: " + score);
}
else
{
toggle.setText("Start");
}
}
}
public static class YellowPressed implements ActionListener {
public void actionPerformed(ActionEvent event)
{
//Right now, the buttons just print to the screen
//The "real" program would implement other instructions here
System.out.println("Yellow");
}
}
public static class RedPressed implements ActionListener {
public void actionPerformed(ActionEvent event)
{
System.out.println("Red");
}
}
public static class BluePressed implements ActionListener {
public void actionPerformed(ActionEvent event)
{
System.out.println("Blue");
}
}
public static class GreenPressed implements ActionListener {
public void actionPerformed(ActionEvent event)
{
System.out.println("Green");
}
}
public static class Ticker implements ActionListener {
public void actionPerformed(ActionEvent event)
{
//When the timer triggers, if a button is active,
//set it back to its original color 6/10 of a second later
flash = !flash;
if (flash) //If the button is to be lit, turn it white
{
if (color == 0)
yellow.setBackground(Color.white);
else if (color == 1)
red.setBackground(Color.white);
else if (color == 2)
blue.setBackground(Color.white);
else if (color == 3)
green.setBackground(Color.white);
}
else //Otherwise, change it back to its original color
{
if (color == 0)
yellow.setBackground(Color.yellow);
else if (color == 1)
red.setBackground(Color.red);
else if (color == 2)
blue.setBackground(Color.blue);
else if (color == 3)
green.setBackground(Color.green);
blink.stop();
}
}
}
}
Here is what I am using to create the circle, for some reason it does not create the circle:
JPanel panelBgImg = new JPanel() {
public void paintComponent(Graphics g){
g.setStroke(new BasicStroke(8));
g.fillOval(0, 0, 400, 400);
}
};
First of all, you're never (in the code provided) adding the panelBgImg to any component, so nothing is going to show it (you have to add it to a frame or another panel somewhere).
You should also change your paintComponent method though...
JPanel panelBgImg = new JPanel() {
public void paintComponent(Graphics g){
//Call super method to draw background
super.paintComponent(g);
//Cast to Graphics2D to use setStroke
Graphics2D g2d = (Graphics2D) g;
//Set the color of the circle you want to draw
g2d.setColor(Color.RED);
g2d.setStroke(new BasicStroke(8));
//g2d.fillOval(0, 0, 400, 400);
//Use drawOval instead of fillOval (otherwise it will be filled with the color)
g2d.drawOval(0, 0, 400, 400);
}
};

Display Two Classes Java

so I am new to designing graphics in Java I was wondering if anyone could help me here. I have two classes and I want to display both of them at the same time in a JFrame. But only one or the other get displayed.
public class Tutorial extends JPanel implements ActionListener {
Background bc = new Background();
Timer tm = new Timer(5,this);
int x =0, velX = 2;
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(x, 30, 50, 30);
tm.start();
}
#Override
public void actionPerformed(ActionEvent e) {
if(x<0 || x>550){
velX = -velX;
}
x = x+ velX;
repaint();
}
public static void main(String [] args){
Background bc = new Background();
Tutorial t = new Tutorial();
JFrame jf = new JFrame();
jf.setTitle("Tutorial");
jf.setSize(600,400);
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.add(t);
jf.add(bc);
}
My second class
public class Background extends Canvas {
public void paint(Graphics g){
g.setColor(Color.GREEN);
g.fillRect(0,0,600,125);
g.fillRect(0,250,600,125);
g.setColor(Color.black);
g.fillRect(0,125,600,125);
}
For some reason I can only get either Background to be displayed or Tutorial?
Can anyone point me in the right direction or tell me where I am going wrong. I want to be able to display multiple things like these classes in the one window
The default layout manager of a JFrame is a BorderLayout.
By using the single-argument JFrame.add() function, you're adding both of the components to the BorderLayout.CENTER portion of your JFrame. This means that you'll only see one of the components.
The solution is to either use a different layout manager, or to add the components to different sections of your BorderLayout.
More info here: http://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html

GUI Using JFrame, & JPanel drawing custom shapes

Pretty much I create a Shape class, with Rectangle, Circle, Triangle extending Shape, and a Square class extending Circle. I have the code working with this main class, but I'm having a tough time converting it into GUI because I'm not sure how to do number 3 to make this come together and how to make a g.drawOval(with given x,y & radius) and draw triangle(given x,y, base and height).
Project6 class will have to extend the JFrame class
Project6 constructor will have to set up the GUI window.
A new abstract method: public void display(Graphics g); should be added to the base and derived classes.
A custom JPanel must be set up with a paintComponent method
The new display(Graphics g) method will have to draw the shapes on the GUI window and be called from a loop in the paintComponent method.
import javax.swing.*;
import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Project6 extends JFrame {
private Shape [] thearray = new Shape[100];
public static void main (String [] args) {
Project6 tpo = new Project6();
tpo.run();
}
public void run () {
int count = 0;
thearray[count++] = new Circle(20, 20, 40);
thearray[count++] = new Triangle(70, 70, 20, 30);
thearray[count++] = new Rectangle(150, 150, 40, 40);
thearray[count++] = new Square(100, 100, 50, 75);
for (int i = 0; i < count; i ++ ) {
thearray[i].display();
}
int offset = 0;
double totalarea = 0.0;
while (thearray[offset] != null) {
totalarea = totalarea + thearray[offset].area();
offset++;
}
System.out.println("The total area for " + offset + " Shape objects is " + totalarea);
}
public Project6() {
JFrame frame = new JFrame();
frame.setSize(800, 700);
frame.setTitle("Shapes: Circle, Triangle, Rectangle, Square");
frame.setLocationRelativeTo(null); //Center Frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static class MyPanel extends JPanel {
public static JPanel showJPanel(Graphics g) {
panel = new MyPanel();
return panel;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for(int i = 0; i < thearray.length && thearray[i] != null; i++) {
thearray[i].display();
Do I add something like this at the end of each of my classes? I.E. Circle, Square, Triangle, Rectangle class?
#Override
public void draw(Graphics g) {
g.drawRect(getXPos(), getYPos(), width, height);
}
I can't change the way the array is set up, but isn't this supposed to be the class that extends JFrame?
public Project6() {
JFrame frame = new JFrame();
frame.setSize(800, 700);
frame.setTitle("Shapes: Circle, Triangle, Rectangle, Square");
frame.setLocationRelativeTo(null); //Center Frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
I'm new to GUI so this is a little hard to do, but would this work for drawing the shapes? But I get an error saying nonstatic method get() cant be referenced from static context
class NewPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(Triangle.getXPos(), 0, 0, Triangle.getYPos());
g.drawLine(Triangle.getXPos(), 0, Triangle.getXPos, Triangle.getYPos());
g.drawLine(Triangle.getXPos(), Triangle.getYPos, 0, Triangle.getYPos());
g.drawRect(Rectangle.getXPos(), Rectangle.getYPos(), Rectangle.getWidth(), Rectangle.getHeight());
g.drawRect(Square.getXPos(), Square.getYPos(), Square.getWidth(), Square.getHeight());
g.drawOval(Circle.getXPos(), Circle.getYPos(), Circle.getRadius(), 10);
for(int i = 0; i < thearray.length && thearray[i] != null; i++) {
thearray[i].display();
}
}
Your class extends a JFrame that is never displayed
You should draw your shapes in the paintComponent method of a JPanel, one that is added to your JFrame.
I would use an ArrayList<Shape>, not an array, since this way I'd be able to add as many or as few Shapes to my collection and not have to worry about null items.
I'd then iterate through the collection in the paintComponent method override and draw each Shape using a Graphics2D object.
Regarding your last question, "Do I add something like this at the end of each of my classes? ie(Circle, square, triangle, rectangle class?..." no, there's no need for a "draw" method since you'll be using the paintComponent method to do your drawing.

Categories

Resources