RubikFX:用JavaFX 3D解决魔方难题(3)

RubikFX:用JavaFX 3D解决魔方难题(3)

首页休闲益智魔方难题更新时间:2024-05-07

RubikFX, Lite Version

-----------------------------------------------------------------

魔方,精简版

后面我们将加入更多的特性,但是现在我们创建一个JavaFX应用,使用BorderPane组件,在Pane控件中添加内容,包含按钮的执行旋转功能的工具条。

public class TestRubikFX extends Application { private final BorderPane pane=new BorderPane(); private Rubik rubik; @Override public void start(Stage stage) { rubik=new Rubik(); // create toolbars //创建工具条,在上下左右四个方向创建4个工具条 ToolBar tbTop=new ToolBar(new Button("U"),new Button("Ui"),new Button("F"), new Button("Fi"),new Separator(),new Button("Y"), new Button("Yi"),new Button("Z"),new Button("Zi")); pane.setTop(tbTop); ToolBar tbBottom=new ToolBar(new Button("B"),new Button("Bi"),new Button("D"), new Button("Di"),new Button("E"),new Button("Ei")); pane.setBottom(tbBottom); ToolBar tbRight=new ToolBar(new Button("R"),new Button("Ri"),new Separator(), new Button("X"),new Button("Xi")); //工具条自身方向的设定 tbRight.setOrientation(Orientation.VERTICAL); pane.setRight(tbRight); ToolBar tbLeft=new ToolBar(new Button("L"),new Button("Li"),new Button("M"), new Button("Mi"),new Button("S"),new Button("Si")); tbLeft.setOrientation(Orientation.VERTICAL); //工具条自身方向的设定 pane.setLeft(tbLeft); pane.setCenter(rubik.getSubScene()); pane.getChildren().stream() .filter(n->(n instanceof ToolBar)) .forEach(tb->{ ((ToolBar)tb).getItems().stream() .filter(n->(n instanceof Button)) .forEach(n->((Button)n).setOnAction(e->rubik.rotateFace(((Button)n).getText()))); }); rubik.isOnRotation().addListener((ov,b,b1)->{ pane.getChildren().stream() .filter(n->(n instanceof ToolBar)) .forEach(tb->tb.setDisable(b1)); }); final Scene scene = new Scene(pane, 880, 680, true); scene.setFill(Color.ALICEBLUE); stage.setTitle("Rubik's Cube - JavaFX3D"); stage.setScene(scene); stage.show(); } }

下图就是我们完成后的样子。

图四

如果你想对这个应用进行深入的研究,你能够在我的GitHub上找到源代码。

网址如下:https://github.com/jperedadnr/LiteRubikFX

提示你,首先需要添加3DViewer的jar包。

如何运作?举个例子, “F”旋转,我们在rot中应用

// rotate cube indexes //旋转立方体顺序索引 rot.turn(btRot); // get new indexes in terms of blocks numbers from original order //依据原始顺序的Block编号得到新的顺序索引 reorder=rot.getCube();

使用rot.printCube()方法,我们可以得到旋转前(order)和旋转后(reorder)小立方体的编号

order: 50 51 52 49 54 53 59 48 46 || 58 55 60 57 62 61 47 56 63 || 67 64 69 66 71 70 68 65 72
reorder: 59 49 50 48 54 51 46 53 52 || 58 55 60 57 62 61 47 56 63 || 67 64 69 66 71 70 68 65 72
通过比对列表和获取差异项,我们可以知道哪些小立方体必须旋转,当然我们需要添加中间立方体的编号(54),它在列表中保持不变,但是它也应该被旋转。所以我们为9个立方体创建列表层。

// select cubies to rotate: those in reorder different from order. //选择被旋转的立方体,所有那些新序号与老序号不同的 AtomicInteger index = new AtomicInteger(); layer=order.stream() .filter(o->!Objects.equals(o, reorder.get(index.getAndIncrement()))) .collect(Collectors.toList()); // add central cubie //加入中间立方体 layer.add(0,reorder.get(Utils.getCenter(btRot))); // set rotation axis //设置旋转轴 axis=Utils.getAxis(btRot);

Utils是一个管理旋转类型数值的类,举个例子。

public static Point3D getAxis(String face){ Point3D p=new Point3D(0,0,0); switch(face.substring(0,1)){ case "F": case "S": p=new Point3D(0,0,1); break; } return p; } public static int getCenter(String face){ int c=0; switch(face.substring(0,1)){ case "F": c=4; break; } return c; }

当我们获得了小立方体和旋转轴,现在需要考虑旋转监听器如何工作了。在时间轴中,(预定义)一次EASE_BOTH插值角度增加从0到90度,所以角度增量在开始时比较小,中间变大,结束时又变小。下面是一种可行的增量变化表:

0.125º-3º-4.6º-2.2º-2.48º-...-2.43º-4.78º-2.4º-2.4º-0.55º。对于在angNew中的每个值,监听者rotMap对于小立方体的每一层都会进行一个小的旋转。在HashMap表中,隶属于小立方体的meshviews面,将进行一个相对于前一个Affine矩阵的旋预旋转。

/* Listener to perform an animated face rotation */ rotMap=(ov,angOld,angNew)->{ mapMeshes.forEach((k,v)->{ layer.stream().filter(l->k.contains(l.toString())) .findFirst().ifPresent(l->{ Affine a=new Affine(v.getTransforms().get(0)); a.prepend(new Rotate(angNew.doubleValue()-angOld.doubleValue(),axis)); v.getTransforms().setAll(a); }); }); };

在600毫秒内,我们将对40个meshview面进行30到40次小旋转。最后,在旋转完成后,我们只需在最后的小立方体列表中更新顺序,如此我们又可以进行下一次旋转。

魔方—完整版

添加更多的特性

现在我们取得了一个很基础的基于JavaFX应用的作品,现在可以添加更多地特性,例如图形箭头和旋转预览,在旋转执行前显示旋转方向。

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved