Codename One Animation Trouble (also in Solitaire demo)?

一世执手 提交于 2019-12-22 08:55:25

问题


What happened to Codename One animations? I use a lot of them and as of december my app is no longer working. When I left things in June everything was fine (and was so for more than a year).

My app is a draughts (checkers) game and is available in the app stores since 2013. After seeing the CN1 Poker demo, I completely rewrote my GUI as I wanted to add those kind of animations to my app. What happens now is that I suddenly get index out of bound exceptions. I narrowed this down to the following situation:

cont.addComponent(comp);
...
...
cont.getComponent(0);  <-- index out of bound exception: 0 out of 0

I already used newVM=true in June, which is now the default, I believe. I tried to fix things by adding a

cont.animateLayoutAndWait(100);

after the addComponent call. This fixed the index out of bound exceptions, but now the app just crashes at random moments after working fine for a short time. I tried but have been unable to find the source of the problem. Something has changed in CN1 so that my code no longer works. (See also the old forum, search for "draughts", where I posted a complete listing of my basic design.)

I studied the new Solitaire demo, which has most of the animation functionality I need. And the app store versions of Solitaire run fine on all my iOS/Android devices. A minor bug is that it is possible to drag a group of cards including some that are facing down, which are facing up during the drag operation. Also it is pretty hard to pick up exactly the right amount of cards. Dragging a group of cards also leaves a white trail on the screen, which doesn't look very nice. This also happens in the Simulator.

As an experiment, I redesigned my GUI layout to exactly resemble that of the Solitaire code: two layers of buttons, the only difference being that I use GridLayout(10,10) where Solitaire uses SolitaireLayout(). This works, except for one thing: if a checker moves DOWN the board it correctly moves OVER other checkers, but if a checker moves UP the board it moves UNDER the other checkers.

My code looks like this:

Button pc = (Button)piecesCnt.getComponentAt(a1);
Button to = (Button)piecesCnt.getComponentAt(a2);

piecesCnt.removeComponent(pc);
piecesCnt.addComponent(a1, createPieceButton(Piece.EMPTY_PIECE, true));
piecesCnt.removeComponent(to);
piecesCnt.addComponent(a2, pc);

piecesCnt.animateLayoutAndWait(1000);

So it seems that the buttons are always drawn in their GridLayout order, while I hoped that the animated (moving) button would be drawn last/on top, just like the moving cards in Solitaire.

Is this a difference between handling animation in SolitaireLayout vs. GridLayout? If so, can this be changed in the animation logic? Otherwise I have to add an extra animation layer and a lot of overhead.

And on Android, the animation (movement) is not working properly. So I decided to also self-build and test the Solitaire demo on my devices with the current CN1 version (plugin 3.2.6, libs 2016-01-11). I changed the animations to be 10x slower, to better see what is happening. In the Simulator, the layout and animations work mostly fine but on my iOS and Android devices there are a lot of problems.

Simulator (Windows 7, NetBeans 8.0.2): - I replaced the font icons by text since they are missing. - But how to access the hamburger menu? I don't see the 3 dots and there is even no space for it. - Sometimes it is possible to drags a groups of cards with some downfacing cards taken; while dragged they are temporarily flipped facing up. - Autoplay doesn't always seem to work. (Not all moves are played.)

On iOS 9.2 (iPad 4), iOS 8.4 (iPhone 4): - [?] is shown in the hamburger menu check boxes. - After startup, the tableau background card-backs jump to the bottom of the container/screen. - Sometimes, in the deal-from-deck animation, cards in the rightmost tableau are temporarily facing up, and cards already facing up are flipped facing down. The final deal-state is correct, however. - Undo/Redo: sometimes results in a facing-up card on deck 0 and a facing-down on deck 1. - Redo sometimes 'jumps' to the new layout instead of animating. iPhone only: - A sequence of autoplay moves left an inconsistent foundation state: top cards heart-J, club-10, club-K, diamond-Q; i.e., 2 club-cards on different foundation stacks.

