How to make Object generator in the AnimationTimer loop JavaFX - java

I'm making simple spaceship game on JavaFX. I have class SpaceShip and i want to make random objects from this class in my main game loop on random starting position(in interval of 5 seconds for example). I try to use Timer schedule() method for the task. The problem is that i can't get clear image of the spaceship, it disappears and show in other point because of the constantly looping. Can someone help me with advice how to handle with this.
My game loop:
new AnimationTimer() {
#Override
public void handle(long currentNanoTime) {
double t = (currentNanoTime - startNanoTime) / 1000000000.0;
double xMoving = ((100 * t) % (canvas.getWidth() + 100));
double x = 232 + 128 * Math.cos(t);
double y = 232 + 128 * Math.sin(t);
//background image clears canvas
gc.drawImage(space, 0, 0);
gc.drawImage(earth, x, y);
gc.drawImage(sun, 196, 196);
// draw UFO
gc.drawImage(ufo.getFrame(t), 100, 25);
//draw spaceShip
SpaceShip.generate(new SpaceShip(spaceShipImageArr, 0.100, gc, t, xMoving - 100, (randomNum + 150)));
//timer schedule
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
SpaceShip.generate(new SpaceShip(spaceShipImageArr, 0.100, gc, t, xMoving - 100, (randomNum + 230)));
}
}, 5000);
}
}.start();
And the SpaceShip class:
package objectClasses;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
public class SpaceShip{
private final GraphicsContext gc;
private final double frame;
private final double y;
private final double x;
private AnimatedImage object;
public SpaceShip(Image[] arr, double duration, GraphicsContext gc, double frame, double x, double y) {
object = new AnimatedImage();
this.object.frames = arr;
this.object.duration = duration;
this.gc = gc;
this.frame = frame;
this.y = y;
this.x = x;
}
private void drawShip() {
this.gc.drawImage(this.object.getFrame(frame), x, y);
}
public static void generate(SpaceShip spaceShip) {
spaceShip.drawShip();
}
}

This is the culprit.
SpaceShip.generate(new SpaceShip(spaceShipImageArr, 0.100, gc, t, xMoving - 100, (randomNum + 150)));
Basically, in each frame you create a new instance of SpaceShip. The blinking images are caused by space ships being created over and over again. That won't work.
You need to create these objects outside the game loop and store their references:
SpaceShip mySpaceShip = new SpaceShip(spaceShipImageArr, 0.100, gc, t, xMoving - 100, (randomNum + 150));
In the game loop you just update the position of your actors (that part is missing) and keep drawing them at new positions.
SpaceShip.generate(mySpaceShip);
Side note: you can remove the generate method altogether, make drawShip public, rename it to draw and simply call mySpaceShip.draw() - it will be more evident what's the role of your methods. It's perfectly acceptable for an object to draw itself.

Related

Way to repaint this JPanel after given count

