Kali ini saya akan memberikan sebuah tutorial mengenai bagaimana membuat animasi bola billiard dengan javaFX yang preview-nya dapat dilihat pada gambar berikut:
Adapun source codenya adalah sebagai berikut:
package fjr.collision;
import java.util.ArrayList;
import java.util.Random;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBuilder;
import javafx.scene.shape.Circle;
import javafx.scene.shape.CircleBuilder;
import javafx.stage.Stage;
import javafx.util.Duration;
public class BilliardBallDetection extends Application {
ArrayList listParticle;
ArrayList listCircle;
double xwidth = 400;
double ywidth = 400;
double timeStep = 2.5;
double numberParticle = 9;
Random rand = new Random();
double dmin = 30.0;
double xmax = xwidth;
double xmin = 0.0;
double ymax = ywidth;
double ymin = 0.0;
double size = 0.0;
double dmax = 0.0;
double vmax = 3.0;
Timeline animation;
Button buttonPause, buttonPlay;
@Override
public void start(Stage primarySrage) throws Exception {
// TODO Auto-generated method stub
generateParticle();
removeOverlap(listParticle);
generateCircle();
Group root = new Group();
for(int i =0; i< listCircle.size(); i++){
Circle c = listCircle.get(i);
root.getChildren().add(c);
}
animation = new Timeline();
animation.setCycleCount(Timeline.INDEFINITE);
animation.setAutoReverse(false);
KeyFrame kf = new KeyFrame(Duration.millis(20), new EventHandler(){
@Override
public void handle(ActionEvent event) {
// TODO Auto-generated method stub
checkBoundary();
calculateCollision();
moveParticle();
redrawCircle();
}
});
animation.getKeyFrames().add(kf);
root.getChildren().addAll(buttonPlay = ButtonBuilder.create()
.text("PLAY").onAction(new EventHandler() {
@Override
public void handle(ActionEvent arg0) {
// TODO Auto-generated method stub
animation.play();
}
}).translateX(10).translateY(10)
.build(),
buttonPause = ButtonBuilder.create()
.text("PAUSE").translateX(80).translateY(10)
.onAction(new EventHandler() {
@Override
public void handle(ActionEvent arg0) {
// TODO Auto-generated method stub
animation.pause();
}
})
.build()
);
primarySrage.setScene(new Scene(root, xwidth, ywidth));
primarySrage.show();
}
private void redrawCircle(){
for(int i=0; i< listCircle.size(); i++){
Circle c = listCircle.get(i);
Particle p = listParticle.get(i);
c.setCenterX(p.x);
c.setCenterY(p.y);
}
}
private void generateCircle(){
listCircle = new ArrayList<>();
for(int i=0; i< listParticle.size(); i++){
Particle p = listParticle.get(i);
Circle c = CircleBuilder.create()
.centerX(p.x).centerY(p.y).radius(dmin)
.build();
listCircle.add(c);
}
}
private void generateParticle() {
listParticle = new ArrayList<>();
for (int i = 0; i < numberParticle; i++) {
Particle particle = new Particle(rand.nextDouble() * xwidth,
rand.nextDouble() * ywidth, rand.nextDouble() * vmax,
rand.nextDouble() * vmax);
listParticle.add(particle);
}
}
private void calculateCollision(){
for(int i=0; i< listParticle.size(); i++){
Particle p1 = listParticle.get(i);
for(int j= i+1; j< listParticle.size(); j++ ){
Particle p2 = listParticle.get(j);
chekCollision(p1, p2);
}
}
}
public void chekCollision(Particle p1, Particle p2) {
if (isOverlap(p1, p2)) {
double deltaX = p1.x - p2.x;
double deltaY = p1.y - p2.y;
double distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
deltaX = deltaX/ distance;
deltaY = deltaY/distance;
double aci = p1.vx * deltaX + p1.vy * deltaY;
double bci = p2.vx * deltaX + p2.vy * deltaY;
double acf = bci;
double bcf = aci;
p1.vx = p1.vx + (acf - aci) * deltaX;
p1.vy = p1.vy + (acf - aci) * deltaY;
p2.vx = p2.vx + (bcf - bci) * deltaX;
p2.vy = p2.vy + (bcf - bci) * deltaY;
}
}
private void moveParticle() {
for (int i = 0; i < listParticle.size(); i++) {
Particle p = listParticle.get(i);
p.x += p.vx * timeStep;
p.y += p.vy * timeStep;
}
}
public void removeOverlap(ArrayList list) {
for (int i = 0; i < list.size(); i++) {
Particle p1 = list.get(i);
for (int j = list.size() - 1; j > i; j--) {
Particle p2 = list.get(j);
if(isOverlap(p1, p2))
list.remove(j);
}
}
}
public void checkBoundary(){
for(int i =0; i< listParticle.size() ;i++){
Particle p = listParticle.get(i);
if(p.x > xmax - dmin)
p.vx = -Math.abs(p.vx) ;
if(p.x < xmin + dmin)
p.vx = Math.abs(p.vx);
if(p.y > ymax - dmin)
p.vy = - Math.abs(p.vy);
if(p.y < ymin + dmin)
p.vy = Math.abs(p.vy);
}
}
private boolean isOverlap(Particle p1, Particle p2){
double deltax = p1.x - p2.x;
double deltay = p1.y - p2.y;
if(deltax * deltax + deltay * deltay < 4 * dmin * dmin)
return true;
return false;
}
public static void main(String[] args) {
launch(args);
}
private class Particle {
double x = 0.0;
double y = 0.0;
double vx = 0.0;
double vy = 0.0;
public Particle(double x, double y , double vx , double vy ) {
this.x = x;
this.y = y;
this.vx = vx ;
this.vy = vy;
}
}
}