像耐心/克朗代克纸牌游戏一样拖动节点 [英] Drag nodes like in a patience/Klondike card game

查看:277
本文介绍了像耐心/克朗代克纸牌游戏一样拖动节点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在做一个克朗代克游戏。逻辑完全正常。我刚刚在javafx中遇到了用户界面的问题。



我一直试图从画面堆中移动/拖动卡而没有预期的结果。
我的卡是一个ImageView,里面有一个Image。这些卡片在一个窗格内:

  Pane tableau = new Pane(); 
for(int i = 0; i< n; i ++){
Image img = new Image(resources / images /+(i + 1)+。png);
ImageView imgView = new ImageView(img);
imgView.setY(i * 20);
// imgView这里的鼠标事件
tableau.getChildren()。add(imgView);
}

我试过:

  imgView.setOnMousePressed((MouseEvent mouseEvent) - > {
dragDelta.x = imgView.getLayoutX() - mouseEvent.getSceneX();
dragDelta.y = imgView.getLayoutY() - mouseEvent.getSceneY();
});
imgView.setOnMouseDragged((MouseEvent mouseEvent) - > {
imgView.setLayoutX(mouseEvent.getSceneX()+ dragDelta.x);
imgView.setLayoutY(mouseEvent.getSceneY()+ dragDelta .y);
});

这个解决方案不起作用,因为我正在设置位置所以当释放卡片时不会返回到它的位置是的,因为卡片正在与其他UI对象发生碰撞。



另一次尝试:

  imgView.setOnDragDetected((MouseEvent event) - > {
ClipboardContent content = new ClipboardContent();
content.putImage(img);
Dragboard db = imgView.startDragAndDrop (TransferMode.ANY);
db.setDragView(img,35,50);
db.setContent(content);
event.consume();
});

在这个解决方案中,问题是:卡片是半透明的,就像移动文件一样,光标不是/禁止但除此之外它运作良好:没有碰撞,如果我释放鼠标,卡片会移到原来的位置。
另一个问题是,我不知道我是否可以使用此解决方案移动超过1张卡?



我的最后一个问题是,如何将一个节点(在本例中为ImageView)或一组节点从一堆移动到另一堆,就像在纸牌卡中一样游戏?

解决方案

要了解原始卡位置,您应该使用 a关于我回复的那个问题的主题,如果你想了解更多关于它的话。


I'm doing a Klondike game. The logic is all working. I'm just having trouble with the UI in javafx.

I've been trying to move/drag the cards from the 'tableau pile' arround without the expected result. My card is a ImageView with an Image inside. The cards are inside a Pane:

Pane tableau = new Pane();
for (int i = 0; i < n; i++) {
    Image img = new Image("resources/images/" + (i + 1) + ".png");
    ImageView imgView = new ImageView(img);
    imgView.setY(i * 20);
    //imgView Mouse Events here
    tableau.getChildren().add(imgView);
}

I tried:

imgView.setOnMousePressed((MouseEvent mouseEvent) -> {
    dragDelta.x = imgView.getLayoutX() - mouseEvent.getSceneX();
    dragDelta.y = imgView.getLayoutY() - mouseEvent.getSceneY();
});
imgView.setOnMouseDragged((MouseEvent mouseEvent) -> {
    imgView.setLayoutX(mouseEvent.getSceneX() + dragDelta.x);
    imgView.setLayoutY(mouseEvent.getSceneY() + dragDelta.y);
});

This solution dont work because I'm setting the positions so when release the card wont return to where it was, and because the card is colliding with other UI objects.

Another attempt:

imgView.setOnDragDetected((MouseEvent event) -> {
    ClipboardContent content = new ClipboardContent();
    content.putImage(img);
    Dragboard db = imgView.startDragAndDrop(TransferMode.ANY);
    db.setDragView(img, 35, 50);
    db.setContent(content);
    event.consume();
});

In this solution the problems are: the card comes semitransparent, like moving a file, the cursor becames no/forbidden but other than that it works well: no collisions and the card goes to his original place if I release the mouse. Another problem is that I dont know if I can move more than 1 card with this solution?

My final question is, How can I move a node (in this case an ImageView) or a group of nodes, from one pile to another, like in a Solitaire Card Game?

解决方案

For knowing the original card position you should use the setTranslateX (and Y) instead of setLayoutX in your mouse handler. So when the user releases the card, you can simply invoke a Transition and let the card fly back to the layout position. If the user releases the card on a valid place, you set the translate coordinates to 0 and change the layout position or use relocate.

If you want to make the cards semitransparent, you could e. g. change the opacity or apply CSS.

By the way, I wouldn't use Clipboardcontent, it seems inappropriate for your needs.

You can move multiple objects with your mouse handling code. You simple have to apply the translation to multiple objects simultaneously. When you drag a pile, you determine the cards on top of the selected card and apply the transition to all of them.


Here's a quick example to show you how it could look like.

Card.java, you'll use an ImageView.

public class Card extends Rectangle {

    static Random rand = new Random();