On Android 5.1.1 (Nexus 7) there are more problems. - The deal-from-deck animation is not a pretty sight. Cards are inserted (slide to) at the bottom of the tableau stacks, so they slide under the other cards. The moving cards should be facing down but are usually facing up, always showing the first card dealt to tableau 1. The cards in rightmost tableau are temporarily facing up. Finally, cards already facing up are flipped facing down and back again. The final deal-state is correct, however. - Autoplay is also not correct when there are several moves in a row. During the animations, several foundation cards facing up change their card value (front) temporarily. Also the moving cards slide under the foundation card (and sometimes over, but this is the exception). - After finishing the game, the well-done screen is corrupted since the text seems to be displayed not overlayed but below the cards (in a big white space), which are only partly visible in a tiny portion at the top of the screen. - Starting a new game often fails because deal-from-deck is not started; the screen shows a deck and four kings on the foundations with the tableau empty. When tapping the deck, a card is dealt and the well done sequence is show. Sometimes repeating this results in successfully starting a new game.

All this has taken a lot of time already and I still don't have my app working again, which is very frustrating. Having so much trouble with this topic, even with a downloaded demo app, building this type of app with Codename One feels like building on quicksand. Please help!


回答1:


You should have probably just asked once you ran into trouble instead of working so hard on the post. Yes we did make a major somewhat compatibility breaking change as part of a bug fix for long standing animation issues (where animations running in parallel could collide).

This introduced some collisions but reduced inconsistencies between devices/simulator which is always a good thing.

We announced this here: https://www.codenameone.com/blog/new-animation-manager.html

It is actually MUCH simpler to create a portable animation now as everything will get synchronized to avoid animation collisions e.g. if you do Component.removeComponent() while an animation is in progress it will be implicitly added to the animation queue and performed after the animation completes instead of immediately.

To postpone your next action until after the animations we have:

form.getAnimationManager().flushAnimation(() -> doThisAfterAnimation());

Much simpler and no special case global locks.

Its a bit hard to "port" code directly to the new approach but it looks like your animate logic relies on the animation taking 1000ms and that it completely finished when the method returns which might not always be the case (as add/remove calls or other logic might get in the way).

In the past the only way to guarantee an animation was complete was to distance them apart but now you can just use flushAnimation to make sure all animations have completed. Keep in mind that some things that aren't explicitly an animation can now become an animation by accident e.g. an add/remove component will become an animation if there is an animation in progress when you call them...




回答2:


I'm sorry I missed the blog post on the new animation manager of December 16. The Solitaire demo dates October 8 and I checked the Developer Guide of December 31, so I thought I was up to date. Still, it is not clear to me how to use the new animation manager. The new Developer Guide of Januari 11 says nothing on the topic and your answer/blog post are not very elaborate.

It would also help a lot if the Solitaire demo would be updated to work with the new animation manager.

Now my questions are:

1) How do I rewrite the following code to use the new animation manager? This is a realistic scenario because I have animations with multiple steps that should be performed consecutively.

// simplified code (background layer omitted)
public void start() {
    if(current != null){
        current.show();
        return;
    }

    Form form = new Form("Draughts 2");
    Container piecesCnt = new Container(new GridLayout(10, 10));
    for (int i=0; i<100; i++) {
        piecesCnt.addComponent(createPieceButton(piece(i), true));
    }
    form.addComponent(piecesCnt);
    form.show();

    new UITimer(() -> {
        testAnimation(piecesCnt, 0,9 , 9,0);    // moves UNDER other pieces
        testAnimation(piecesCnt, 9,0 , 0,9);    //   "   OVER    "     "
        testAnimation(piecesCnt, 1,0 , 9,8);    //   "   OVER    "     "
        testAnimation(piecesCnt, 9,8 , 1,0);    //   "   UNDER   "     "
    }).schedule(1500, false, form);
}

