簡體   English   中英

如何為陀螺制作動畫?

[英]How to animate a spinning top?

它是光明節,我正在嘗試動畫陀螺(dreidel):

陀螺

我可以讓它在自己的軸上旋轉。 這是我的代碼:

import static javafx.scene.paint.Color.*;

import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Point3D;
import javafx.scene.Camera;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Cylinder;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
import javafx.util.Duration;

public class DreidelAnim extends Application {

    private double bodyBase = 30;
    private double bodyHeight = bodyBase * 3 / 2;
    private double baseRadius = bodyBase / 2;

    @Override
    public void start(Stage stage) throws Exception {
        DoubleProperty spinAngle = new SimpleDoubleProperty();
        Rotate spin = new Rotate(0, Rotate.Z_AXIS);
        spin.angleProperty().bind(spinAngle);

        Timeline spinAnim = new Timeline(new KeyFrame(Duration.seconds(2), new KeyValue(spinAngle, 360)));
        spinAnim.setCycleCount(Timeline.INDEFINITE);
        spinAnim.play();

        Group dreidel = createDreidel();
        Translate zTrans = new Translate(0, 0, -(bodyHeight/2 + baseRadius));
        dreidel.getTransforms().addAll(spin, zTrans);

        Scene scene = new Scene(dreidel, 200, 200, true, SceneAntialiasing.BALANCED);
        scene.setFill(SKYBLUE);
        scene.setCamera(createCamera());

        stage.setScene(scene);
        stage.show();
    }

    private Group createDreidel() {
        double handleHeight = bodyBase * 3/4;
        Cylinder handle = new Cylinder(bodyBase / 6, handleHeight);
        handle.setTranslateZ(-(bodyHeight + handleHeight) / 2);
        handle.setRotationAxis(Rotate.X_AXIS);
        handle.setRotate(90);
        handle.setMaterial(new PhongMaterial(RED));

        Box body = new Box(bodyBase, bodyBase, bodyHeight);
        body.setMaterial(new PhongMaterial(BLUE));

        Sphere base = new Sphere(baseRadius);
        base.setTranslateZ(bodyHeight / 2);
        base.setMaterial(new PhongMaterial(GREEN));

        return new Group(handle, body, base);
    }

    private Camera createCamera() {
        PerspectiveCamera camera = new PerspectiveCamera(true);
        camera.setFarClip(1000);

        int xy = 150;
        Translate trans = new Translate(-xy, xy, -120);
        Rotate rotXY = new Rotate(70, new Point3D(1, 1, 0));
        Rotate rotZ = new Rotate(45, new Point3D(0, 0, 1));
        camera.getTransforms().addAll(trans, rotXY, rotZ);

        return camera;
    }

    public static void main(String[] args) {
        launch();
    }
}

我創建了一個簡單的模型,圍繞其軸旋轉,並將其翻譯為其尖端(0, 0, 0) 結果如下:

在此輸入圖像描述

如何才能實現與頂部圖片類似的旋轉軸旋轉?

物體旋轉的軸的旋轉稱為進動 旋轉頂部運動需要2次旋轉:

  1. 物體圍繞其內軸旋轉(平行於紅色手柄)。
  2. 圍繞靜態軸旋轉其中一個內部軸(在這種情況下為z )。

從表面上看,你需要2個Animation實例。 但是,兩個旋轉實際上是相同的。 兩者的軸心點是(0, 0, 0) (在zTrans )並且它們都在z軸周圍,只有一個以一定角度傾斜。

這是修改后的代碼:

import static javafx.scene.paint.Color.*;

import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Point3D;
import javafx.scene.Camera;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Cylinder;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
import javafx.util.Duration;

public class FinalDreidelSpin extends Application {

    private double bodyBase = 30;
    private double bodyHeight = bodyBase * 3 / 2;
    private double baseRadius = bodyBase / 2;

    @Override
    public void start(Stage stage) throws Exception {
        double tiltAngle = 40;
        DoubleProperty spinAngle = new SimpleDoubleProperty();

        Rotate spin = new Rotate(0, Rotate.Z_AXIS);
        Rotate tilt = new Rotate(tiltAngle, Rotate.X_AXIS);

        spin.angleProperty().bind(spinAngle);

        Timeline spinAnim = new Timeline();
        spinAnim.getKeyFrames().add(new KeyFrame(Duration.seconds(2), new KeyValue(spinAngle, 360)));
        spinAnim.setCycleCount(Timeline.INDEFINITE);
        spinAnim.play();

        Group dreidel = createDreidel();
        Translate zTrans = new Translate(0, 0, -(bodyHeight/2 + baseRadius));
        dreidel.getTransforms().addAll(spin, tilt, spin, zTrans);

        Scene scene = new Scene(new Group(dreidel, createAxes()), 200, 200, true, SceneAntialiasing.BALANCED);
        scene.setFill(SKYBLUE);
        scene.setCamera(createCamera());

        stage.setScene(scene);
        stage.show();
    }

