Drawing a lot of same objects from a class in JFrame - java

Answer is written by me in the bottom of the question
I want to get a lot of 'Ates' objects in a frame. I tried a lot of examples but always failed.
In this context, I want to see a lot of rectangles which are going to left. However, there is just one and it is going faster and faster...
It does not show more than one object at the same time. Can you tell me what is the problem?
I used this code:
public class GamePanel extends JPanel
{
public void paint(Graphics g)
{
super.paint(g);
g.setColor(Color.BLACK);
for(Ates a1 : StartGame.alist) // alist is an arraylist for Ates class objects
{
g.fillRect(a1.getX(), a1.getY(), 20, 20);
}
...
Example creating:
public void sentAtes()
{
r = rand.nextInt(471)+60;
Ates a = new Ates(r);
alist.add(a);
}
Ates class:
public Ates(int a)
{
x = 700;
y = a;
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
public void setX( int a )
{
x = a;
}
public void setY( int a )
{
y = a;
}
StartGame class:
public class StartGame extends JFrame implements KeyListener, ActionListener
{
protected static ArrayList<Ates> alist = new ArrayList<Ates>();
public static int cen = 0;
...
public StartGame()
{
jp = new GamePanel();
add(jp);
...
int delay = 10;
ActionListener taskPerformed = new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
cen++;
if(cen > 50)
{
cen = 0;
sentAtes();
}
for(Ates a1 : alist)
{
a1.setX(a1.getX()-1);
}
repaint();
}
};
new Timer(delay,taskPerformed).start();
...
Info: If there is only one object, it is going left as expected.
Answer to my question.
In Ates class, you should not use static word for variables and use this. prefix to set them.
This should be work.
public class Ates extends JFrame
{
protected int x;
protected int y;
public Ates(int a)
{
this.x = 700;
this.y = a;
}

Ok the only other thing I can see is that your StartGame() is only initialised once. Therefore your new Timer is only called once. Because it is the timer that is causing the ActionListener to be created it is only ever created and run through once and therefore can never reach the stage where it will create another rectangle. The actionPerformed() method only runs once in the program as there is no action or loop to cause it to run again.

Related

How do I create multiple objects of the same class?

I want to create two objects of the Ball class. I have tried the following:
public class World extends JPanel {
JFrame frame = new JFrame("GreenJ");
Actor[] actor = new Actor[100];
int n = 0;
public World() throws InterruptedException{
frame.add(this);
frame.setSize(1000, 1000);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void addObject(Actor a) {
actor[n] = a;
frame.add(actor[n]);
}
}
public class MyWorld extends World {
public MyWorld() throws InterruptedException {
addObject(new Ball(frame, 250, 750));
addObject(new Ball(frame, 750, 250));
}
}
public class Ball extends Actor{
int x;
int y;
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.fillOval(x, y, 50, 50);
}
public Ball(JFrame frame, int a, int b) throws InterruptedException{
frame.add(this);
x = a;
y = b;
}
public void main(String[]Args) {
repaint();
}
}
When I run this code I only get the first 'ball' in my frame. I have tried some other things but without success.
Thank you in advance. ElAdriano
The value of n is never changed in your code. So addObject will always put the new object in index 0 of your actor array.
Change your Actor[] into ArrayList of type Actor this would help you forget about where to add the next object or at any index n
ArrayList<Actor> actors = new ArrayList<>();
and change the addObject() method to add the object to the actors array
addObject(Actor a){
actors.add(a);
}

Java Swing Horse Simulation

I am having problem getting my horse position to update within my swing application. I have tried multiple methods to update the xPosition of the horses as they refresh across the swing panel that represents the racetrack.
public class HorseModel{
/*Horse dimensions*/
private int x;
private int y;
private final int horsePerimeter = 20;
public HorseModel(int xT, int yT){
/*Constructor*/
x = xT;
y = yT;
}
public void setDimensions(int dimX, int dimY){
/*Sets program dimensions*/
x = dimX;
y = dimY;
}
public void createHorse(Graphics2D h){
/*Paints HorseModel on screen as 2 dimensional object*/
Ellipse2D.Double horseModel = new Ellipse2D.Double(x, y, horsePerimeter, horsePerimeter);
h.setColor(Color.RED);
h.fill(horseModel);
h.setColor(Color.YELLOW);
h.draw(horseModel);
}
}
public class HorseMovement implements Runnable{
public final int xStartPos = 10; //change
public final int yStartPos = 20;
private RaceTrack hRaceTrack;
private HorseModel Horse2D;
private int xPos, yPos;
public HorseMovement(RaceTrack r, int yPos_Spacing){
/*Constructor*/
xPos = xStartPos;
yPos = yStartPos * yPos_Spacing;
Horse2D = new HorseModel(xPos, yPos);
hRaceTrack = r;
}
public HorseModel moveHorse(HorseModel horseObject){
/*Updates horse positon*/
horseObject = new HorseModel(xPos++, yPos);
return horseObject;
}
public void paintComponent(Graphics h){
/*paints the new horse after movement*/
this.Horse2D = moveHorse(Horse2D);
Graphics2D hMod = (Graphics2D) h;
Horse2D.createHorse(hMod);
}
public void run(){
/*Repaints the horse models as they increment movement across the screen*/
hRaceTrack.repaint();
hRaceTrack.revalidate();
}
}
public class RacePanel extends JFrame{
/*Frame Buttons*/
private JPanel mPanel;
private JButton startRace = new JButton("Start Race");
private JButton stopRace = new JButton("Stop Race");
private JButton startOver = new JButton("Start Over");
/*Panel to fill with HorseModels for race*/
private RaceTrack rTrack;
/*Window dimensions*/
public int Window_Height = 1024;
public int Window_Width = 768;
public RacePanel(){
/*Constructor*/
initGui();
initRace();
initQuit();
setSize(Window_Width, Window_Height);
}
public void initGui(){
/*Initializes the main race panel and sets button positions and layouts*/
mPanel = new JPanel(new BorderLayout());
rTrack = new RaceTrack();
JPanel horsePanel = new JPanel(); //panel to house horse objects before running across screen
horsePanel.setLayout(new GridLayout(1, 3));
positionJPanels(horsePanel, mPanel);
}
public void initRace(){
/*implements action listener for start race button*/
class StartRace implements ActionListener{
public void actionPerformed(ActionEvent e){
startRace.setEnabled(true);
rTrack.initTrack();
}
}
ActionListener event = new StartRace();
stopRace.addActionListener(event);
}
public void initQuit(){
/*Implements the action listener for stop race button*/
class StopRace implements ActionListener{
public void actionPerformed(ActionEvent e){
System.exit(0);//exits program if race is stopped
}
}
ActionListener event = new StopRace();
stopRace.addActionListener(event);
}
public void positionJPanels(JPanel h, JPanel p){
/*Handles adding buttons to a JPanel*/
h.add(startRace);
h.add(startOver);
h.add(stopRace);
p.add(h, BorderLayout.NORTH); //sets the horse panel buttons to the top of the layout
p.add(rTrack, BorderLayout.CENTER); //sets
add(p);
}
}
public class RaceController {
public static void main(String[] args){
new RaceController();
}
public RaceController(){
/*Constructor*/
JFrame mFrame = new RacePanel();
mFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mFrame.setVisible(true);
}
}
public class RaceTrack extends JPanel{
/*Sets horses within race track*/
private int numOfHorseObjects = 5;// change this to a dynamic
private int numOfThreads = 25;
/*Holds horse thread from HorseObject class*/
private ArrayList<HorseMovement> horses = new ArrayList<>();
private ArrayList<Thread> threads = new ArrayList(numOfThreads);
public RaceTrack(){
/*Constructor*/
setBackground(Color.black);
reset();
}
public void initTrack(){
/*Starts the RaceTrack simulation*/
threads.clear(); //clears the thread arraylist still residing
for(int i = 0; i < horses.size(); i++){
Thread T = new Thread(horses.get(i));
T.start();
threads.add(T);
}
}
public void reset(){
/*resets horse position within screen*/
horses.clear();
for(int i = 0; i < numOfHorseObjects; i++){
horses.add(new HorseMovement(this, i + 1));
}
}
public void paintComponent(Graphics g){
/*overrides graphics paint method in order to paint the horse movements
* through the arraylist of HorseMovements*/
super.paintComponent(g);
for(HorseMovement h : horses){
h.paintComponent(g);
}
}
}
Issues with your code:
You never give the startRace JButton an ActionListener, so how is button going to have any affect, and how is the race ever going to start? Note that you're adding the StartRace ActionListener object to the stopRace JButton, and I'm guessing that this was done in error.
Even if you added that Listener to the startRace button, the action listener will only advance all the horses one "step" and no more -- there are no loops in within your background threads to perform actions repetitively.
You seem to be creating new Horse2D objects needlessly. Why not simply advance the location of the existing Horse2D object?
Myself, I'd use a single Swing Timer rather than a bunch of Threads in order to simplify the code.

Is there a way to paint on a JPanel object from ouside it's class?

I searched and found a few questions worded similar, but nothing that applied to my situation so here I go.
I'm trying to make a game with different levels, each level plays completely different.
originally, my code looked like this and worked fine:
public class Life extends JPanel{
private Story story;
public Life(){
story = new Story(this);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
story.render(g)
public void terminate(){
story.terminate();
System.out.println("Terminated");
}
public static void main(String[] args){
final JFrame frame = new JFrame("Life");
final Life life = new Life();
frame.add(life);
}
}
public class Story {
private int phase;
private Life life;
public Story(Life life){
this.life = life;
phase = 0;
}
public void render(Graphics g){
if(phase == 0) levelOneRender(g);
if(phase == 1) levelTwoRender(g);
}
}
I was worried that I'd be wasting time every game tick checking what phase I was in. Since I plan to have 20+ phases, the code would get inefficient fast.
So I had an idea to simply pass along the Graphics object from JPanel from my Life object to my Story Object and paint the Jpanel in a different class for each phase like this:
public class Life extends JPanel{
public Life(){
story = new Story(this);
}
public static void main(String[] args){
final JFrame frame = new JFrame("Life");
final Life life = new Life();
}
}
public class Story {
private int phase;
private Intro intro;
private Life life;
public Story(Life life){
this.life = life;
phase = 0;
intro = new Intro(this);
}
public void nextPhase(){
this.phase++;
}
public Life getLife() {
return this.life;
}
}
public class Intro {
private static final int DELAY = 100; // in milliseconds, so 10 ticks per second
private Timer timer;
private Story story;
private Graphics g;
private int counter;
public Intro(Story story) {
this.story = story;
this.g = story.getLife().getGraphics();
this.counter = 0;
timer = new Timer(DELAY, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
tick();
story.repaint();
}
});
timer.start();
}
public void tick(){
if(counter <= 40){
terminate();
}
counter++;
render();
}
public void render(){
story.getLife().paint(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.draw(new Rectangle(0,0,10,10));
}
public void terminate(){
timer.stop();
story.nextPhase();
}
}
Unfortunately this just doesnt work, as story.getLife().paint(g); in class Intro throws a nullPointerException, when I run it. And I'm quite sure that's not the only problem with my attempt at this.
Is there a correct way to do what I'm going for?
Thank you very much for your time. Any insight would be appreciated.
public void render(Graphics g){
if(phase == 0) levelOneRender(g);
if(phase == 1) levelTwoRender(g);
}
This is not as big of a problem as you think. What you have now is fine. But those checks can be avoided (as you are requesting).
Rather than handle the painting for each level in separate methods, handle them in separate objects:
public bstract class Level {
public abstract void paint(Graphics g);
}
public final class LevelOne extends Level {
public void paint(Graphics g) {
//...
}
}
public final class LevelTwo extends Level {
public void paint(Graphics g) {
//...
}
}
This way, instead of checking an int value to see which render to draw, you can just switch a Level value to render a new level:
Level one = new LevelOne();
Level two = new LevelTwo();
Level currentLevel = one;
public void paintComponent(Graphics g) {
super.paintComponent(g);
currentLevel.paint(g);
}
Simply switch the value of level to render different levels

Animating Separate Objects

I've been working on an "elevator simulator" where I have to animate "elevators". I was able to create different elevator objects, which I did by having each Elevator Object have the parameters of width, height, and coordinates. I then stored them all into an array and used a for loop to draw them into my frame using JPanel's PaintComponent method.
Can you guys suggest a way or give me advice to animate them separately from each other, like say move them up and down independently? I was able to make it move when I only had ONE elevator, but when I tried to apply it to multiple elevators, it did not work.
My previous attempt involved an ActionListener (that listens to a button press to "start" the animation) that simply changes the x and y coordinates of the SINGLE elevator. So How do I go and do that with several elevators (the number of elevators is arbitrary to the user). Do I have to make as many ActionListeners as there are elevators, so it can listen to them independently?
Here's the ActionListener that worked when there's only ONE elevator. It only moves up so far.
private ActionListener timerActionUp = new ActionListener()
{
private int iterator = 0;
private int top = 0;
public void actionPerformed(ActionEvent e)
{
if(top<floorQueue.length){
if(iterator<floorQueue[top]){ //floorQueue is so that the elevator knows where to stop
if(movefloorup<VERTICALFLOORDISTANCE*6){ //this is when the elevator reaches the "top" floor that can fit into the screen, and moves to the next column representing the floors further up
repaint();
movefloorup = movefloorup + VERTICALFLOORDISTANCE;
System.out.println(movefloorup);
iterator++;
}
else{
//timer.stop();
repaint();
switchmarker = 1; //makes elevator moves to the next column
movefloorup = 0;
iterator++;
}
}
else
{
System.out.println("Picking up passengers...");
elevatorCapacity = elevatorCapacity + peopleQueue[floorQueue[top]];
System.out.println("Passengers in elevator: "+elevatorCapacity);
peopleQueue[floorQueue[top]] = 0; //Updates the number of people in the elevator
top++;
if(elevatorCapacity >= 5)
{
System.out.println("WARNING! ELEVATOR FULL!");
elevfull = 1;
}
//timer.stop();
}
}
else
{
System.out.println("Done picking up passengers.");
timer.stop();
}
}
};
Thank you very much!
"Do I have to make as many ActionListeners as there are elevators, so it can listen to them independently?"
No, that would require multiple Timers. Avoid doing this whenever you can.
"Can you guys suggest a way or give me advice to animate them separately from each other, like say move them up and down independently?"
What you should do is try and implement the business logic in methods within your Elevator class and just call the those methods while looping through all the Elevators in your array.
Two make the Elevators to appear to move independently, you can have flags, say in your move method, like
public void move() {
if (move) {
// do something
}
}
What ever is your reason for making the elevator move, that will be the reason the raise the flag. And vice versa. Maybe something like if (onFloor) { elevator.move = false }, maybe for a duration of 20 timer "iterations", and keep a count in the elevator class, that will reset back to 0 when the count hits 20, then move will be back at true.
Here's an example you can play with. I was working on it for about 20 minutes then gave up. The logic is a bit off, but it basically points out the ideas i mentioned. Maybe you'll have better luck with it. You can also see a good working example of moving different object here
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class ElevatorAnimate extends JPanel {
private static final int D_W = 300;
private static final int FLOORS = 6;
private static final int FLOOR_HEIGHT = 100;
private static final int BUILDING_HEIGHT = FLOORS * FLOOR_HEIGHT;
private static final int D_H = BUILDING_HEIGHT;
private static final int BUILDING_BASE = BUILDING_HEIGHT;
private static final int ELEVATOR_HEIGHT = 60;
private static final int ELEVATOR_WIDTH = 30;
private final List<Elevator> elevators;
public ElevatorAnimate() {
elevators = createElevators();
Timer timer = new Timer(50, new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (Elevator el : elevators) {
el.move();
}
repaint();
}
});
timer.start();
}
private List<Elevator> createElevators() {
List<Elevator> list = new ArrayList<>();
list.add(new Elevator(6, 30));
list.add(new Elevator(4, 90));
list.add(new Elevator(2, 150));
return list;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
drawFloors(g);
for (Elevator el : elevators) {
el.drawElevator(g);
}
}
private void drawFloors(Graphics g) {
for (int i = 1; i <= FLOORS; i++) {
g.drawLine(0, FLOOR_HEIGHT * i, D_W, FLOOR_HEIGHT * i);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}
public class Elevator {
int floor, x, y;
boolean move = false;
boolean up = true;
int stopCount = 0;
public Elevator(int floor, int x) {
this.floor = floor;
y = BUILDING_HEIGHT - (floor * FLOOR_HEIGHT);
this.x = x;
}
public void drawElevator(Graphics g) {
g.fillRect(x, y, ELEVATOR_WIDTH, ELEVATOR_HEIGHT);
}
public void move() {
if (y <= 0) {
up = false;
} else if (y >= BUILDING_BASE + ELEVATOR_HEIGHT) {
up = true;
}
if (isOnFloor()) {
move = false;
}
if (move) {
if (up) {
y -= 2;
} else {
y += 2;
}
} else {
if (stopCount >= 20) {
move = true;
stopCount = 0;
} else {
stopCount++;
}
}
}
private boolean isOnFloor() {
return y / FLOOR_HEIGHT == 100;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new ElevatorAnimate());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Starting from this related Subway simulation, the following variation adds two independent panels, each of which contains its own view and control panel.
// Common initialization for either JApplet or JFrame
private static void initContainer(Container container) {
container.add(createPanel(), BorderLayout.NORTH);
container.add(createPanel(), BorderLayout.SOUTH);
}
private static JPanel createPanel() {
JPanel panel = new JPanel(new BorderLayout());
ButtonPanel control = new ButtonPanel();
SubwayPanel subway = new SubwayPanel(control);
panel.add(subway, BorderLayout.NORTH);
panel.add(control, BorderLayout.SOUTH);
subway.beginOperation();
return panel;
}

How to avoid NullPointerException on initial painting of Components?

Currently I try to make a game of chess. This, the main class, calls a JPanel subclass, on which I draw the figures.
package schach;
public class schach extends JFrame {
private SpielFeld spiel = new SpielFeld();
public schach(String title) {
Container cp = getContentPane();
cp.add(spiel, BorderLayout.CENTER);
}
public static void main(String[] args) {
new schach("Schach");
}
}
Then it continues with SpielFeld, the JPanel subclass, which is supposed to draw the figures and the board:
package schach;
public class SpielFeld extends JPanel {
private Image brettimg = new ImageIcon("schach\\sprites\\brett.png").getImage();
private Image bauerWimg = new ImageIcon("schach\\sprites\\bauerW.png").getImage();
private ArrayList<Figur> figuren = new ArrayList<Figur>();
private Bauer bauerW1 = new Bauer(6, 0);
public SpielFeld() {
figuren.add(bauerW1);
}
#Override
public void paintComponent(Graphics g) {
g.drawImage(brettimg, 0, 0, null);
for (int i=0; i<figuren.size(); i++) {
g.drawImage(bauerWimg, (int) figuren.get(i).getPoint().getX()*64, (int) figuren.get(i).getPoint().getY()*64, null);
}
}
}
Now I do get a NullPointerException. I think this is because of the order in which "spiel" is created, the paintComponent is called (by default, uppon adding the JFrame to the ContentPane or something?) and the ArrayListed created and filled. I tried to comment out stuff to see how it work, but can't seem to figure it out. How does it work and how can I fix this? I tried to delete the hopefully unimportant stuff.
Figur.java
package schach;
public class Figur {
Point posi;
public Figur(int x, int y) {
posi.setLocation(x, y);
}
public Point getPoint() {
return posi;
}
}
Bauer.java
package schach;
public class Bauer extends Figur {
boolean zug = false;
public Bauer(int x, int y) {
super(x, y);
}
}
#skirsch
This?
Exception in thread "main" java.lang.NullPointerException
at schach.Figur.<init>(Figur.java:10)
at schach.Bauer.<init>(Bauer.java:8)
at schach.SpielFeld.<init>(SpielFeld.java:29)
at schach.schach.<init>(schach.java:10)
at schach.schach.main(schach.java:31)
In the class Figur the Point posi is not initialized and therefor null.
Replace
public Figur(int x, int y) {
posi.setLocation(x, y);
}
by
public Figur(int x, int y) {
posi = new Point(x, y);
}
to eliminate the NullPointer Exception.

Categories

Resources