private void testAnimation(Container piecesCnt, int x1,int y1, int x2,int y2) {
    int a1 = 10 * y1 + x1;
    int a2 = 10 * y2 + x2;        
    Button pc = (Button)piecesCnt.getComponentAt(a1);
    Button to = (Button)piecesCnt.getComponentAt(a2);
    piecesCnt.removeComponent(pc);
    piecesCnt.addComponent(a1, createPieceButton(Piece.EMPTY_PIECE, true));
    piecesCnt.removeComponent(to);
    piecesCnt.addComponent(a2, pc);
    piecesCnt.animateLayoutAndWait(1000);
}

2) Does rewriting this also solve the problem of a piece (checker) moving under instead of over other pieces?

3) How do I pause between two testAnimation calls? (Not by sleeping, obviously.)


Update 2016-01-14 (see also the Comment section):

I tried two things with flushAnimations:

1) Use a function like this:

private void flushAnimations(Form f) {
    f.getAnimationManager().flushAnimation(() -> {});
}

and call this between successive animations. This doesn't work.

2) Nested flushAnimations:

testAnimation(piecesCnt, 0,9 , 9,0);
f.getAnimationManager().flushAnimation(() -> {
    testAnimation(piecesCnt, 9,0 , 0,9);
    f.getAnimationManager().flushAnimation(() -> {
        testAnimation(piecesCnt, 1,0 , 9,8);
        f.getAnimationManager().flushAnimation(() -> {
            testAnimation(piecesCnt, 9,8 , 1,0);
        });
    });
});

This does seem to work on iOS but not on my Android device: usually after the second animation the screen flickers and jumps to the next state instead of animating. Apart from that it is not a nice solution.

Then I tried the UITimer solution:

blockUI(piecesCnt);
new UITimer(() -> { testAnimation(piecesCnt, 0,9 , 9,0); }).schedule(2000, false, f);
new UITimer(() -> { testAnimation(piecesCnt, 9,0 , 0,9); }).schedule(3500, false, f);
new UITimer(() -> { testAnimation(piecesCnt, 1,0 , 9,8); }).schedule(5000, false, f);
new UITimer(() -> { testAnimation(piecesCnt, 9,8 , 1,0); }).schedule(6500, false, f);
new UITimer(() -> { 
    unblockUI(piecesCnt); 
}).schedule(8000, false, f);

private boolean blocked = false;    
private void blockUI(Container piecesCnt) {
    blocked = true;
    int n = piecesCnt.getComponentCount();
    for (int i=0; i<n; i++) {
        piecesCnt.getComponentAt(i).setDraggable(false);
    }
}
private void unblockUI(Container piecesCnt) {
    ...
}

This works but is also not a very desirable solution. I have a function 'autoplay' that replays an entire game consisting of >100 moves, each move consisting of 1 or more animation steps. The user can interrupt this process by pressing a stop button.

I would very much like to see how the Poker and Solitaire (Autoplay!) demos should be coded with the new animation handling.

Please take a minute to follow this link to see what kind of functionality I am talking about: http://toernooibase.kndb.nl/opvraag/applet.php?taal=1&kl=46&Id=4579&r=10&jr=16&wed=845502. Just press the autoplay button ('>') below the board. (My app can also play the game, and you can set it to think for say 1 minute per move, during which the GUI is blocked.)

I saw that the Developer Guide was updated Jan 13, and now has a chapter/appendix on Casual Game Programming. This is nice, although the CN1Poker version it lists still doesn't properly work on an Android device: the dealing cards animation shows the cards mostly jumping and sometimes sliding to their positions. (It works fine in the Simulator and on iOS devices.)

How can you say it is 'actually MUCH simpler to create a portable animation now'? I think in my case it actually just became much harder. I need an alternative for the old approach which is also used in the poker and solitaire demos: use animateLayoutAndWait and have no add/remove actions during an animation (also made sure by program logic).

As a basic subscriber (for 3 years now) I have to use the latest CN1 version, but with the current CN1 version I no longer know how to code this. I think this is a quite fundamental issue as all games use functionality like this.



来源:https://stackoverflow.com/questions/34743873/codename-one-animation-trouble-also-in-solitaire-demo

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!