    public Card() {


        setWidth(100);
        setHeight(200);

        Color color = createRandomColor();

        setStroke(color);
        setFill( color.deriveColor(1, 1, 1, 0.4));

    }

    public static Color createRandomColor() {

        int max = 200;

        Color color = Color.rgb( (int) (rand.nextDouble() * max), (int) (rand.nextDouble() * max), (int) (rand.nextDouble() * max));

        return color;
    }
}

Game.java, the application.

public class Game extends Application {

    static List<Card> cardList = new ArrayList<>();

    @Override
    public void start(Stage primaryStage) {

        MouseGestures mg = new MouseGestures();

        Group root = new Group();

        for( int i=0; i < 10; i++) {

            Card card = new Card();
            card.relocate( i * 20, i * 10);

            mg.makeDraggable(card);

            cardList.add( card);
        }

        root.getChildren().addAll( cardList);

        Scene scene = new Scene( root, 1600, 900);

        primaryStage.setScene( scene);
        primaryStage.show();

    }

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

    // TODO: don't use a static method, I only added for the example 
    public static List<Card> getSelectedCards( Card currentCard) {

        List<Card> selectedCards = new ArrayList<>();

        int i = cardList.indexOf(currentCard);
        for( int j=i + 1; j < cardList.size(); j++) {
            selectedCards.add( cardList.get( j));
        }

        return selectedCards;
    }

}

MouseGestures.java, the mouse handling mechanism.

public class MouseGestures {

    final DragContext dragContext = new DragContext();

    public void makeDraggable(final Node node) {

        node.setOnMousePressed(onMousePressedEventHandler);
        node.setOnMouseDragged(onMouseDraggedEventHandler);
        node.setOnMouseReleased(onMouseReleasedEventHandler);

    }

    EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {

        @Override
        public void handle(MouseEvent event) {

            dragContext.x = event.getSceneX();
            dragContext.y = event.getSceneY();

        }
    };

    EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {

        @Override
        public void handle(MouseEvent event) {

            Node node = (Node) event.getSource();

            double offsetX = event.getSceneX() - dragContext.x;
            double offsetY = event.getSceneY() - dragContext.y;

            node.setTranslateX(offsetX);
            node.setTranslateY(offsetY);

            // same for the other cards
            List<Card> list = Game.getSelectedCards( (Card) node);
            for( Card card: list) {
                card.setTranslateX(offsetX);
                card.setTranslateY(offsetY);
            }


        }
    };

    EventHandler<MouseEvent> onMouseReleasedEventHandler = new EventHandler<MouseEvent>() {

        @Override
        public void handle(MouseEvent event) {

            Node node = (Node) event.getSource();

            moveToSource(node);

            // same for the other cards
            List<Card> list = Game.getSelectedCards( (Card) node);
            for( Card card: list) {
                moveToSource(card);
            }

            // if you find out that the cards are on a valid position, you need to fix it, ie invoke relocate and set the translation to 0
            // fixPosition( node);

        }
    };

    private void moveToSource( Node node) {
        double sourceX = node.getLayoutX() + node.getTranslateX();
        double sourceY = node.getLayoutY() + node.getTranslateY();

        double targetX = node.getLayoutX();
        double targetY = node.getLayoutY();

        Path path = new Path();
        path.getElements().add(new MoveToAbs( node, sourceX, sourceY));
        path.getElements().add(new LineToAbs( node, targetX, targetY));

        PathTransition pathTransition = new PathTransition();
        pathTransition.setDuration(Duration.millis(1000));
        pathTransition.setNode(node);
        pathTransition.setPath(path);
        pathTransition.setCycleCount(1);
        pathTransition.setAutoReverse(true);

        pathTransition.play();
    }

    /**
     * Relocate card to current position and set translate to 0.
     * @param node
     */
    private void fixPosition( Node node) {

        double x = node.getTranslateX();
        double y = node.getTranslateY();

        node.relocate(node.getLayoutX() + x, node.getLayoutY() + y);

        node.setTranslateX(0);
        node.setTranslateY(0);

    }

    class DragContext {

        double x;
        double y;

    }

    // pathtransition works with the center of the node => we need to consider that
    public static class MoveToAbs extends MoveTo {

        public MoveToAbs( Node node, double x, double y) {
            super( x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);

        }

    }

    // pathtransition works with the center of the node => we need to consider that
    public static class LineToAbs extends LineTo {

        public LineToAbs( Node node, double x, double y) {
            super( x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
        }

    }

}

When you pick a card or a multiple of cards, they'll transition back to their origin.

Here's a screenshot where I dragged the 3rd card from top.

When you check if a card is on a valid destination, you should invoke the fixPosition method. It simply relocates the card, i. e. calculates the position using the translation values, repositions the node and sets its translation to 0.

The tricky part was the PathTransition. It's working from the center of a node, not from the x/y coordinates. Here's a thread regarding that issue to which I replied in case you wish to know more about it.

这篇关于像耐心/克朗代克纸牌游戏一样拖动节点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