I am doing an assignment from the Java Exposure textbook, which was written in 2007. This book includes some code that I usually update to use some of the more recent features (just basic stuff). However, in this one I am running into a problem. All I tried to do is replace the show with setVisible(true) and change the Frame to a JFrame and add a gfx.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);. However, I noticed that this wouldn't actually cause the window to close. If I clicked many times, maybe 1/30 tries it would close. If I reduced the delay from 10 to 1, it usually closed within 2 tries. This of course led me to believe that the delay method is causing this erratic behavior. I tried Thread.sleep, but of course that didn't work. Is there any simply way to get this code so that the frame will close when I hit the close button? If there isn't, what would be the less simple way of doing it?
Here is the code:
// Lab30st.java
// The Screen Saver Program
// Student Version
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import javax.swing.JOptionPane;
public class Lab30st
{
public static void main(String args[])
{
GfxApp gfx = new GfxApp();
gfx.setSize(800,600);
gfx.addWindowListener(new WindowAdapter() {public void
windowClosing(WindowEvent e) {System.exit(0);}});
gfx.show();
}
}
class GfxApp extends Frame
{
private int circleCount, circleSize;
public GfxApp()
{
circleCount = 50;
circleSize = 30;
}
class Coord
{
private int xPos;
private int yPos;
public Coord(int x, int y)
{
xPos = x;
yPos = y;
}
}
public void paint(Graphics g)
{
int incX = 5;
int incY = 5;
int diameter = 30;
int timeDelay = 10;
Circle c = new Circle(g,diameter,incX,incY,timeDelay);
for (int k = 1; k <= 2000; k++)
{
c.drawCircle(g);
c.hitEdge();
}
}
}
class Circle
{
private int tlX; // top-left X coordinate
private int tlY; // top-left Y coordinate
private int incX; // increment movement of X coordinate
private int incY; // increment movement of Y coordinate
private boolean addX; // flag to determine add/subtract of increment for X
private boolean addY; // flag to determine add/subtract of increment for Y
private int size; // diameter of the circle
private int timeDelay; // time delay until next circle is drawn
public Circle(Graphics g, int s, int x, int y, int td)
{
incX = x;
incY = y;
size = s;
addX = true;
addY = false;
tlX = 400;
tlY = 300;
timeDelay = td;
}
public void delay(int n)
{
long startDelay = System.currentTimeMillis();
long endDelay = 0;
while (endDelay - startDelay < n)
endDelay = System.currentTimeMillis();
}
public void drawCircle(Graphics g)
{
g.setColor(Color.blue);
g.drawOval(tlX,tlY,size,size);
delay(timeDelay);
if (addX)
tlX+=incX;
else
tlX-=incX;
if (addY)
tlY+=incY;
else
tlY-=incY;
}
public void newData()
{
incX = (int) Math.round(Math.random() * 7 + 5);
incY = (int) Math.round(Math.random() * 7 + 5);
}
public void hitEdge()
{
boolean flag = false;
if (tlX < incX)
{
addX = true;
flag = true;
}
if (tlX > 800 - (30 + incX))
{
addX = false;
flag = true;
}
if (tlY < incY + 30) // The +30 is due to the fact that the title bar covers the top 30 pixels of the window
{
addY = true;
flag = true;
}
if (tlY > 600 - (30 + incY))
{
addY = false;
flag = true;
}
if (flag)
newData();
}
}
You are "freezing" the Event Dispatch Thread with
public void delay(int n)
{
long startDelay = System.currentTimeMillis();
long endDelay = 0;
while (endDelay - startDelay < n)
endDelay = System.currentTimeMillis();
}
This means that all the other stuff that is trying to happen (like closing the window) has to wait until the thread comes out of the "sleep".
basically you shouldn't be doing the delay in the EDT, it should be on a different thread and then ask the EDT thread to update.
Your "busy wait" delay may cause other problems too. You can improve the behavior by using Thread.sleep()
See Java Event-Dispatching Thread explanation
That's terrible.
You need to restructure the whole code.
Let's start with the really bad:
delay is (almost) a busy wait, I haven't seen busy waits since BASIC was modern. It basically holds the CPU hostage to the thread, not only does it do nothing, no other thread (almost) can use the time slice. The reason I say almost is that calling the system time function causes a context switch that could allow other threads to run, but it is still bad.
The still pretty bad:
Replacing with Thread.sleep. Better yes, no busy wait, but you are still holding the one and only UI thread. This means no other UI work can happen up to and including closing the main window.
What needs to happen:
Get an external timer (e.g. javax.swing.Timer) to trigger the draw event and do next part of the animation.
Search for "Java smooth animation" there are many examples of how to do this, double buffer and all.
Related
Hello Stack Overflow people :)
I'm a huge newbie when it comes to coding, and I've just ran into a problem that my brain just won't get over...
Before I start blabbering about this issue, I'll paste my code so as to give a little bit of context (sorry in advance if looking at it makes you wanna puke). The main focus of the issue is commented and should therefore be fairly visible :
Main
ArrayList<Individual> individuals = new ArrayList<Individual>();
void setup()
{
size(500,500);
for(int i = 0; i < 2; i++)
{
individuals.add(new Individual());
}
println(frameRate);
}
void draw()
{
background(230);
for(int i = 0; i < individuals.size(); i++)
{
individuals.get(i).move();
individuals.get(i).increaseTimers();
individuals.get(i).display();
}
}
Individual
class Individual
{
float x;
float y;
int size = 5;
Timer rdyBreed; /* Here is the object that appears to be shared
between individuals of the ArrayList */
float breedRate;
float breedLimit;
Individual()
{
x = random(0, width);
y = random(0, height);
rdyBreed = new Timer("rdyBreed", 0);
breedRate = random(.2, 3);
breedLimit = random(10, 20);
}
void move()
{
int i = (int)random(0, 1.999);
int j = (int)random(0, 1.999);
if (i == 0)
{
x = x + 1;
} else
{
x = x - 1;
}
if (j == 0)
{
y = y + 1;
} else
{
y = y - 1;
}
checkWalls();
}
void checkWalls()
{
if (x < size/2)
{
x = width - size/2;
}
if (x > width - size/2)
{
x = size/2;
}
if (y < size/2)
{
y = width - size/2;
}
if (y > width - size/2)
{
y = size/2;
}
}
void display()
{
noStroke();
if (!rdyBreed.finished)
{
fill(255, 0, 0);
} else
{
fill(0, 255, 0);
}
rect(x - size/2, y - size/2, size, size);
}
void increaseTimers()
{
updateBreedTimer();
}
void updateBreedTimer()
{
rdyBreed.increase(frameRate/1000);
rdyBreed.checkLimit(breedLimit);
rdyBreed.display(x, y);
}
}
Timer
class Timer
{
float t;
String name;
boolean finished = false;
Timer(String name, float t)
{
this.t = t;
this.name = name;
}
void increase(float step)
{
if (!finished)
{
t = t + step;
}
}
void checkLimit(float limit)
{
if (t >= limit)
{
t = 0;
finished = true;
}
}
void display(float x, float y)
{
textAlign(RIGHT);
textSize(12);
text(nf(t, 2, 1), x - 2, y - 2);
}
}
Now that that's done, let's get to my question.
Basically, I'm trying to create some sort of a personal Conway's Game of Life, and I'm encountering a lot of issues right off the bat.
Now my idea when writing this piece of code was that every individual making up the small simulated "society" would have different timers and values for different life events, like mating to have children for example.
Problem is, I'm not a huge pro at object-oriented programming, and I'm therefore quite clueless as to why the objects are not having each their own Timer but both a reference to the same timer.
I would guess making an ArrayList of timers and using polymorphism to my advantage could make a change, but I'm not really certain of it or really how to do it so... yeah, I need help.
Thanks in advance :)
EDIT : Here is a screenshot of the debugger. The values keep being the same with each iteration of the updates.
Screenshot
What makes you think they reference the same Timer object? The values of t displayed in the debugger are going to be the same until one of them reaches the breedLimit and gets set to 0, because they're being initialized at the same time.
Try this and see that the values of t are different.
void setup() {
size(500,500);
}
void mouseClicked() {
individuals.add(new Individual());
}
I'd recommend setting the breakpoint somewhere around here:
t = 0;
finished = true;
They do not share the same timer, you create a new Timer object for each Individual.
class Individual {
// ...
Timer rdyBreed;
Individual() {
// ...
rdyBreed = new Timer("rdyBreed", 0);
//...
The only way they could be sharing the same Timer is if you were setting rdyBreed elsewhere, but since you don't want that I recommend making it final.
If you did want to share the same Timer instance across all individuals then you could declare it static.
public class Physics {
private int weight;
private int durability;
private int xPos;
private int yPos;
private int x;
private int y;
private Level level;
int tileWidth = Main.WIDTH / 16;
int tileHeight = Main.HEIGHT / 16;
public Physics(Level level) {
this.level = level;
}
private void timer(int time) {//this is a subroutine that creates a slight pause
long currTime = System.currentTimeMillis();//this is getting the current time in miliseconds
long target = currTime + time;//this gets the current time and sets the target pause time to the current time plus the time you want to pause for in miliseconds
while(System.currentTimeMillis() < target);//this makes the program idle for the target amount of time
}
public void gravity(int weight,int durability, int xPos, int yPos, Tile tile, TileType tileTypes, boolean newblock){//this is the physics subroutine for when you place a block
if(tile.getType() == TileType.SKY) {//if the tile you clicked on is a sky tile
tile.setType(tileTypes, weight, durability);//it sets the new tiles type weight and durability to the ones that were entered dependant on which block the user chose from the pallet
timer(50);//this is the pause time between the block falling one y coordinate down
tile.setType(TileType.SKY, 0, 0);//once the tile is drawn one tile down it changeds the tile above it back to a sky tile
yPos++;//this the increases the y possition by one to foxus on the next block down
gravity(weight,durability, xPos, yPos, level.tiles[xPos][yPos],tileTypes,true);//this then calls the subroutine within itself and this keeps going until it hits the bottom
}
//if (newblock == true) {
if(tile.getType() != TileType.SKY){//once the tile below the tile we're focusing on is not a sky tile this if statement is run
Tile tile1 = level.tiles[xPos][yPos-1];//it sets the tile were focusing on to the tile above the non skytile
tile1.setType(tileTypes, weight, durability);//it then sets this tile to the tile you chose from the pallet
weight = tile1.getWeight();
durability = tile1.getDurability();
System.out.println(weight+"kg, "+durability);
}
//}
//if (newblock == false) {
//}
}
public void blockDestruction(Tile tile) {
}
public void tick(int x, int y) {
Tile tile = level.tiles[x][y];
Tile tile1 = level.tiles[x][y-1];
TileType tilecheck = tile1.getType();
if(tile1.getType() != TileType.SKY && tile1.getType() != TileType.GRASS) {
gravity(tile.getWeight(),tile.getDurability(),x,y,tile,tile.getType(),false);
y = y-1;
/*for (int i = y-1; i>0;i--) {
if (tilecheck != TileType.SKY) {
tile1 = level.tiles[x][i];
tilecheck = tile1.getType();
}else
tile1 = level.tiles[x][i+1];
tile1.setType(TileType.SKY);
}*/
}
}
}
the tick method at the end there is what I'm struggling with what im trying to create is a method that i would be able to put inside of my tick method in my main class which will check all blocks to see if there is a block underneath them and if not then it will apply the gravity method to that block. Sorry for the code being such a mess ive been trying loads of solutions and have gotten completely stuck so it is currently a bit of a mess if anything need explaining then please let me know.
this is what the program looks like
I am trying to create a fading/transitioning colors algorithm between two colors and a timer; the timer will determine how fast the colors swap between one another. The only problem is: the more fading transition objects I add in, the faster they all go. For example: If I add in one StandardFade (the class) object, it will run at the timer (alpha, in the code) I give it. However, if more objects that do 'fade' appear on the screen, the timer is no longer relevant, and they all go at the same rate, faster and faster with each object. Can anyone explain why?
//Test Game Class, Implements the StandardFades
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
public class TestGame extends Canvas implements Runnable{
private static final long serialVersionUID = -7267473597604645224L;
private Thread thread;
private boolean running = false;
private boolean consoleFPS = true;
private boolean titleFPS = true;
private TestWindow window;
//Fade objects, two is for a really long timer to test to see if they would transition at different times
StandardFade one = new StandardFade(Color.RED,Color.BLUE,.005);
StandardFade two = new StandardFade(Color.RED,Color.GREEN,.00000000000000001);
StandardFade three = new StandardFade(Color.RED,Color.YELLOW,.000005);
private int currentFPS;
private int frames;
public int levelNum;
public TestGame(int width, int height, String title){
this.window = new TestWindow(width,height, title, this);
this.initFades();
this.start();
}
private synchronized void start(){
if(running)
return;
else{
this.thread = new Thread(this);
this.thread.start();
this.running = true;
}
}
private synchronized void stop(){
if(!this.running)
return;
else{
try{
this.thread.join();
}catch(InterruptedException e){
e.printStackTrace();
}
this.running = false;
System.exit(0);
}
}
/**
* This game loop was provided by online sources, though there are many examples
* of a game loop online.
*
* #author RealTutsGML
*/
public void run() {
requestFocus(); //Focuses the click/input on the frame/canvas.
long lastTime = System.nanoTime(); //The current system's nanotime.
double ns = 1000000000.0 / 60.0; //Retrieves how many nano-seconds are currently in one tick/update.
double delta = 0; //How many unprocessed nanoseconds have gone by so far.
long timer = System.currentTimeMillis();
int frames = 0; //The frames per second.
int updates = 0; //The updates per second.
while (running) {
boolean renderable = false; //Determines if the game should render the actual graphics.
long now = System.nanoTime();//At this point, the current system's nanotime once again.
delta += (now - lastTime) / ns;
lastTime = now;
//If the amount of unprocessed ticks is or goes above one...
//Also determines if the game should update or not/render. Approximately sixty frames per second.
while (delta >= 1) {
tick();
delta--;
updates++;
renderable = true;
}
if(renderable){
frames++;
render();
}
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println(frames);
updates = 0;
frames = 0;
}
}
this.stop();
}
/**
* This method should tick everything that needs to be updated via positioning,
* mouse input, etc.
*/
private void tick(){
/********************PUT ALL TICKABLE METHODS IN THIS METHOD; ALL CALCULATIONS, EVERYTHING*********************/
//this.stdFadeHandler.get(0).tick();
//this.stdFadeHandler.tick();
one.tick();
two.tick();
three.tick();
/**********************************END OF TICK METHOD INFORMATION AND METHODS******************************/
}
private void render(){
BufferStrategy bs = this.getBufferStrategy();
if(bs == null){
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLACK);
g2.fillRect(0, 0, window.getWidth(), window.getHeight());
/*******************PLACE ALL DRAWING INSTRUCTIONS WITHIN THIS SECTION OF THE RENDER METHOD*************************/
g2.setColor(one.getColor());
g2.fillRect(20, 20, 200, 200);
g2.setColor(two.getColor());
g2.fillRect(20, 300, 200, 200);
g2.setColor(three.getColor());
g2.fillRect(20, 540, 200, 200);
//this.stdFadeHandler
/*******************DO NOT PLACE ANY MORE DRAWING INSTRUCTIONS WITHIN THIS SECTION OF THE RENDER METHOD***************/
g.dispose();
g2.dispose();
bs.show();
}
private void initFades(){
}
public static void main(String[] args){
TestGame stdGame = new TestGame(800,800,"Test Standard Game");
}
Below is the actual class that MAKES the frames: StandardFade
import java.awt.Color;
import java.awt.Graphics2D;
public class StandardFade {
private static float time = 0;
private static boolean firstColor = true;
private Color color1;
private Color color2;
private double alpha;
private Color fadeColor;
/**
* Lines that implement the
* r,g and b values come from Princeton University; I will author
* them in at the bottom.
*
* Method takes two parameters and fades them into one another according to a
* timer/clock value: alpha.
* #param c1 First color to be used.
* #param c2 Second color to be used.
* #param alpha How fast colors should shift. 0 <= n <= 1.
* Closer value is to zero, the longer it will take to shift.
* ***Important note about alpha: for non-seizure inducing colors, alpha <= .0005***
*
* The issue that is occurring is that, no matter what I do, no matter if I make
* different StandardFade objects and assign them, they will always render at the
* same rate, and faster and faster, depending on how many objects are fading.
*
* #return new Color based on r, g, and b values calculated.
*
* #author (Only code utilized was lines 58-60):
* http://introcs.cs.princeton.edu/java/31datatype/Fade.java.html
*/
public StandardFade(Color c1, Color c2, double alpha){
this.color1 = c1;
this.color2 = c2;
this.alpha = alpha;
}
public void tick() {
if(time <= 1f && firstColor){
time += alpha;
}
else{
firstColor = false;
}
if(time >= 0f && !firstColor)
time -= alpha;
else{
firstColor = true;
}
//System.out.println(time);
short r = (short) (time * color2.getRed() + (1 - time) * color1.getRed());
short g = (short) (time * color2.getGreen() + (1 - time) * color1.getGreen());
short b = (short) (time * color2.getBlue() + (1 - time) * color1.getBlue());
if(r > 255) r = 255;
if(g > 255) g = 255;
if(b > 255) b = 255;
if(r < 0) r = 0;
if(g < 0) g = 0;
if(b < 0) b = 0;
this.fadeColor = new Color(r, g, b);
}
public Color getColor(){
return this.fadeColor;
}
The only problem is: the more fading transition objects I add in, the faster they all go
private static float time = 0;
You are using a static variable for the time. This variable is shared by all instances of the ColorFade class. So each fading objects updates the same variable.
Don't use a static variable (just get rid of the static keyword). Each fading object needs its own "time" variable.
private float time = 0;
I also question if the firstColor variable should be static.
That's quite a lot of code to go through (try to reduce your problem down as much as possible in future) but based on your description of the problem - it gets faster the more objects you have - I'm guessing your problem is this line:
private static float time = 0;
I assume you're aware that static fields are shared between class instances. They do not each store their own separate value.
This causes problems because each instance increments the time value every time the tick method is called. That's okay when there is only one instance, but problematic when there are more.
Simply remove the static keyword and it should work properly:
private float time = 0;
class DrawPane extends JPanel
{
//size is the size of the square, x and y are position coords
double size = 1, x = 0, y = 0;
double start = (-1) * size;
public void paintComponent(Graphics shape)
{
for(x = start; x <= scWidth; x += size)
{
shape.drawRect((int)x, (int)y , (int)size, (int)size);
//When a row is finished drawing, add another
if(x >= scWidth)
{
x = start; y += size;
}
//Redraws the entire grid; makes the for loop infnite
else if(y >= scHeight)
{
x = start; y = start;
}
}
}
}
I'm confused as to why JPanel refuses to work with the loop once I make it infinite. How would I go about allowing it to do so?
When you make the loop "infinite" you effectively tie up and freeze the Swing event thread preventing Swing from doing anything. Instead use a Swing Timer to drive your animation.
e.g.,
class DrawPane extends JPanel {
//size is the size of the square, x and y are position coords
double size = 1, x = 0, y = 0;
double start = (-1) * size;
public DrawPane() {
int timerDelay = 200;
new Timer(timerDelay, new ActionListener(){
public void actionPerformed(ActionEvent e) {
x += size;
if (x >= scWidth) {
x = start;
y += size;
}
if (y >= scHeight) {
x = start;
y = start;
}
repaint();
}
}).start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g); // Don't forget to call this!
g.drawRect((int)x, (int)y , (int)size, (int)size);
}
}
The paint function is supposed to update the Paint and get out of the way. You really shouldn't be putting in complex logic and definitely not infinite loops there.
Just do what you have (except get rid of the reset stuff that makes your loop infinite) and put repaint() in an infinite loop (preferably with some timer logic) somewhere else in your program.
It will never break out of the paintComponent loop and update the GUI. The GUI will only update once the paintComponent method finishes. If you want to make the loop infinite, you need to take the code out of your event handler and be calling repaint() from elsewhere, possibly using a timer to do so.
I try to create a very simple physics engine for my study (processing used for a interactive installation).
The target is to have a ground covered with balls that you can throw around with gestures (based on Kinect information).
Therefor I need to do some basic physic simulation like bouncing and thats what I started with. So there are just balls falling down and bouncing. I simulated the air resistance with a simple 0.995f multiplication on the speed if the ball moves up. Works nice and looks realistic. The main problem is, that the balls never stay calm on the ground. Instead they start to tremble on the ground. That means there is a movement of 1 or 2 pixels up and down.
How can I prevent that without implementing some "borders" on which I set the position directly to the bottom and the speed to zero?
My applet:
public class BubblePhysicApplet extends PApplet {
public static int width = 640;
public static int height = 480;
long lastTime = -1;
Bubble[] mBubbles = new Bubble[10];
Random mRandom = new Random();
public void setup() {
// size(width, height, OPENGL);
size(width, height, P2D);
for (int i = 0; i < mBubbles.length; i++) {
mBubbles[i] = new Bubble(mRandom.nextInt(width), mRandom.nextInt(height), 50);
}
lastTime = System.currentTimeMillis();
}
public void draw() {
background(0);
long tmp = System.currentTimeMillis();
long elapsed = tmp - lastTime;
for (Bubble bubble : mBubbles) {
bubble.animate(elapsed);
bubble.draw(this);
}
lastTime = System.currentTimeMillis();
}
}
The ball/bubble:
public class Bubble {
float mX;
float mY;
float mSize;
float mSpeedX = 0;
float mSpeedY = 0;
public Bubble(int x, int y, int size) {
mX = x;
mY = y;
mSize = size;
}
public void draw(PApplet applet) {
applet.stroke(255);
applet.noFill();
applet.ellipseMode(PApplet.CENTER);
applet.ellipse(mX, mY, mSize, mSize);
}
public void animate(long elapsed) {
updateSpeedY(elapsed);
if (mSpeedX != 0 || mSpeedY != 0) {
checkBorders();
}
}
private void checkBorders() {
if (mY > BubblePhysicApplet.height - mSize / 2) {
mY = 2 * BubblePhysicApplet.height - (mY + mSize);
mSpeedY = -mSpeedY;
}
if (mX > BubblePhysicApplet.width) {
mX = BubblePhysicApplet.width - (mX - BubblePhysicApplet.width);
mSpeedX = -mSpeedX;
}
}
private void updateSpeedX() {
}
private void updateSpeedY(long elapsed) {
mSpeedY += (elapsed / 1000f) * 9.81f;
if (mSpeedY < 0) {
mSpeedY *= 0.95f;
}
mY += mSpeedY;
}
}
It's not only air resistance that slows the ball down, but the fact that it's not perfectly elastic as this line suggests: mSpeedY = -mSpeedY;
The ball absorbs energy when it squishes against the floor before it bounces back, so it doesn't bounce as high. Try a real super ball. I seem to remember it only bounces 80% as high or so. You might try:
mSpeedY = - (0.8 * mSpeedY);
you have to fix your check borders method, read this answer I just gave a complete formulas needed for realistic physical simulation. and it's also more realistic if you move objects using hist method (p = v*dt + 1/2*adtdt)
The problem is that in updateSpeedY we have mSpeedY += (elapsed / 1000f) * 9.81f; even when there is a collision. That said collision is detected later in checkBorders where the speed is flipped mSpeedY = -mSpeedY;. The problem is that if the ball is hitting the floor with a speed near 0, it bounces with a speed of 0 + (elapsed / 1000f) * 9.81f;!!
You have to rethink your code.
in the same fashion you used a friction factor for the air, you can also include a friction factor for the contact with the ground, and which even higher values, so at each contact, it starts to lose eneger rapidly and finally stops