I am trying to make a snake type game and I am having trouble making the "pellets" appear at random places at fixed intervals (I want to make it 10 seconds). When I run the program after the brief delay I gave it (1000 milliseconds) the pellets begin to appear extremely rapidly flashing on the screen in different locations. What I want to do is make the pellets appear one at a time in random locations every 10 seconds instead of flashing around rapidly. Any help would be appreciated.
P.S. I have never done something like this before so, apologies if the code may seem a bit crude. Any advice with coding in general is also very appreciated.
Edit: I know this is still incorrect but I just want to know if I am at least on the right track so far. Now the "pellet" is just sitting there in the top right hand corner of the frame. Is there a problem now with my timer or the list or just everything in general. By the way if updating my code just to show new problems is frowned upon in this website let me know and I'll just ask for help in the comments section and stop with the edits.
package snake;
import java.awt.* ;
import java.awt.event.*;
import java.util.* ;
import javax.swing.*;
import javax.swing.Timer;
/**
*
* #author Carlos
*/
public class Pellet extends JPanel
{
Random randomNumber = new Random() ;
int x = 0 ;
int y = 0 ;
private Game game ;
private Timer timer ;
private final int DELAY = 100 ;
private ArrayList<Pellet> al = new ArrayList<>() ;
public Pellet(Game game)
{
this.game = game ;
}
#Override
public void paint(Graphics g)
{
super.paintComponent(g);
g.fillOval(x, y, 10, 10);
for(int i = 0 ; i < al.size() ; i++)
{
Pellet p = al.get(i) ;
p.paintComponent(g);
}
}
public void pelletTimer()
{
timer = new Timer(DELAY, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
x = randomNumber.nextInt(game.getWidth()) ;
y = randomNumber.nextInt(game.getHeight()) ;
Pellet p = new Pellet(game) ;
al.add(p) ;
repaint() ;
}
}) ;
timer.start();
}
}
You've lots of issues with your code attempt, so let's try to go through them.
First off this is a Swing program and so you must take care to not stomp on the Swing event thread, something that a java.util.Timer will do.
You're also creating your java.util.Timer within a paint method, something that should never be done since this method is for painting and painting only, and should not be slowed down or involved with non-painting code.
Your paint method is not a true paint method override of a Swing component since it has the wrong signature (it uses a Graphics2D parameter not the expected Graphics parameter) and your class does not extend a Swing component, so this method will do nothing of use for you.
Suggestions:
Draw in a paintComponent method override in a class that extends JPanel.
Call the super.paintComponent method first.
Use a javax.swing.Timer or "Swing" Timer to drive your animation.
In your Timer create a new pellet, add it to an ArrayList of pellets, and call repaint() which will tell the JVM to repaint your GUI and thus it will automatically call your paintComponent method for you.
In your paintComponent method, after calling the super's method, iterate through the pellet ArrayList drawing each individual pellet as you do.
Check out the Swing tutorials especially the graphics section. You can find a link to them here: swing info.
And check out this site for Swing animation examples, many written by me. You can find some results with this search.
Related
I have a jpanel that I repaint in a step event. It takes a cars position x and draws it. This works with integers but I'd like to use a double. The car objects x and y are needed to be doubles because I want to rotate and accelerate in a direction. Any ideas? Here's my current code:
public class Map extends JPanel implements ActionListener {
private void step() {
for(int i = 0; i <cars.length; i++) {
Car car = cars[i];
car.move();
repaint(car.getX()-1, car.getY()-1, car.getWidth()+2, car.getHeight()+2);
}
}
}
One possibility is for you to use doubles in your program's model, the classes that describe the state of your system, and use ints (by casting or rounding) in the view -- your GUI graphics code -- when displaying the state of the model.
Another is for you to consider using classes that implement the Shape interface, such as Rectangle2D, Ellipse2D, and Path2D as these use double or float specifiers for positioning
Your current code is broken in that the code is called on the Swing event thread, but appears to attempt to animate within a for loop. That's not going to work, and will only show the starting and finishing state of the animation due to it blocking the Swing event thread. Instead use a Swing Timer to drive the animation.
I'm a beginner in Java programming and have come across an issue (probably an easy one to solve).
I am experimenting with Java GUI and wish to create a window in which the colours of an array are cycled through until there are no more colours. I believe I can do this using a for loop and cycling through the array, however I do not know how to loop through the background colour.
Any help and explanation would be appreciated.
public void flashColor() {
Color [] color = { Color.red,Color.orange,Color.green };
int i = 0;
for(i=0;i<color.length;i--){
getContentPane().setBackground(Color(i));
}
}
This line tells me:
getContentPane().setBackground(Color(i));
that yours appears to be a Swing GUI (a key bit of information that you left out of your question!), and so you need to take Swing threading into consideration. Your current code will in fact loop through all the colors, but it will do so immediately, and on the Swing thread so that the GUI will have no way to paint any of the colors other than the last one. The solution: use a Swing Timer and not a for loop. Inside the timer advance an index int variable and use it to show the color.
Something like:
getContentPane().setBackground(colorArray[0]);
int delay = 1000; // for 1 second
Timer myTimer = new Timer(delay, new ActionListener() {
int index = 0;
public void actionPerformed(ActionEvent e) {
index++;
if (index >= colorArray.length) {
((Timer)e.getSource()).stop(); // stop the timer
} else {
getContentPane().setBackground(colorArray[index]);
}
}
});
myTimer.start();
The code has not been tested, and you'll want to read the Swing Timer tutorial for the details.
Note the key here is that yes you need to loop, and to pause (so that the color can be seen) but you need to do your looping and pausing in a thread thread is off of the Swing event dispatch thread (or EDT). Yes you could do this using a SwingWorker, but that is a way more difficult way to do this. It is far easier to use a Swing Timer to do this for you. Note that it uses a background thread invisibly for you.
I have done a program that numerically solves a set of differential equations which describes how an "arbitrary" illness move in an isolated and constant population, it was a programming assignment from a class I took a while ago. What I've done to extend it is to add some graphical components that can pause, reset and "play" the simulation, as well as some components that allows me to change some constants in the equations.
All this was an exercise in programming as I find it to be fun and exciting and want to become better.
However, at the moment I'm stuck, what I want to do now is to make a very simple form of animation of it. I want to visualize the data I get for the number of infected, susceptibles and resistants in a grid as points. I managed to create the grid and have an idea of how to place the dots.
The problem I have is how to draw the dots as the program is working, I can draw one dot in the grid but only as the grid is created, that's it. I need to be able to create a dot at a specific place in the grid, this goes on until the number of dots reaches a finite number, say 30. At that points I want to have the first dot, the one the left, removed, all the dots shifted to the left and place the new dot at the furthest right of the grid, the whole thing is then repeated.
I think I will be able to figure it out with some help/hints about the paintComponent() method and whether I need to use repaint() method at all, I can't get my head around these for some reason. I've read through my course literature on Java, but despite the extensive sections where he explains most of the different graphical components he does not say that much about those methods, only that you don't call for the paintComponent() method, it is done automatically.
If there is something unclear let me know and I'll try to clarify it.
Thanks in advance.
//
Fox Mulder
I think I will be able to figure it out with some help/hints about the paintComponent() method and whether I need to use repaint() method at all, I can't get my head around these for some reason.
Basically, say you create a custom component by extending JPanel. When you #Override the paintComponent() method, it get implicitly called for you, so you never have to call it. So what ever you paint inside the method, gets drawn on your surface. For example
public class DrawingPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(x, y, 10, 10);
}
}
When you call repaint() you are basically causing the paintComponent method to be call implicitly. So to answer your question, Yes you will need to call it if you want to animate, as you will need to update some kind of variable (like the x and y) in the paintComponent() method, to see any change in the drawing.
You can see more at Performing Custom Painting
Not to handle the actual animation, you'll want to use a javax.swing.Timer. You can see more at How to use Swing Timers. Here's the basic construct
Timer ( int delayInMillis, ActionListener listener )
where delayInMillis is the time to delay between ticks(in this case animations) and the ActionListener listens for "ticks". Each tick, the actionPerformed of the ActionListener is called. There, you can put the code to update any variables you use for animation.
So for example you update the x and y, in the actionPerformed, then call repaint()
public class DrawingPanel extends JPanel {
int x = 50;
int y = 50;
public DrawingPanel() {
Timer timer = new Timer(40, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
x += 5;
y += 5;
repaint();
}
});
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(x, y, 10, 10);
}
}
Now this was just a simple example. But in your case you want to animate a scatter plot. So what you can do is have a list of Points and in the actionPerformed you can add pull points from that list and push them into another list that is to be drawn. So say you have this
List<Point> originalPoints;
List<Point> pointsToDraw;
...
#Override
protected void paintComponent(Grapchics g) {
super.paintComponent(g);
for (Point point : pointsToDraw) {
g.fillOval(point.x - 5, point.y - 5, 10, 10);
}
}
Basically all the points in pointsToDraw list will be drawn. Initially it will be empty. And in the timer, you can add to the list, until the originalPoints list is exhausted. For example.
List<Point> originalPoints;
List<point> pointsToDraw;
private int currentIndex = 0;
public DrawingPanel(List<Point> originalPoints) {
this.originalPoints = originalPoints;
pointsToDraw = new ArrayList<>();
Timer timer = new Timer(40, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
if (currentIndex == originalPoints.size() - 1) {
((Timer)e.getSource()).stop();
} else {
pointsToDraw.add(originalPoints.get(currentIndex));
currentIndex++;
}
repaint();
}
});
timer.start();
}
So basicall you just keep a current index. When the index reaches the size of the original list, you stop the timer. Otherwise you just pop from the originalPoints and push to the pointsToDraw. For every point you add the pointsToDraw, a repaint() is called, and there will be another point for the paintComponent to draw a circle with.
The END
UDPATE
I just reread your question, and I think I have have misunderstood it. If you want all the points drawn, then basically just have one list. And draw all the points initially. with each tick, just remove the first index, advance all the rest up an index, and add a new one to the end. Though this is the implementation of a LinkedList so you may just want to use that
I have a program selection tool that i made. it opens a JFrame with 17 buttons, 15 of which are customizable, and they get their text from a .txt document located in the C: drive. when i click the assign button, it opens a JFileChooser to select a file to open when the button is clicked. You then select a button to change, and then type the text you want displayed by the button. After that the program rewrites the .txt file and updates the buttons. here is the code for updating:
public static void restart() {
start.assignButtonActions();
start.assignButtonText();
start.paint(graphics);
}
public void assignButtonActions() {
/**
* assign button actions
*/
for (int i = 0; i < buttonAction.length; i++) {
buttonAction[i] = io.readSpecificFromHD("C:\\ButtonActions.txt", i
+ 1 + actionButton.length);
}
}
public void assignButtonText() {
for (int i = 0; i < actionButton.length; i++) {
/**
* set button text
*/
actionButton[i].setText(io.readSpecificFromHD(
"C:\\ButtonActions.txt", i + 1));
}
}
public void paint(Graphics g) {
g.drawImage(getImage("files/background.png"), 0, 0, FRAMEWIDTH,
FRAMEHEIGHT, null);
refresh();
}
public void refresh() {
graphics.drawImage(getImage("files/background.png"), 0, 0, FRAMEWIDTH,
FRAMEHEIGHT, null);
for (int i = 0; i < actionButton.length; i++) {
actionButton[i].repaint();
}
assignButton.repaint();
helpButton.repaint();
}
Thats all the code that is required for this question i believe. The problem is, after the method restart() is called, the background is there, with a white square around the buttons, with it being white inside the square. not really a major problem, but really incredibly annoying and pretty unprofessional. At first i thought it was that the buttons were resizing after the background is painted, so i made it so that the refresh runs twice each time its called. didnt help one bit.
EDIT:
I fixed the problem. I took hovercraft's answer and modified what i learned a little bit. all i had to do was modify the restart() method to:
public static void restart() {
start.assignButtonActions();
start.assignButtonText();
start.repaint();
}
because the repaint(); repaint the whole component which was what hovercraft said. Thank you a ton everyone! hope this helps future questions.
You appear to be handling your Swing graphics incorrectly by calling paint(...) directly and trying to use a Graphics object outside of a JComponent's paintComponent(...) method. Don't do this, as all the Swing graphics tutorials will tell you (if you've not gone through some of them yet, you will want to do this soon). Instead do all graphics within a JComponent's (such as a JPanel's) paintComponent(...), call the super's method first, and use the Graphics object provided by the JVM in the paintComponent's method parameter.
Edit
Tutorial links:
The introductory tutorial is here: Lesson: Performing Custom Painting.
The advanced tutorial is here: Painting in AWT and Swing.
I'm thinking that you'll have to re-write most of your graphics code. Changes you should make:
Draw only in a JPanel or other JComponent-derived class, not in a JFrame or other top-level window.
Draw in your class's paintComponent(...) method.
Place an #Override annotation just above your paintComponent(...) method to be sure that you are in fact overriding the super method.
Call the super's paintComponent(...) as the first line (usually) of your paintComponent(...) override method.
Use the Graphics object passed into this method by the JVM.
Do not use a Graphics object obtained by calling getGraphics() on a component (with rare exceptions).
Do not give your class a Graphics field and try to store the Graphics object in it. The Graphics objects given by the JVM do not persist and will quickly become null or non-usable.
Do not call paint(...) or paintComponent(...) directly yourself (with rare exceptions -- and your current code does not qualify as one of the exceptions, trust me).
You will likely not need to call repaint() on your JButtons
(second question in a few hours)
Kay so I'm making a chess variant in java, I have my console program working how I want it but now I'm trying to convert it to a swing GUI while STILL keeping the console things intact. So up to now I have my array of squares with pieces in them for the console, and a 2-dimensional array of JPanels with pieces in them for the GUI. I haven't implemented moving pieces in the GUI yet so I'm still doing it from the console but the actual GUI doesn't update after I've moved a piece...even though it does on the console (sorry if this is confusing).
The GUI consists of a constructor which calls some methods drawBoard() and drawSidebar() and sets sizes, titles etcetc...so this is what the main method looks like:
public static void main(String args[]) {
ChessGUI GUI = new ChessGUI();
Board console = new Board();
do {
console.printBoard();
console.getScore();
console.getMove();
GUI.boardPanel.revalidate();
GUI.sidePanel.revalidate();
GUI.repaint();
} while (true);
}
and drawBoard() incase it makes any difference:
public void drawBoard() {
LayoutManager layout = new GridLayout(NUMBER_OF_ROWS, NUMBER_OF_COLS);
boardPanel.setLayout(layout);
boardPanel.setPreferredSize(new Dimension(200, 450));
chessBoard = new JPanel[NUMBER_OF_ROWS][NUMBER_OF_COLS];
for (int i = 0; i < NUMBER_OF_ROWS; i++) {
for (int j = 0; j < NUMBER_OF_COLS; j++) {
chessBoard[i][j] = new JPanel();
chessBoard[i][j].setBackground(getColor(i,j));
int index = i * 4 + j;
if (!(boardArray.chessBoard[index].square.isEmpty())) {
Piece piece = (Piece) boardArray.chessBoard[index].square.firstElement();
chessBoard[i][j].add(new JLabel(piece.toString()));
}
boardPanel.add(chessBoard[i][j]);
}
}
}
the repaint and revalidate methods don't seem to be calling at all, even though the console is being updated :(
I don't really understand what you are doing. But it doesn't make sense to recreate the entire board panel every time a move is made. All Swing components can only have a single parent, to the easier solution is to just move the piece from one panel to the other. So the code would be something like:
previousPanel.remove( piece );
currentPanel.add( piece );
previousPanel.revalidate();
previousPanel.repaint();
currentPanel.revalidate();
It looks like you're never actually removing anything from 'boardPanel,' even though you are resetting its LayoutManager.
A safer approach might be to remove 'boardPanel' from its container, then create a new instance for 'boardPanel,' add that to the container, then add the other JPanel pieces to this new 'boardPanel.' Effectively, you would be reconstructing the entire JPanel hierarchy after every move.
As you've noticed, Swing can be quite finicky once you start trying to add/move/remove components after they've been added to containers. For games, often the best approach would be to have 1 JComponent/Component and use Java2D methods to draw on top of it. Swing is typically only used for forms-based applications.
Changing the layout doesn't do anything.
You need to call boardPanel.removeChildren()
However, this is going to be extremely slow.
Really, what you should be doing is have your own JPanel, overwrite paintComponent() and draw the images into the appropriate dimensions using Java Graphics.