Hello I would like to prevent graphics drawing and drawing again but I don't know how to do, I just want my panel delete all painted graphics and restart with same code. I tried some methods posted here but no one does the job.
public class Main extends JPanel implements ActionListener {
Timer timer;
private double angle = 444;
private double scale = 1;
private double delta = 0.0001;
RoundRectangle2D.Float r = new RoundRectangle2D.Float();
int counter = 0;
public Main() {
timer = new Timer(55, this);
timer.start();
}
public void paint(Graphics g) {
counter++;
int h = getHeight();
int w = getWidth();
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(new Color(randomNumber(0, 155), randomNumber(0, 255),randomNumber(0, 155), randomNumber(0, 255)));
drawCircles(g2d, getWidth()/2, getHeight()/2, 250);
if(counter > 200){
g2d.clearRect (0, 0, getWidth(), getHeight());
super.paintComponent(g2d);
counter = 0;
}
}
public int randomNumber(int min, int max) {
int c = new Random().nextInt((max - min) + 1);
return c;
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setUndecorated(true);
Dimension dim = new Dimension(Toolkit.getDefaultToolkit()
.getScreenSize().width, Toolkit.getDefaultToolkit()
.getScreenSize().height);
frame.setSize(dim);
frame.setLocation(0, 0);
frame.setBackground(new Color(0, 0, 0, 255));
frame.add(new Main());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
void drawCircles(Graphics graphics, int xMid, int yMid, int radius) {
// end recursion
if(radius < 5)
return;
// Draw circle
// start recursion
//left
drawCircles(graphics, xMid-radius, yMid, radius / 2);
((Graphics2D) graphics).rotate(angle);
graphics.drawOval(xMid - radius, yMid - radius, radius * 2, radius * 2);
//right
drawCircles(graphics, xMid+radius, yMid, radius / 2);
graphics.drawOval(xMid - radius, yMid - radius, radius * 2, radius * 2);
((Graphics2D) graphics).rotate(angle);
((Graphics2D) graphics).rotate(angle);
((Graphics2D) graphics).setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
((Graphics2D) graphics).setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
}
public void actionPerformed(ActionEvent e) {
if (scale < 0.01)
delta = -delta;
else if (scale > 0.99)
delta = -delta;
scale += delta;
angle += 0.001;
repaint();
}
}
I am not sure I understand you fully, but you can use a JToggleButton (for example) where is the toggle button is down it prevents drawing. I can see something like this inside your drawCircles() method:
void drawCircles(Graphics graphics, int xMid, int yMid, int radius)
{
if(!toggleBtn.isSelected() // the toggle button is pressed
{
// draw something
}
}
In your example, you are drawing two circles and two ovals. If I understood you correctly, you want to be able to pause in the middle of the method, for example, and only draw the first circle. Then, at some point, you want to continue drawing the two ovals and the remaining circle. Unfortunately, you cannot do that. You cannot stop (or pause) a method in the middle of it.
Methods have to execute to completion (whether to the end, or an exception is thrown). However, you can create some kind of task to draw ONE shape (for example, a circle). If you create multiple tasks, you can draw many circles. To accomplish this, you will need to learn about Concurrency and probably about Java Tasks. You can have these tasks execute in some kind of order and because of concurrency, you could pause and resume these drawing tasks the way I think you would want.

Java program not running correctly on MAC

I have recently started using a mac to develop on and i am having a strange problem.
Take the Program below:
public class Driver {
public static void main(String [ ] args) {
SolarSystem SSpanel = new SolarSystem(600, 600);
SSpanel.drawSolarObject(0, 0, 30, "YELLOW");
}
}
the SolarSystem class extends JFrame and basically when the new SolarSystem is created it makes a panel of that size.
the drawSolarObjects basically draws a circle of a certain colour and size. finishedDrawing actually makes the object appear on the panel.
The example above does work but I have more complex requirements which involve putting this into a while loop.
this is where it gets weird, if i run the below program with cmd on a windows computer it works fine and prints the yellow circle to the screen. On my mac, adding this while loop causes it to just create the panel but not paint the yellow circle.
public class Driver{
public static void main(String [ ] args) {
boolean oMove = true;
SolarSystem SSpanel = new SolarSystem(600, 600);
while(oMove){
SSpanel.drawSolarObject(0, 0, 30, "YELLOW");
SSpanel.finishedDrawing();
}
}
}
I put a print into my loop to check it was running through it and that showed that it was definitely running through the loop.
Does anyone know what could be causing this?
Ive am adding the functions so you can get a better picture
SolarSystem Constructer:
public SolarSystem(int width, int height)
{
this.width = width;
this.height = height;
this.setTitle("The Solar System");
this.setSize(width, height);
this.setBackground(Color.BLACK);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
drawSolarObject Function:
public void drawSolarObject(double distance, double angle, double diameter, String col)
{
Color colour = this.getColourFromString(col);
double centreOfRotationX = ((double) width) / 2.0;
double centreOfRotationY = ((double) height) / 2.0;
double rads = Math.toRadians(angle);
double x = (int) (centreOfRotationX + distance * Math.sin(rads)) - diameter / 2;
double y = (int) (centreOfRotationY + distance * Math.cos(rads)) - diameter / 2;
synchronized (this)
{
if (things.size() > 1000)
{
System.out.println("\n\n");
System.out.println(" ********************************************************* ");
System.out.println(" ***** Only 1000 Entities Supported per Solar System ***** ");
System.out.println(" ********************************************************* ");
System.out.println("\n\n");
this.dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
}
else
{
SolarObject t = new SolarObject((int)x, (int)y, (int)diameter, colour);
things.add(t);
}
}
}
finishedDrawing function:
public void finishedDrawing()
{
try
{
this.repaint();
Thread.sleep(30);
}
catch (Exception e) { }
synchronized (this)
{
things.clear();
}
}
This all works fine on a windows PC
Your code risks tying up the Swing event thread preventing it from drawing on your GUI, and effectively freezing your program. Instead use a Swing Timer, not a while loop to achieve your goal.
e.g.,
final SolarSystem SSpanel = new SolarSystem(600, 600);
int timerDelay = 100;
new Timer(timerDelay, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// do repeated action in here
}
}).start();
As an aside, I was going to place,
SSpanel.drawSolarObject(0, 0, 30, "YELLOW");
SSpanel.finishedDrawing();
inside my timer code, but it wouldn't make sense because this code isn't "dynamic" and doesn't change anything or do any animation.

How do I make the frame rate consistent?

I'm making simple 2D games. I have a game loop, and in the game loop i have an update
method. I make things move by adding 1 to xPos whenever it loops. This means that if you have a slow fps then everything goes in slow motion and if you have a high fps everything moves
really quick.
This is my code:
long fpsTimer;
int frames;
public void run(){
running = true;
fpsTimer = System.nanoTime();
while(running){
render();
update();
try{
Thread.sleep(6);
}catch(InterruptedException e){}
frames++;
if(System.nanoTime() >= fpsTimer+1000000000){
System.out.println(frames+" fps");
frames = 0;
fpsTimer = System.nanoTime();
}
}
}
All Code
import java.awt.*;
import java.awt.image.*;
import javax.swing.JFrame;
import java.awt.event.*;
public class Game extends Canvas implements Runnable{
public static final int WIDTH = 800;
public static final int HEIGHT = 300;
public JFrame f;
private String title = "Untitled Test";
private Image image;
private Sprite player;
public Game(){
player = new Sprite(100, 100);
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setMaximumSize(new Dimension(WIDTH, HEIGHT));
setMinimumSize(new Dimension(WIDTH, HEIGHT));
addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent e){
player.keyPressed(e.getKeyCode());
}
public void keyReleased(KeyEvent e){
player.keyReleased(e.getKeyCode());
}
});
}
public static void main(String[] args){
Game g = new Game();
g.f = new JFrame(g.title);
g.f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
g.f.add(g);
g.f.setResizable(false);
g.f.pack();
g.f.setLocationRelativeTo(null);
Thread gameLoop = new Thread(g);
gameLoop.start();
g.f.setVisible(true);
}
private void render(){
BufferStrategy bs = getBufferStrategy();
if(bs==null){
createBufferStrategy(3);
return;
}
image = createImage(WIDTH, HEIGHT);
Graphics g = image.getGraphics();
player.draw(g);
g.dispose();
Graphics bsg = bs.getDrawGraphics();
bsg.drawImage(image, 0, 0, WIDTH, HEIGHT, null);
bsg.dispose();
bs.show();
}
private void update(){
player.move();
}
long fpsTime;
int frames;
public void run(){
fpsTime = System.nanoTime();
while(true){
render();
update();
try{
Thread.sleep(6);
}catch(InterruptedException e){}
frames++;
if(System.nanoTime() >= fpsTime+1000000000){
System.out.println(frames+" fps");
frames = 0;
fpsTime = System.nanoTime();
}
}
}
}
First of all you should not have a constant sleep time. Instead, the value should be dynamically computed. Maybe its easier to use Timer#scheduleAtFixedRate(...) cause this already takes care of that for you.
Then, 6 ms per iteration seems much too less. 60 frames per second is ideal (if you just have 30, its ok too i think), so 16 ms is enough (or about 32 for 30 fps). (Note that the refresh rate of your screen is the upper limit - it should be about 60 Hz - more doesn't make sense).
Thirdly think about dynamically computing the moves of your objects. Instead of having a constant delta that you add to the coordinates you should better have some kind of 'move function' that calculates the coordinates based on the current time.
Let's say you want to move an object along the x-axis with a constant speed of pps pixels per second. The function for the x coordinate of that object would then be:
x(t) := x0 + (t - t0) * pps / 1000
(t is the time that has passed since start in ms, x0 is the initial x coordinate of the object when it appeared first, t0 is the time at which the object appeared, pps is the number of pixels the object should move per second).
This makes your objects move with the same speed - no matter what framerate you've got.
Note that this approach gets more complex if your objects should react to user input or other events (like collisions etc.).

What does a thread 3 NullPointerException mean in my code?

I keep getting this error when I compile and run my Java program using the ACM library
Exception in thread "Thread-3" java.lang.NullPointerException
at SpaceTravel.getBlackHoleDistance(SpaceTravel.java:148)
at SpaceTravel.gameOverBlackHole(SpaceTravel.java:132)
at BlackHole.oneTimeStep(BlackHole.java:84)
at BlackHole.run(BlackHole.java:45)
at java.lang.Thread.run(Unknown Source)
This is the game class:
import acm.graphics.*;
import acm.program.*;
import acm.util.*;
import java.awt.*;
public class SpaceTravel extends GraphicsProgram {
// specify the size of the window
public static int APPLICATION_WIDTH = 1280;
public static int APPLICATION_HEIGHT = 600;
// class constants
private static final double
PLANET_SIZE = 80,
BLACK_HOLE_SIZE = 100,
STAR_SIZE = 2;
// instance variables
private GOval ice, fire, iron;
private GPoint lastPoint;
private boolean isDragging = false;
private GLabel gameOverText, goal, win;
private BlackHole blackhole1, blackhole2;
private RandomGenerator rand = new RandomGenerator();
// init method, draw the graphics objects
public void init() {
setBackground(Color.BLACK);
// call the randomly colored stars method
drawStars();
// call 1 instance of BlackHole class
blackhole1 = new BlackHole(BLACK_HOLE_SIZE, 5, 2, this);
add(blackhole1, APPLICATION_WIDTH-400, APPLICATION_HEIGHT/2 );
new Thread(blackhole1).start();
// call 1 instance of BlackHole class, but name it differently
blackhole2 = new BlackHole(BLACK_HOLE_SIZE, 3, -4, this);
add(blackhole2, APPLICATION_WIDTH-200, APPLICATION_HEIGHT/2 );
new Thread(blackhole2).start();
// draw fire planet
fire = drawCircleCentered(100, 400, PLANET_SIZE);
add(fire);
fire.setFilled(true);
fire.setColor(Color.RED);
// draw ice planet
ice = drawCircleCentered(100, 100, PLANET_SIZE);
add(ice);
ice.setFilled(true);
ice.setColor(Color.BLUE);
// draw iron planet
iron = drawCircleCentered(100, 250, PLANET_SIZE);
add(iron);
iron.setFilled(true);
Color grey = new Color(34, 34, 34);
iron.setColor(grey);
// game over text
gameOverText = new GLabel ("GAME OVER", APPLICATION_WIDTH/2 - 250, APPLICATION_HEIGHT/2);
gameOverText.setColor(Color.RED);
gameOverText.setFont(new Font("DEFAULT_FONT", Font.BOLD, 90));
// goal text
goal = new GLabel ("GOAL", APPLICATION_WIDTH-150, APPLICATION_HEIGHT/2);
goal.setColor(Color.RED);
goal.setFont(new Font("DEFAULT_FONT", Font.BOLD, 20));
add(goal);
// win text
win = new GLabel ("WINRAR IS YOU!", APPLICATION_WIDTH/2 - 350, APPLICATION_HEIGHT/2);
win.setColor(Color.RED);
win.setFont(new Font("DEFAULT_FONT", Font.BOLD, 90));
}
// checker method if the ice and fire plantes touch, call the game over method below.
private void checkFireIce(GOval fire, GOval ice) {
if(getDistance(fire, ice) < PLANET_SIZE ) {
gameOver(fire);
}
}
// checker method for when fire planet gets to the goal text, call the game winning method below
private void checkPlanetsGoal() {
if(fire.getBounds().intersects(goal.getBounds())) {
winGame();
}
}
// start dragging if the ball is pressed
public void mousePressed(GPoint point) {
if (ice.contains(point)) {
isDragging = true;
lastPoint = point;
}
}
// move the ball when it is dragged, and call checking methods for game winning or game over conditions
public void mouseDragged(GPoint point) {
checkFireIce(fire, ice);
checkPlanetsGoal();
if (isDragging) {
ice.move(point.getX()-lastPoint.getX(),
point.getY()-lastPoint.getY());
lastPoint = point;
// bump the planets
bump(ice, iron);
bump(iron, fire);
}
}
// checking method for if any of the planets have touched an instance of black hole
public void gameOverBlackHole(BlackHole blackhole) {
double a = getBlackHoleDistance(fire, blackhole);
double b = getBlackHoleDistance(ice, blackhole);
double c = getBlackHoleDistance(iron, blackhole);
if(a < BLACK_HOLE_SIZE/2 + PLANET_SIZE/2) {
gameOver(fire);
}
if(b < BLACK_HOLE_SIZE/2 + PLANET_SIZE/2) {
gameOver(ice);
}
if(c < BLACK_HOLE_SIZE/2 + PLANET_SIZE/2) {
gameOver(iron);
}
}
// get distance between a black hole instance and a planet
private double getBlackHoleDistance(GOval planet, BlackHole blackhole) {
return GMath.distance(planet.getX()+PLANET_SIZE/2, planet.getY()+PLANET_SIZE/2,
blackhole.getX(), blackhole.getY());
}
// bump helper method, calculates how much to move a tangent planet by when it is being bumped by another
private void bump(GOval planet1, GOval planet2) {
double offset = PLANET_SIZE+1.5 - getDistance(planet1, planet2);
if (offset > 0) {
planet2.move(offset*(planet2.getX()-planet1.getX())/PLANET_SIZE,
offset*(planet2.getY()-planet1.getY())/PLANET_SIZE);
}
}
// a helper method, compute the distance between the centers of the balls
private double getDistance(GOval planet1, GOval planet2) {
return GMath.distance(planet1.getX()+PLANET_SIZE/2, planet1.getY()+PLANET_SIZE/2,
planet2.getX()+PLANET_SIZE/2, planet2.getY()+PLANET_SIZE/2);
}
// a helper method, draw a circle centered at the given location
private GOval drawCircleCentered(double centerX, double centerY, double size) {
return new GOval(centerX-size/2, centerY-size/2, size, size);
}
// a helper method, draw randomly colored stars
private void drawStars() {
for (int i = 0; i < 1000; i++) {
GOval star = drawCircleCentered(rand.nextDouble(0, APPLICATION_WIDTH), rand.nextDouble(0, APPLICATION_HEIGHT), STAR_SIZE);
add(star);
star.setFilled(true);
star.setColor(rand.nextColor());
}
}
// helper method to switch dragging off when mouse is released from window
public void mouseReleased(GPoint point) {
isDragging = false;
}
// helper method to switch dragging off, remove the planet that touched the goal, and display game over text
public void gameOver(GOval planet) {
isDragging = false;
remove(planet);
add(gameOverText);
}
// helper method to switch dragging off, remove planets, and display win text
private void winGame() {
isDragging = false;
add(win);
remove(fire);
remove(ice);
remove(iron);
remove(goal);
}
}
And this is the class that creates blackholes which can end the game.
// import libraries
import acm.program.*;
import acm.graphics.*;
import acm.util.*;
import java.awt.*;
import java.util.*;
public class BlackHole extends GCompound implements Runnable {
// instance variables
private double size, xSpeed, ySpeed;
private SpaceTravel game;
private boolean stopHammerTime = false;
// constructor for BlackHole
public BlackHole(double size, double xSpeed, double ySpeed, SpaceTravel game) {
// save the parameters size, xSpeed, ySpeed, centerX, centerY, and game
this.size = size;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
this.game = game;
// call method drawBlackhole
drawBlackhole(0, 0, size, 3, 40);
}
// run method, move the black hole until it hits a planet || stopHammerTime = true
public void run() {
while(!stopHammerTime) {
oneTimeStep();
}
}
// helper method, creates a black hole
private void drawBlackhole(double centerX, double centerY, double size, double gap, int layers) {
for (int i = 0; i < layers; i++) {
// this gradient color will lighten each time the for loop completes
Color gradient = new Color(0 + 5*i, 0 + 5*i, 0 + 5*i);
GOval ring = drawCircleCentered(centerX, centerY, size-gap*2*i);
add(ring);
ring.setFilled(true);
ring.setColor(gradient);
}
}
// a helper method, draw a circle centered at the given location
private GOval drawCircleCentered(double centerX, double centerY, double size) {
return new GOval(centerX-size/2, centerY-size/2, size, size);
}
// a helper method, move the blackHole in oneTimeStep
private void oneTimeStep() {
double x = getX();
double y = getY();
// if the black hole hits the left or the right wall, reverse the x-speed
if (x < size/2 || x+size/2 > game.getWidth()) xSpeed = -xSpeed;
// if the black hole hits the top or the bottom wall, reverse the y-speed
if (y < size/2 || y+size/2 > game.getHeight()) ySpeed = -ySpeed;
// move the black hole by a small interval, incorporating changes from if statements
move(xSpeed, ySpeed);
// check if a planet has touched a blackhole
game.gameOverBlackHole(this);
// delay
pause(20);
}
}
I'm pretty sure that I have the calling of the classes correct, but for some reason, the blackhole2 that is called just crashes.
Exception in thread "Thread-3" java.lang.NullPointerException
It is NullPointerException happened while executing thread "Thread-3"
At line 148 in SpaceTravel.java file.
It seems while Thread-3 executing, line 148 is trying to do some operation on null reference, which results in NullPointerException.
The code that apparently crashes looks like this:
return GMath.distance(planet.getX()+PLANET_SIZE/2, planet.getY()+PLANET_SIZE/2,
blackhole.getX(), blackhole.getY());
Most likely either planet or blackhole parameter is null. Use debugger or println() to figure out which one. This piece of code is called from three places:
double a = getBlackHoleDistance(fire, blackhole);
double b = getBlackHoleDistance(ice, blackhole);
double c = getBlackHoleDistance(iron, blackhole);
Your line numbers are a bit off but I dare to say it's the first line, so either fire or blackhole is null.
A Quick Analysis:
gameOverBlackHole method gets called form BlackHole class this is causing problems because remove happens inside that method. If remove happens while one thread is executing then there will NPE
This a can be a example of what might go wrong if you share state of object between multiple threads

How do you move an object in a wavy pattern?

I know the following code will move an object in a straight line. How can I get the object to travel in a wavy line? I know that something extra is required for the x variable.
public void draw(Graphics2D g)
{
g.setColor(Color.WHITE);
g.fillOval ((int) (x - r), (int) (y - r), (int)
(2 * r),
(int) (2 * r));
y++;
if (y - r > height)
y = -r;
}
Use the sine or cosine function to calculate y as a function of x.
Multiply the sine or cosine function to increase the amplitude (how high it goes)
y = 100 * sin(x) // will make it have peaks of -100 and 100
Divide the x to increase the period. (distance between peaks)
y = sin(x/2) // will make it take twice the x distance between peaks.
Something like this:
public void draw(Graphics2D g)
{
g.setColor(Color.WHITE);
g.fillOval ((int) (x - r), (int) (y - r), (int)
(2 * r),
(int) (2 * r));
x++; // Left to right movement
// Example, modify the multipliers as necessary
y = 100 * Math.sin(Math.toDegrees(x/4))
}
Including a sin(x) or cos(x) in your function will provide a regular wave pattern, irregular pattern needs a more sophisticated function
I know you already accepted an answer, but here's something to draw additional inspiration from that I whipped up...
package wavy;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Wavy {
public static void main(String[] args) {
final JFrame frame = new JFrame("Wavy!");
final WavyPanel wp = new WavyPanel();
frame.getContentPane().add(wp, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final Ticker t = new Ticker(wp);
final Repainter r = new Repainter(wp);
frame.pack();
frame.setVisible(true);
final Timer tickTimer = new Timer();
final Timer paintTimer = new Timer();
paintTimer.schedule(r, 1000, 50);
tickTimer.schedule(t, 1000, 10);
}
private static class WavyPanel extends JPanel {
private final Dimension size = new Dimension(640, 480);
private int amplitude = 50;
private int frequency = 5;
private int x = 0;
private double y = size.height / 2;
private int yBase = 0;
WavyPanel() {
super(true);
}
#Override
protected void paintComponent(final Graphics g) {
final Graphics2D g2 = (Graphics2D)g;
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, size.width, size.height);
g2.setColor(Color.BLACK);
g2.fillOval(x, (int)y, 30, 30);
}
#Override
public Dimension getPreferredSize() {
return size;
}
#Override
public Dimension getMinimumSize() {
return size;
}
#Override
public Dimension getMaximumSize() {
return size;
}
public void tick() {
//Move a pixel to the right; loop over to the left when reaching edge
x = (++x) % size.width;
//Length of one full wave = panel width divided by frequency
final int waveLength = size.width / frequency;
//Incrementing yBase; capping off at wavelength
yBase = (++yBase) % waveLength;
//Normalizing to [0..1]
final double normalized = (double)yBase / (double)waveLength;
//Full wave at 2*pi, means...
final double radians = normalized * Math.PI * 2;
//Getting the sine
final double sine = Math.sin(radians);
//Multiplying with amplitude, add to center position and we have our y
y = (int)(sine * amplitude) + size.height/2;
}
}
private static class Ticker extends TimerTask {
private final WavyPanel panel;
Ticker(final WavyPanel panel) {
this.panel = panel;
}
#Override
public void run() {
panel.tick();
}
}
private static class Repainter extends TimerTask {
private final WavyPanel panel;
Repainter(final WavyPanel panel) {
this.panel = panel;
}
#Override
public void run() {
panel.repaint();
}
}
}
This should run at an approximate 20 frames per second. You can increase this by setting the second argument of paintTimer.schedule(r, 1000, 50) lower. The speed of movement can be altered by lowering (speeding up) or increasing (slower) the second argument of tickTimer.schedule(t, 1000, 50).
Changing the amplitude field of WavyPanel will change how high/low the circle moves. Changing the frequency to a higher value will result in shorter waves, while a lower value will produce longer waves.
With some additional work you could add in controls to change the amplitude and frequency on-the-fly. Some additional notes:
You may wish to add some safeguard to the tick() method to make sure that when one invocation is already running, additional ones are skipped until the first one is done. Otherwise the calculations could fail for short tick intervals. A semaphore could be used here.
Since trigonometric calculations aren't exactly the cheapest, you may consider caching some results (e.g. in an array) for re-use if many similar animations are to be played or if there's a lot more drawing going on.
I hope I'm interpreting this right. Could use the sine or cosine of either your x or y coordinate. I'm not at a machine with java so I can't make an example at the moment..
You're right that you need to update both the x and y variables to get a wavy line. Here's the general strategy for a horizontal line that is wavy up and down:
Choose a function f(x) that has the shape you want. This will be used to calculate values for y. (For instance, you can use y = amplitude * Math.sin(frequency * x) to get a regular sine wave of a given amplitude and frequency.)
If necessary, write the code that implements your function.
Set x to some initial value.
In draw, before you paint the oval, calculate y = f(x);. Paint the oval and then increment x. If necessary, reset x so it stays in range.
If you want a vertical line that is wavy left and right, just reverse the roles of x and y in the above. If you want the oval to go in the reverse direction, just decrement instead of increment in step 4.
this sample is for point(Line with one length) on sinus graph and clock using.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class RunSwing extends JPanel {
static int x1 = 500;
static int y1 = 500;
static int x2 = x1;
static int y2 = y1;
final static int vectorLength = 100;
final static int sinx2 = x2;
final static int siny2 = y2;
static double count = 0;
private static RunSwing run = new RunSwing();
final Timer print = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
//increaseSinusGraph();
increaseClockVector();
count+=6; //for clock for 1 second
/*count++;//for sinus*/
if (count % 360 == 0)
System.out.println((count / 360) + " minute passed");
}
});
RunSwing() {
print.start();
}
public static void main(String[] args) {
JFrame frame = new JFrame("amir");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(run);
frame.setSize(1100, 700);
frame.setVisible(true);
}
static void increaseClockVector() {
double cos = Math.cos(Math.toRadians(count));
double sin = Math.sin(Math.toRadians(count));
y2 = siny2 + (int) (vectorLength * sin);
x2 = sinx2 + (int) (vectorLength * cos);
}
static void increaseSinusGraph() {
double sin = Math.sin(Math.toRadians(count));
y2 = siny2 + (int) (vectorLength * sin);
x2++;
}
private void createPoint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawLine(x2, y2, x2 + 1, y2 + 1);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(0, 0, 0));
g.drawLine(x1, y1, x2, y2);//for clock
/*g.drawLine(x2, y2, x2+1, y2+1);//for sinus*/
repaint();
}
}

Categories

Resources