    private Group createDreidel() {
        double handleHeight = bodyBase * 3/4;
        Cylinder handle = new Cylinder(bodyBase / 6, handleHeight);
        handle.setTranslateZ(-(bodyHeight + handleHeight) / 2);
        handle.setRotationAxis(Rotate.X_AXIS);
        handle.setRotate(90);
        handle.setMaterial(new PhongMaterial(RED));

        Box body = new Box(bodyBase, bodyBase, bodyHeight);
        body.setMaterial(new PhongMaterial(BLUE));

        Sphere base = new Sphere(baseRadius);
        base.setTranslateZ(bodyHeight / 2);
        base.setMaterial(new PhongMaterial(GREEN));

        return new Group(handle, body, base);
    }

    private Camera createCamera() {
        PerspectiveCamera camera = new PerspectiveCamera(true);
        camera.setFarClip(1000);

        int xy = 150;
        Translate trans = new Translate(-xy, xy, -100);
        Rotate rotXY = new Rotate(70, new Point3D(1, 1, 0));
        Rotate rotZ = new Rotate(45, new Point3D(0, 0, 1));
        camera.getTransforms().addAll(trans, rotXY, rotZ);

        return camera;
    }

    private Group createAxes() {
        int axisWidth = 1;
        int axisLength = 400;

        Cylinder xAxis = new Cylinder(axisWidth, axisLength);
        xAxis.setMaterial(new PhongMaterial(CYAN));

        Cylinder yAxis = new Cylinder(axisWidth, axisLength);
        yAxis.setRotationAxis(Rotate.Z_AXIS);
        yAxis.setRotate(90);
        yAxis.setMaterial(new PhongMaterial(MAGENTA));

        Cylinder zAxis = new Cylinder(axisWidth, axisLength);
        zAxis.setRotationAxis(Rotate.X_AXIS);
        zAxis.setRotate(90);
        zAxis.setMaterial(new PhongMaterial(YELLOW));

        return new Group(xAxis, yAxis, zAxis);
    }

    public static void main(String[] args) {
        launch();
    }
}

我添加了軸表示以方便查看。 請注意, getTransforms()列表不要求其對象是唯一的(與getChildren()不同),這允許我們重用相同的動畫。 如下所述,動畫的順序也很重要。

傾斜是圍繞xy軸的簡單旋轉。
如果我們tilt然后spingetTransforms().addAll(tilt, spin, zTrans) ,我們將得到內部旋轉(上面列出的1),只是傾斜:

紡

如果我們spin然后tiltgetTransforms().addAll(spin, tilt, zTrans) ,我們就會得到進動(上面列出的2):

歲差

在完整代碼中組合2將得到所需的結果:

充分

這是另一個可能的答案,非常基於@ user1803551方法,但使用可以使用紋理圖像的3D網格和不同的進動周期。

這是它的樣子:

節dreidel

為了應用紋理,我將使用dreidel主體的網絡概念,這個圖像:

這是基於這個圖像

最后,我將為手柄添加一個普通的圓柱體。

我不會詳細介紹如何為主體創建TriangleMesh ,但我們定義了9個頂點(3D坐標),16個紋理坐標(2D)和14個三角形面,包括頂點索引和紋理索引。 立方體由其側邊width定義,金字塔由其height 凈尺寸為L = 4 * width, H = 2 * width + height

例如,面0具有頂點0-2-1,紋理指數8-3-7,其中頂點0具有坐標{width / 2, width / 2, width / 2} ,紋理索引8具有坐標{width, 2 * width} ,在[0,1]之間歸一化: {width / L, 2 * width / H}

在這種情況下,為了樣本,值是硬編碼的:

float width = 375f;
float height = 351f;

這是3D形狀類:

class DreidelMesh extends Group {

    float width = 375f;
    float height = 351f; 

