Ejemplo de Phys2d & JavaFX.

Voy a describir un ejemplo (un hola mundo en Phys2d) del uso de la librería Phys2d y JavaFX, revisa los demos, documentación y de que se trata en: http://www.cokeandcode.com/phys2d/

En este sencillo ejemplo describo (de manera muy humilde, dada mi poca experiencia con los términos y uso de la librería) una pelota que cae sobre una superficie plana y revota repetidas veces disminuyendo su vote (Restitución) hasta caer al infinito o que se vaya la luz... es necesario agregar la librería de Phys2d al proyecto.

WorldFactory

Se encarga de crear y dirigir el comportamiento en mundo virtual de la superficie y pelota que usaremos. Esta clase no dibuja nada solo genera los efectos de los objetos.

import net.phys2d.raw.Body;
import net.phys2d.math.Vector2f;
import net.phys2d.raw.World;
import net.phys2d.raw.StaticBody;
import net.phys2d.raw.shapes.Box;
import net.phys2d.raw.shapes.Circle;

/**
 * @author rodrigo salado anaya
 */

public class WorldFactory {

    def vector: Vector2f = new Vector2f(Global.gravityX, Global.gravityY);
    var world: World = getWorld();
    var ground: Body = getGround();
    public var ball: Body = getBall();

    init {
        Global.timer.play();
        world.add(ground);
        world.add(ball);
    }

    function getWorld(): World {
        new World(vector, 20)
    }

    function getGround(): Body {
        var boxShape: Box = new Box(Global.groundWidth, Global.groundEight);
        ground = new StaticBody(boxShape);
        ground.setPosition(Global.groundPositionX, Global.groundPositionY);
        ground.setRestitution(Global.groundRestitution);
        ground;
    }

    function getBall(): Body {
        var circleShape: Circle = new Circle(Global.ballRadius);
        ball = new Body(circleShape, Global.ballMass);
        ball.setPosition(Global.ballPositionX, Global.ballPositionY);
        ball.setRestitution(Global.ballRestitution);
        ball.setMaxVelocity(Global.ballMaxVelocity, Global.ballMaxVelocity);
        ball;
    }

    public function <<step>>(): Void {
        world.step();
    }

}

vector: Este atributo es el encargado de definir la fuerza y dirección de la gravedad en nuestro mundo. Por ejemplo si Global.gravityY es positivo cae y mientas mayor el valor mas rápido lo hace, si es negativo se eleva, con Global.gravityX se comporta igual que en el eje de las Y.

def vector: Vector2f = new Vector2f(Global.gravityX, Global.gravityY);

world: Es como definimos a nuestro mundo, en el agregaremos todos los objetos que desarrollemos, la superficie y la pelota.

var world: World = getWorld();

ground: Es la superficie en la que rebotara nuestra pelota.

var ground: Body = getGround();

ball: Es la pelota. Es publica por que de ella obtendremos su ubicación para otra(s) clase(s).

public var ball: Body = getBall();

init: JavaFX no cuenta con constructores como en Java, así que esta es una manera de iniciar lo que uno desea al crear una instancia, checa esto
En la clase Global, hay un reloj (timer: Timeline) con el que generamos el tiempo virtual de nuestro mundo y es el encargado de iniciar dicho reloj.

Global.timer.play();

Luego agregamos el suelo y la pelota al mundo que creamos

world.add(ground);
world.add(ball);

getWorld: Esta función se encarga de regresar un mundo con un vector que define la dirección y fuerza de la gravedad, también el número de iteraciones que se producirán a cada paso que se produzcan en nuestro mundo, estos pasos son como producidos por un reloj.

new World(vector, 20)

getGround: Esta función regresa un cuerpo, en este caso una superficie en forma de caja muy delgada y larga, pretende ser un suelo (StaticBody). Se le define su posición inicial y su coeficiente de restitución; con esto los objetos rebotaran en ella, si la pones en 0, no lo aran.
getBall: A diferencia de getGround también le agregamos el máximo de velocidad al que pueda alcanzar este objeto.
step: Esta función es importante, por que ella es la que actualiza todos los efectos de los objetos, y cada ves que hacemos una llamada a esta, nuestro mundo produce las iteraciones definidas y decide que valor dar a todos sus objetos mediande un salto 'world.step();'
Esta entre <<>>, por que step es una palabra reservada y esto produce que no se comporte así.

Global

Se encarga de definir constantes y variables del programa, normalmente las constantes van con mayúsculas y guiones bajos pero me dio flojera cambiarlas ya en la noche :P.

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.shape.Rectangle;
import javafx.scene.paint.Color;

/**
 * @author rodrigo salado anaya
 */

/*Stage*/
public def stageWidth = 300;
public def stageEight = 300;
/*word*/
public def gravityX = 5;
public def gravityY = 800;
/*ground*/
public def groundWidth = stageWidth;
public def groundEight = 1;
public def groundPositionX = stageWidth / 2;
public def groundPositionY = stageEight;
public def groundRestitution = 3;
public def groundFriction = 1;
public def ground = Rectangle {
            x: 0
            y: groundPositionY
            width: groundWidth
            height: groundEight
            fill: Color.BLACK
        }
/*ball*/
public def ballRadius = 10;
public def ballMass = 50;
public def ballPositionX = 5;
public def ballPositionY = 0;
public def ballRestitution = 0.3;
public def ballMaxVelocity = 500;
/*timer*/
public var timer: Timeline = Timeline {
            repeatCount: Timeline.INDEFINITE
            keyFrames: KeyFrame {
                time: 0.01s action: function() {
                    ball.updateState();
                    world.step();
                }
            }
        }
/*Mundo&Objeto*/
public var world: WorldFactory = WorldFactory {};
public var ball: BallBody = BallBody { ball: world.ball }

ground: Es la figura o parte gráfica (solo eso) que representa el suelo. Si me puedes explicar por que el ' width: groundWidth / 2', seguro que eso esta mal por que si lo dejo normal se duplica la distancia..., no se pro que... corre el ejemplo y checa eso ok..
Lo que pasa es que el punto de posición para un cuadrado en Phys2d es el centro del rectángulo y en JFX es la esquina superior izquierda.

public def ground = Rectangle {
            x: groundPositionX
            y: groundPositionY
            width: groundWidth / 2
            height: groundEight
            fill: Color.BLACK
        }

timer: Es el encargado de generar el tiempo de recarga de los datos, desde los gráficos hasta los eventos generados en ese proceso por nuestro mundo.
ball.updateState(); es un método de la clase BallBody, sirbe para actualizar la posición gráfica (x,y) de la pelota.
world.step(); es encargado de dar el siguiente paso a las iteraciones de nuestro mundo.
world: Este es nuestro mundo que usaremos. Lo deje aquí por que se ve mejor, pero se pudo haber puesto en Main por ejemplo.

public var world: WorldFactory = WorldFactory {};

ball: Es nuestra pelota, ojo es un CustomNode y converge en ella lo necesario para que se comporte como un objeto real.

public var ball: BallBody = BallBody { ball: world.ball }

BallBody

Es el nodo encargado de dibujar y actualizar la ubicación de nuestra pelota.

import javafx.scene.CustomNode;
import javafx.scene.Group;
import javafx.scene.Node;
import net.phys2d.raw.Body;
import javafx.scene.shape.Circle;
import javafx.scene.paint.Color;

/**
 * @author rodrigo salado anaya
 */

public class BallBody extends CustomNode {

    public-init var ball: Body;
    var x: Number;
    var y: Number;
    var circle: Circle = Circle {
                centerX: bind x
                centerY: bind y
                radius: Global.ballRadius
                fill: Color.BLACK
            }

    public function updateState(): Void {
        x = ball.getPosition().getX();
        y = ball.getPosition().getY();
    }

    public override function create(): Node {
        return Group {
                    content: [circle]
                };
    }

}

public-init var ball: Body;: Contendrá los efectos producidos por la pelota. public-init checa esto
updateState(): Esta función actualiza los datos de la ubicación de la pelota.

Stage

Bueno es el que lanza todo nuestro programa.

Stage {
    title: "Gravedad FX 1"
    scene: Scene {
        width: Global.stageWidth + 20
        height: Global.stageEight + 20
        content: [
            Global.ball,
            Global.ground
        ]
    }
}


Nota: Es un hola mundo en Phys2d, y es muy compleja para mí, si alguien sabe algo de ella seria bueno que contribuyese, o si corres el ejemplo y juegas con el lo que pueda y saca cosa interesantes, o aun que no lo sean son útiles todas los tip, insisto por mínimo que sea :P, te agradeceré mucho.
En realidad hay muy poca documentación de Phys2d, incluso de como desarrollar un ejemplo sencillo, no dudes en comentarme alguna sugerencia o duda, y ya veremos como nos la resolvemos o corregimos.
Seria bueno tener una biblioteca de ejemplos de Phys2D.

Nos leemos pronto y pasatela bien.

AdjuntoTamaño
ball.png5.75 KB

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de Nopalin

me parece una muy buena

me parece una muy buena libreria, gracias por el aporte.
Saludos