Random is shared between all instances of an object - java

I want to create a bunch of dots and move each of them in a random direction. Problem is that all dots are heading the same direction, so they look like a single dot even if there are a hundred of them. I tried to google it but found nothing. I bet it's just my stupidity, but I really don't know what to do with it.
Thanks for helping me.
Dot class:
public class Dot extends JPanel {
private PVector pos;
private PVector vel;
private PVector acc;
private Brain brain;
private boolean dead = false;
public Dot(){
this.brain = new Brain(400);
this.pos = new PVector(Main.width/2, Main.height/2);
this.vel = new PVector(0, 0);
this.acc = new PVector(0, 0);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval((int)pos.x, (int)pos.y, 4, 4);
}
public void show(JFrame frame) {
frame.add(this);
}
public void move() {
if(brain.directions.length > brain.step) {
acc = brain.directions[brain.step];
brain.step++;
} else {
dead = true;
}
vel.add(acc);
vel.limit(5);
pos.add(vel);
}
public void update() {
if(!dead) {
move();
if(pos.y < 2 || pos.y < 2 || pos.x > Main.width - 2 || pos.y > Main.height - 2) {
dead = true;
}
}
}
}
Brain class:
public class Brain {
public PVector[] directions;
public int step = 0;
public Brain(int size) {
directions = new PVector[size];
randomize();
}
private void randomize() {
for(int i = 0; i < directions.length; i++) {
Random random = new Random();
float randomAngle = (float)(random.nextFloat() * 2 * Math.PI);
directions[i] = PVector.fromAngle(randomAngle);
}
}
}
Population class:
public class Population {
public Dot[] dots;
public Population(int size) {
dots = new Dot[size];
for(int i = 0; i < size; i++) {
dots[i] = new Dot();
}
}
public void show(JFrame frame) {
for(int i = 0; i < dots.length; i++) {
dots[i].show(frame);
}
}
public void update() {
for(int i = 0; i < dots.length; i++) {
dots[i].update();
}
}
}
Gui class:
public class Gui {
JFrame frame = new JFrame();
Population test = new Population(100);
public Gui() {
startup();
}
private void startup() {
frame.setSize(Main.width, Main.height);
frame.setLocation(200, 200);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
ScheduledExecutorService executor = Executors
.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
update();
}
}, 0, 1000 / 60, TimeUnit.MILLISECONDS);
test.show(frame);
}
private void update() {
frame.repaint();
test.update();
}
}

Each "Dot" instance is a separate panel. You have a 100 Dots, so you have 100 panels. But: the layout manager set on JFrame only shows one of the Dots (probably the last Dot).
There may be a way to make this work so that the 100 panels are all painted on top of each other, by playing around with layout managers, but the "proper" (in my opinion) way to fix this is to make Population extend JPanel, not Dot. That way, Population is a single component that displays the entire population.
These are the changes you need to make:
// add "extends JPanel"
public class Population extends JPanel {
// Change "show" into "paintComponent"
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for(int i = 0; i < dots.length; i++) {
dots[i].show(g);
}
}
}
// Remove "extends JPanel"
public class Dot {
// Change "paintComponent" into "show"
public void show(Graphics g) {
g.fillOval((int)pos.x, (int)pos.y, 4, 4);
}
}
public class Gui {
private void startup() {
// remove this:
// test.show(frame);
// add this:
frame.add(test);
}
}

Related

Swing draws 3 JLabels instead of one

I don't really understand why this program draws three pawns instead of one and two of which seem to have a random(probably not so random) positions. enter image description here
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class GamePanel extends JPanel implements MouseListener {
static final int SCREEN_EDGE = 800;
static final int GAME_UNITS = 64;
static final int UNIT_SIZE = 100;
final int[] x = new int[GAME_UNITS];
final int[] y = new int[GAME_UNITS];
boolean running = false;
public GamePanel() {
this.setPreferredSize(new Dimension(SCREEN_EDGE, SCREEN_EDGE));
this.setFocusable(true);
this.addKeyListener(new MyKeyAdapter());
startGame();
int[] position = {0,0};
int[] position1 = {1, 0};
new Pawn(position,-1);
}
private void startGame() {
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
public void draw(Graphics g){
int counter = 1;
for (int y = 0; y < SCREEN_EDGE/UNIT_SIZE; y++) {
// 1 == "white" 2 == "black"
int color = (y % 2 == 0) ? 1 : 2;
for (int x = 0; x < SCREEN_EDGE/UNIT_SIZE; x++) {
g.setColor(color == 1 ? new Color(239,217, 181) : new Color(180, 136,98));
g.fillRect(x*UNIT_SIZE, y*UNIT_SIZE, UNIT_SIZE, UNIT_SIZE);
color = color == 1 ? 2 : 1;
}
}
for (int i = 0; i < Figure.figures.length; i++) {
JLabel figureSprite = new JLabel(Figure.figures[i].image, JLabel.CENTER);
figureSprite.setSize(90,90);
figureSprite.setLocation(Figure.figures[i].position[0] + 5,Figure.figures[i].position[1] + 5);
this.add(figureSprite);
}
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
public class MyKeyAdapter extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
}
}
}
import javax.swing.*;
public abstract class Figure{
protected int value;
protected ImageIcon image;
protected int[][] possibleMoves;
public int[] position;
// white = -1 black = 1
protected int whiteOrBlack;
protected static int figureCount = 1;
// int[figureCount][0 = x][1 = y][2 = color]
public static Figure[] figures = new Figure[figureCount];
public Figure(int value, int[] position, int[][] possibleMoves , int whiteOrBlack) {
this.value = value;
this.position = position;
this.possibleMoves = possibleMoves;
this.whiteOrBlack = whiteOrBlack;
Figure[] oldFigures = figures;
figures = new Figure[figureCount];
for (int i = 0; i < oldFigures.length; i++) {
figures[i] = oldFigures[i];
}
figures[figureCount - 1] = this;
figureCount++;
}
public abstract void move(int[] coordinates);
}
import javax.swing.*;
import java.awt.*;
import java.io.*;
public class Pawn extends Figure{
public Pawn(int[] position, int whiteOrBlack) {
super(1, position, new int[3][2], whiteOrBlack);
super.image = new ImageIcon(getClass().getClassLoader().getResource("graphics/" + (whiteOrBlack == -1 ? "whitePawn.png" : "blackPawn.png")));
Image newImage = super.image.getImage().getScaledInstance(90,90, Image.SCALE_AREA_AVERAGING);
super.image = new ImageIcon(newImage);
}
public void checkMoves(){
for (int i = 0; i < figures.length; i++) {
if((position[0] - 1) == figures[i].position[0] && (position[1] + this.whiteOrBlack) == figures[i].position[1] && figures[i].whiteOrBlack != this.whiteOrBlack) {
possibleMoves[0][0] = position[0] - 1;
possibleMoves[0][1] = position[1] + this.whiteOrBlack;
}else possibleMoves[0] = new int[2];
if((position[0]) != figures[i].position[0] && (position[1]) != figures[i].position[1]){
possibleMoves[1][0] = position[0];
possibleMoves[1][1] = position[1] + 1;
}else possibleMoves[1] = new int[2];
if((position[0] + 1) == figures[i].position[0] && (position[1] + this.whiteOrBlack) == figures[i].position[1] && figures[i].whiteOrBlack != this.whiteOrBlack) {
possibleMoves[2][0] = position[0] + 1;
possibleMoves[2][1] = position[1] + this.whiteOrBlack;
}else possibleMoves[2] = new int[2];
}
}
#Override
public void move(int[] coordinates) {
}
}
import javax.swing.*;
public class GameFrame extends JFrame {
public GameFrame(){
this.add(new GamePanel());
this.setTitle("Chess");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
this.setResizable(false);
this.setVisible(true);
this.setLocationRelativeTo(null);
}
}
public class ChessGame {
public static void main(String[] args) {
new GameFrame();
}
}
I tried few thing like changing JLabel to BufferedImage but it would've generate other problems down the line like not being able to use MouseListener so i feel stuck. I would love to know why this code generates 3 textures too.
It looks like you are adding FigureSprites from within the drawComponent() method. The more often you draw the window, the more figures you have to draw.
Instead within drawComponent() just draw the current state but do not modify it. Adding figures has to come from somewhere else. For example, you could create the necessary pawns in the constructor. Or in extra methods that might get triggered based on user input.
In case you modify the GamePanel's state enough so that it should get painted freshly, just invoke repaint(). Swing will mark this component for repainting and decide on it's own when to run the paint() method, which in turn will run paintComponent().

JPanel only updates after the for loop is finished

I was making a sorting visualizer and everything worked fine until I tried implementing the sort function on a button onClick. The issue is that when the sort function is called from a button, the screen doesn't refresh until the for loop is finished (normally, the sort function works perfectly).
MAIN:
public class Main {
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
}
}
TEST CLASS:
public class Test implements ActionListener {
public static int WIN_WIDTH = 1500;
public static int WIN_HEIGHT = 750;
JButton shuffleButton;
JButton bubbleSort;
JFrame window;
SortArray sortArray;
public Test() throws InterruptedException {
shuffleButton = new JButton();
shuffleButton.setBounds(WIN_WIDTH + 10, 0, 160, 100);
shuffleButton.setText("Shuffle!");
bubbleSort = new JButton();
bubbleSort.setBounds(WIN_WIDTH + 10, 200, 160, 100);
bubbleSort.setText("Bubble sort!");
window = new JFrame("Sort Visualizer");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(WIN_WIDTH + 200, WIN_HEIGHT);
window.setVisible(true);
sortArray = new SortArray();
window.add(shuffleButton);
window.add(bubbleSort);
window.add(sortArray);
sortArray.repaint();
sortArray.bubble_sort();
shuffleButton.addActionListener(this);
bubbleSort.addActionListener(this);
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == shuffleButton){
sortArray.shuffleArray();
}else if(e.getSource() == bubbleSort){
try {
sortArray.bubble_sort();
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
}
}
}
SORT ARRAY CLASS:
public class SortArray extends JPanel {
private static int NM_NM = WIN_WIDTH;
public static int BAR_WIDTH = WIN_WIDTH / NM_NM;
private int[] array;
public SortArray() throws InterruptedException {
shuffleArray();
}
public void shuffleArray(){
Random rng = new Random();
array = new int[NM_NM];
for (int i = 0; i < array.length; i++) {
repaint();
array[i] = rng.nextInt(WIN_HEIGHT);
}
}
public void bubble_sort() throws InterruptedException {
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
//bubble sort
System.out.println("sorting...");
Boolean not_sorted = true;
while (not_sorted) {
TimeUnit.MICROSECONDS.sleep(1);
not_sorted = false;
for (int i = 1; i < array.length; i++) {
if (array[i] > array[i - 1]) {
int temp = array[i];
array[i] = array[i-1];
array[i-1] = temp;
not_sorted = true;
repaint();
}
}
if (!not_sorted) {
break;
}
}
return null;
}
};
worker.execute();
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D graphics2D = (Graphics2D) g;
super.paintComponent(graphics2D);
for (int j = 0; j < array.length; j++) {
graphics2D.setColor(Color.BLACK);
graphics2D.fillRect(j * BAR_WIDTH, 0, BAR_WIDTH, array[j]);
}
}
}
Ok, I solved it, basically all I did was surround all the commands inside the bubbleSort() function with a SwingWorker and then executed it. (This creates a background thread). I edited the code to the right one.

Adding a JButton to a class extended by JPanel is not displaying a button

When I use normal JPanel initialized inside main instead of extended class. The button is added to a panel without problem and after launch is displayed in a center of a frame (default layout).
I would like to be able to add buttons inside an extended class. This problem occurs in Screen class aswell, where I need a Play Again button or Next Level button. The Screen is class extended by a JPanel too and JButtons are initialized inside the constructor aswell.
I'm not sure if the wrong part is in way of adding the components or in writing a code for a JPanel.
Here is the code:
Main:
public static void main(String[] args) {
// window - class JFrame
theFrame = new JFrame("Brick Breaker");
// game panel initialization
GamePanel gamePanel = new GamePanel(1);
theFrame.getContentPane().add(gamePanel);
// base settings
theFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
theFrame.setLocationRelativeTo(null);
theFrame.setResizable(false);
theFrame.setSize(WIDTH, HEIGHT);
theFrame.setVisible(true);
}
GamePanel Class:
public class GamePanel extends JPanel {
// Fields
boolean running;
boolean clicked = false;
private int rows = 8, colms = 11, N = rows * colms, level; // numbers of rows and columns
private int colmsC, rowsC;
private BufferedImage image;
private Graphics2D g;
private MyMouseMotionListener theMouseListener;
private MouseListener mouseListener;
private int counter = 0;
// entities
Ball theBall;
Paddle thePaddle;
Bricle[] theBricks;
Screen finalScreen;
public GamePanel(int level) {
// buttons
JButton pause = new JButton(" P ");
add(pause);
this.level = level;
init(level);
}
public void init(int level) {
// level logic
this.rowsC = level + rows;
this.colmsC = level + colms;
int count = rowsC * colmsC;
thePaddle = new Paddle();
theBall = new Ball();
theBall.setY(thePaddle.YPOS - thePaddle.getHeight() + 2);
theBall.setX(thePaddle.getX() + thePaddle.getWidth() / 2 - 5);
theBricks = new Bricle[count];
theMouseListener = new MyMouseMotionListener();
addMouseMotionListener(theMouseListener);
mouseListener = new MyMouseListener();
addMouseListener(mouseListener);
// make a canvas
image = new BufferedImage(BBMain.WIDTH, BBMain.HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) image.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// specific Bricks initialized
int k = 0;
for (int row = 0; row < rowsC; row++) {
for (int col = 0; col < colmsC; col++) {
theBricks[k] = new Bricle(row, col);
k++;
}
}
running = true;
}
public void playGame() {
while (running) {
//update
if (clicked) {
update();
}
// draw
draw();
// display
repaint();
// sleep
try {
Thread.sleep(20);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// update loop ( like playGame )
public void update() {
// ball moving
checkCollisions();
theBall.update();
}
public void draw() {
// background
g.setColor(Color.WHITE);
g.fillRect(0,0, BBMain.WIDTH, BBMain.HEIGHT-20);
// the bricks
int k = 0;
for (int row = 0; row < rowsC; row++) {
for (int col = 0; col < colmsC; col++) {
theBricks[k].draw(g, row, col);
k++;
}
}
// the ball and the paddle
theBall.draw(g);
thePaddle.draw(g);
// counter
String countString = new Integer(this.counter).toString();
g.drawString(countString, 20, 20);
// WIN / LOOSE SCREEN
if (this.counter == this.N * 20) {
win();
}
if (theBall.getRect().getY() + theBall.getRect().getHeight() >= BBMain.HEIGHT) {
loose();
}
}
public void paintComponent(Graphics g) {
// retype
Graphics2D g2 = (Graphics2D) g;
// draw image
g2.drawImage(image, 0, 0, BBMain.WIDTH, BBMain.HEIGHT, null);
// dispose
g2.dispose();
}
public void pause() {
this.running = false;
finalScreen = new Screen(this.level, counter);
finalScreen.draw(g,"GAME PAUSED");
}
public void win() {
this.running = false;
finalScreen = new Screen(this.level, counter);
finalScreen.draw(g,"YOU WIN");
}
public void loose () {
this.running = false;
finalScreen = new Screen(this.level, counter);
finalScreen.draw(g,"YOU LOOSE");
}
public void addScore() {
this.counter += 20;
theBall.setDY(theBall.getDY() - 0.001);
}
// Mouse Listeners
private class MyMouseListener implements MouseListener {
#Override
public void mouseClicked(MouseEvent e) {
clicked = true;
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
private class MyMouseMotionListener implements MouseMotionListener {
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
if (clicked)
thePaddle.mouseMoved(e.getX());
}
}
}

Why is my ArrayList not looping through all values?

I'm trying to make a small app that bounces balls around the frame of the window. When I add more than 1 ball to the list; it doesn't loop through them as expected.
Here's my code:
The Main Class:
public class Main extends JFrame {
private static final long serialVersionUID = 1L;
private List<Ball> balls = new ArrayList<Ball>();
private List<Ball> tempballs = new ArrayList<Ball>();
private static Timer t;
public void addBall(Ball b) {
tempballs.add(b);
}
public void initUI() {
this.setSize(500, 500);
this.setDefaultCloseOperation(3);
this.setVisible(true);
}
private BufferStrategy bs;
private Random r = new Random();
public void paint() {
if (!tempballs.isEmpty()) {
balls.addAll(tempballs);
tempballs = new ArrayList<Ball>();
}
int i = 0;
System.out.println(balls.size());
for (Ball b : new ArrayList<Ball>(balls)) {
i++;
System.out.println(i);
if ((bs = this.getBufferStrategy()) == null) {
this.createBufferStrategy(2);
return;
}
if (bs.contentsLost() || bs.contentsRestored()) {
return;
}
if (b.y >= this.getHeight() - 100) {
b.ydirection = -r.nextDouble() * 5;
}
if (b.y < 20) {
b.ydirection = r.nextDouble() * 5;
}
if (b.x >= this.getWidth() - 100) {
b.xdirection = -r.nextDouble() * 5;
}
if (b.x < 0) {
b.xdirection = r.nextDouble() * 5;
}
b.x += b.xdirection;
b.y += b.ydirection;
if (b.xdirection > 0)
b.xdirection += 0.1;
else
b.xdirection += -0.1;
if (b.ydirection > 0)
b.ydirection += 0.1;
else
b.ydirection += -0.1;
Graphics g = bs.getDrawGraphics();
g.fillOval((int) b.x, (int) b.y, 100, 100);
bs.show();
g.dispose();
bs.dispose();
}
i = 0;
}
public static void main(String[] args) {
try {
final Main m = new Main();
m.addMouseListener(new Mouse(m));
m.initUI();
t = new Timer();
TimerTask tt = new TimerTask() {
#Override
public void run() {
m.paint();
}
};
t.schedule(tt, Calendar.getInstance().getTime(), 20);
} catch (ConcurrentModificationException e) {
e.printStackTrace();
}
}
}
Here's the Ball class:
public class Ball {
private Random r = new Random();
public double y, x, ydirection, xdirection;
public Ball(int x, int y) {
this.y = y;
this.x = x;
ydirection = r.nextGaussian() * 5;
xdirection = r.nextGaussian() * 5;
}
}
and the mouse listener:
public class Mouse implements MouseListener {
Main m;
public Mouse(Main m) {
this.m = m;
}
#Override
public void mouseClicked(MouseEvent e) {
m.addBall(new Ball(e.getX(), e.getY()));
System.out.println("cl");
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
Additional details:
It only loops through the first item in the list, but the list size grows.
I'm using java 6, but I will change versions if needed.
If your loop does not behave as intended, try to find out why it is cancelled. You have two return statements in your loop which could cause this behavior. Make sysouts before those returns to figure out which one is the cause. Then find out why your if around the return is true. To digg deeper you can use your IDE's debugging mode and place breakpoints at interesting lines or use the step mode to run one line of code at a time.
Besides this you can place both if before the loop, the values they are checking should not change while you are in the paint() function (you are using the UI thread which might change them).
You have below return statements inside the loop. If the execution reaches any of the return statements, the loop ends.
Figure out, if you are entering these conditions and returning for the first value in the list.
if ((bs = this.getBufferStrategy()) == null) {
this.createBufferStrategy(2);
return;
}
if (bs.contentsLost() || bs.contentsRestored()) {
return;
}

Active rendering on a JPanel with ImageIcon tiles? #Java

So I'm trying to write a tile-grid based game and came up with a quite unusual solution. I filled a 2D JPanel Array with JLabels with an ImageIcon as tile. Everything works so far but I did not find any way to render this activly.
I've tryied some methods for active rendering I found on the Internet, but they did not work on my idea. Do you have some ideas how to realize this without rewrite everything to Canvas or something similar?
Here's my code:
Window
public class Win extends JFrame {
private static final long serialVersionUID = 1L;
private BufferStrategy bs;
public Win(int x, int y) {
this.setSize(x, y);
this.setVisible(true);
this.setResizable(false);
this.setIgnoreRepaint(true);
this.createBufferStrategy(2);
setBs(getBufferStrategy());
}
public BufferStrategy getBs() {
return bs;
}
public void setBs(BufferStrategy bs) {
this.bs = bs;
}
}
"Draw"
public class Field extends JPanel {
private static final long serialVersionUID = 5257799495742189076L;
private int x = 0;
private int y = 0;
private JPanel backPanel[][] = new JPanel[19][19];
private BufferedImage images[] = new BufferedImage[100];
private JLabel image[][] = new JLabel[19][19];
public Field() {
this.setLayout(new GridLayout(20, 20));
this.setIgnoreRepaint(true);
}
// Creates Panel Grid & Draws floor
public void setPanels() {
for (int h = 0; h < 19; h++) {
for (int w = 0; w < 19; w++) {
backPanel[h][w] = new JPanel();
backPanel[h][w].setLayout(new GridLayout(1, 1));
image[h][w] = new JLabel(new ImageIcon(images[0]));
backPanel[h][w].add(image[h][w]);
this.add(backPanel[h][w]);
}
}
}
// Loads the Textures
public void getTextures() throws IOException {
for (int i = 0; i < 1; i++) {
images[i] = ImageIO.read(new File("texture.png"));
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(images[1], 0, 0, null);
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
}
Game Loop
public class GameLoop implements Runnable {
private boolean runFlag = true;
#Override
public void run() {
Field field = new Field();
Win window = new Win(640, 640);
window.add(field);
try {
field.getTextures();
} catch (IOException e) {
e.printStackTrace();
}
while (runFlag) {
try {
field.setPanels();
window.getBs().show();
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void stop() {
runFlag = false;
}
}
Some alternatives:
Shuffle the components and do removeAll(), add(), and validate() as shown here.
Shuffle the contents and do setIcon(), as shown here.
In either case,
Use javax.swing.Timer to pace the animation, as shown here and here.
Consider TexturePaint to fill the icons, as shown here.

Categories

Resources