    public DreidelMesh(){
        MeshView bodyMesh = new MeshView(createBodyMesh());
        PhongMaterial material = new PhongMaterial();
        material.setDiffuseMap(new Image(getClass().getResourceAsStream("3dreidel3d.png")));
        bodyMesh.setMaterial(material);

        Cylinder handle = new Cylinder(45, 260);
        handle.setTranslateY(-(handle.getHeight() + width) / 2);
        material = new PhongMaterial(Color.web("#daaf6d"));
        handle.setMaterial(material);

        getTransforms().add(new Rotate(90, Rotate.X_AXIS));
        getChildren().addAll(bodyMesh, handle);
    }

    private TriangleMesh createBodyMesh() {
        TriangleMesh m = new TriangleMesh();

        float L = 4f * width;
        float H = 2f * width + height;
        float w2 = width / 2f;

        // POINTS
        m.getPoints().addAll(
             w2,  w2,  w2, 
             w2,  w2, -w2, 
             w2, -w2,  w2, 
             w2, -w2, -w2, 
            -w2,  w2,  w2, 
            -w2,  w2, -w2, 
            -w2, -w2,  w2, 
            -w2, -w2, -w2, 
             0f,  w2 + height,  0f
        );

        // TEXTURES
        m.getTexCoords().addAll(
            width / L, 0f, 
            2f * width/ L, 0f, 
            0f, width / H,
            width / L, width / H, 
            2f * width/ L, width / H, 
            3f * width/ L, width / H, 
            1f, width / H, 
            0f, 2f * width / H,
            width / L, 2f * width / H, 
            2f * width/ L, 2f * width / H, 
            3f * width/ L, 2f * width / H, 
            1f, 2f * width / H, 
            width / 2f / L, 1f,
            3f * width / 2f / L, 1f,
            5f * width / 2f / L, 1f,
            7f * width / 2f / L, 1f
        );

        // FACES
        m.getFaces().addAll(
            0,  8, 2,  3, 1,  7,           
            2,  3, 3,  2, 1,  7,           
            4,  9, 5, 10, 6,  4,           
            6,  4, 5, 10, 7,  5,           
            0,  8, 1,  7, 8, 12,        
            4,  9, 0,  8, 8, 13,           
            5, 10, 4,  9, 8, 14,           
            1, 11, 5, 10, 8, 15,            
            2,  3, 6,  4, 3,  0,            
            3,  0, 6,  4, 7,  1,            
            0,  8, 4,  9, 2,  3,          
            2,  3, 4,  9, 6,  4,           
            1, 11, 3,  6, 5, 10,           
            5, 10, 3,  6, 7,  5
        );
        return m;
    }
}

節dreidel

最后,這個形狀被添加到場景中,我將提供兩個動畫(而不是一個),一個用於旋轉,一個用於進動:

@Override
public void start(Stage stage) {
    double tiltAngle = 15;
    DoubleProperty spinAngle = new SimpleDoubleProperty();
    DoubleProperty precessionAngle = new SimpleDoubleProperty();

    Rotate spin = new Rotate(0, Rotate.Z_AXIS);
    Rotate precession = new Rotate(0, Rotate.Z_AXIS);
    Rotate tilt = new Rotate(tiltAngle, Rotate.X_AXIS);

    spin.angleProperty().bind(spinAngle);
    precession.angleProperty().bind(precessionAngle);

    Timeline spinAnim = new Timeline();
    spinAnim.getKeyFrames().add(new KeyFrame(Duration.seconds(1.5), new KeyValue(spinAngle, 360)));
    spinAnim.setCycleCount(Timeline.INDEFINITE);
    spinAnim.play();

    Timeline precessionAnim = new Timeline();
    precessionAnim.getKeyFrames().add(new KeyFrame(Duration.seconds(4), new KeyValue(precessionAngle, 360)));
    precessionAnim.setCycleCount(Timeline.INDEFINITE);
    precessionAnim.play();

    Group dreidel = new Group(new DreidelMesh());
    Translate zTrans = new Translate(0, 0, - dreidel.getBoundsInLocal().getMaxZ());
    dreidel.getTransforms().addAll(precession, tilt, spin, zTrans);

    Scene scene = new Scene(new Group(dreidel), 300, 300, true, SceneAntialiasing.BALANCED);
    scene.setFill(SKYBLUE);
    scene.setCamera(createCamera());

    stage.setScene(scene);
    stage.setTitle("JavaFX 3D - Dreidel");
    stage.show();
}

運行應用程序將顯示上面顯示的動畫。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM