代号一个动画麻烦(也在纸牌演示中)?

Posted

技术标签:

【中文标题】代号一个动画麻烦(也在纸牌演示中)?【英文标题】:Codename One Animation Trouble (also in Solitaire demo)? 【发布时间】:2016-01-12 12:35:17 【问题描述】:

代号一号动画发生了什么?我使用了很多,截至 12 月,我的应用程序不再工作。当我在 6 月份离开时,一切都很好(一年多以来一直如此)。

我的应用程序是一个草稿(跳棋)游戏,自 2013 年起在应用程序商店中提供。在看到 CN1 Poker 演示后,我完全重写了我的 GUI,因为我想将这些动画添加到我的应用程序中。现在发生的事情是我突然让 index out of bound 异常。我将其缩小到以下情况:

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

我在 6 月份已经使用了 newVM=true,我相信这是现在的默认设置。我试图通过添加一个

来解决问题
cont.animateLayoutAndWait(100);

在 addComponent 调用之后。这修复了索引超出范围的异常,但现在应用程序在短时间内正常工作后随机崩溃。我试过了,但一直找不到问题的根源。 CN1 发生了一些变化,因此我的代码不再有效。 (另请参阅旧论坛,搜索“草稿”,我在其中发布了我的基本设计的完整列表。)

我研究了新的 Solitaire 演示,它具有我需要的大部分动画功能。 Solitaire 的应用商店版本在我所有的 ios/android 设备上运行良好。一个小错误是可以拖动一组卡片,包括一些面朝下的卡片,这些卡片在拖动操作期间面朝上。也很难拿起正确数量的卡片。拖动一组卡片也会在屏幕上留下一条白色的痕迹,看起来不太好看。这也发生在模拟器中。

作为一个实验,我重新设计了我的 GUI 布局,使其与 Solitaire 代码完全相似:两层按钮,唯一的区别是我使用 GridLayout(10,10),而 Solitaire 使用 SolitaireLayout()。这是有效的,除了一件事:如果棋子在棋盘上向下移动,它会正确地移动到其他棋子上方,但如果棋子在棋盘上移动,它会在其他棋子下方移动。

我的代码如下所示:

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);

所以按钮似乎总是按它们的 GridLayout 顺序绘制,而我希望动画(移动)按钮将在最后/顶部绘制,就像纸牌中的移动卡片一样。

这是在 SolitaireLayout 和 GridLayout 中处理动画的区别吗?如果是这样,这可以在动画逻辑中改变吗?否则我必须添加一个额外的动画层和大量开销。

在 Android 上,动画(运动)无法正常工作。因此,我决定使用当前 CN1 版本(插件 3.2.6,libs 2016-01-11)在我的设备上自行构建和测试 Solitaire 演示。我将动画更改为慢 10 倍,以便更好地了解正在发生的事情。在模拟器中,布局和动画大部分都可以正常工作,但在我的 iOS 和 Android 设备上却有很多问题。

模拟器(Windows 7、NetBeans 8.0.2): - 我用文本替换了字体图标,因为它们丢失了。 - 但是如何访问汉堡菜单?我没有看到 3 个点,甚至没有空间。 - 有时可以拖动一组卡片,其中包含一些向下的卡片;拖动时,它们会暂时朝上翻转。 - 自动播放似乎并不总是有效。 (并非所有的动作都被播放。)

在 iOS 9.2 (iPad 4)、iOS 8.4 (iPhone 4) 上: - [?] 显示在汉堡菜单复选框中。 - 启动后,画面背景卡背跳到容器/屏幕的底部。 - 有时,在发牌动画中,最右边画面中的牌暂时朝上,而已经朝上的牌则翻转为朝下。然而,最终的交易状态是正确的。 - 撤消/重做:有时会导致牌组 0 上的牌面朝上,牌组 1 上的牌面朝下。 - 重做有时会“跳转”到新布局而不是动画。 仅限 iPhone: - 一系列自动播放动作留下了不一致的基础状态:顶牌红心-J、梅花-10、梅花-K、方块-Q;即,不同基础堆栈上的 2 张俱乐部卡。

在 Android 5.1.1 (Nexus 7) 上存在更多问题。 - 甲板上的交易动画不是一个漂亮的景象。卡片插入(滑动到)画面堆栈的底部,因此它们在其他卡片下方滑动。移动的牌应该面朝下,但通常面朝上,总是显示发给画面 1 的第一张牌。最右边画面中的牌暂时面朝上。最后,已经面朝上的牌被翻面朝下然后再翻过来。然而,最终的交易状态是正确的。 - 当连续有几个动作时,自动播放也不正确。在动画期间,几张面朝上的基础卡会暂时改变其卡值(正面)。移动的牌也会滑到基础牌下(有时会滑过,但这是个例外)。 - 完成游戏后,由于文本似乎没有叠加显示,而是显示在卡片下方(在一个大的空白区域中),而在屏幕顶部的一小部分中仅部分可见,因此完成游戏后,完成的屏幕已损坏。 - 开始新游戏经常失败,因为没有开始发牌;屏幕上显示了一个甲板和四个国王在地基上,而画面是空的。当敲击牌组时,会发一张牌并显示做得好的顺序。有时重复此操作会成功开始新游戏。

所有这一切已经花费了很多时间,但我的应用程序仍然无法再次运行,这非常令人沮丧。在这个话题上遇到了很多麻烦,即使下载了演示应用程序,使用 Codename One 构建这种类型的应用程序感觉就像在流沙上构建。请帮忙!

【问题讨论】:

【参考方案1】:

一旦遇到麻烦,您可能应该只是问一下,而不是在帖子上如此努力。是的,作为长期存在的动画问题(并行运行的动画可能发生冲突)的错误修复的一部分,我们确实进行了重大的兼容性破坏性更改。

这引入了一些冲突,但减少了设备/模拟器之间的不一致,这总是一件好事。

我们在这里宣布了这一点:https://www.codenameone.com/blog/new-animation-manager.html

现在创建便携式动画实际上要简单得多,因为所有内容都会同步以避免动画冲突,例如如果您在动画正在进行时执行Component.removeComponent(),它将被隐式添加到动画队列中,并在动画完成后执行,而不是立即执行。

将您的下一个动作推迟到我们拥有的动画之后:

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

更简单,没有特殊情况的全局锁。

将代码直接“移植”到新方法有点困难,但看起来您的动画逻辑依赖于耗时 1000 毫秒的动画,并且在方法返回时它完全完成,这可能并非总是如此(如 add/删除调用或其他逻辑可能会妨碍)。

过去,确保动画完成的唯一方法是将它们分开,但现在您可以使用flushAnimation 来确保所有动画都已完成。请记住,一些不是明确的动画的东西现在可能会意外地变成动画,例如如果在调用它们时正在运行动画,则添加/删除组件将变为动画...

【讨论】:

【参考方案2】:

很抱歉,我错过了 12 月 16 日关于新动画管理器的博文。纸牌演示日期为 10 月 8 日,我查看了 12 月 31 日的开发人员指南,所以我认为我是最新的。尽管如此,我还不清楚如何使用新的动画管理器。 Januari 11 的新开发人员指南没有说明该主题,您的答案/博客文章也不是很详细。

如果将 Solitaire 演示更新为与新的动画管理器一起使用,也会有很大帮助。

现在我的问题是:

1) 如何重写以下代码以使用新的动画管理器?这是一个真实的场景,因为我有多个步骤的动画应该连续执行。

// 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) 重写这个是否也解决了一个棋子(棋子)在其他棋子下方而不是在其他棋子上移动的问题?

3) 如何在两次 testAnimation 调用之间暂停? (显然不是睡觉。)


2016-01-14 更新(另见评论部分):

我用flushAnimations尝试了两件事:

1) 使用这样的函数:

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

并在连续动画之间调用它。这不起作用。

2) 嵌套的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);
        );
    );
);

这似乎适用于 iOS,但不适用于我的 Android 设备:通常在第二个动画之后屏幕闪烁并跳转到下一个状态而不是动画。除此之外,这不是一个好的解决方案。

然后我尝试了 UITimer 解决方案:

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) 
    ...

这可行,但也不是一个非常理想的解决方案。我有一个“自动播放”功能,可以重播由 >100 个动作组成的整个游戏,每个动作由 1 个或多个动画步骤组成。用户可以通过按下停止按钮来中断这个过程。

我非常想看看如何使用新的动画处理对扑克和纸牌(自动游戏!)演示进行编码。

请花一点时间点击这个链接,看看我在说什么类型的功能:http://toernooibase.kndb.nl/opvraag/applet.php?taal=1&kl=46&Id=4579&r=10&jr=16&wed=845502。只需按下板下方的自动播放按钮 ('>')。 (我的应用也可以玩游戏,您可以将其设置为每次移动思考 1 分钟,在此期间 GUI 被阻止。)

我看到开发者指南于 1 月 13 日更新,现在有一个关于休闲游戏编程的章节/附录。这很好,尽管它列出的 CN1Poker 版本仍然不能在 Android 设备上正常工作:发牌动画显示牌大多在跳跃,有时会滑到它们的位置。 (它在模拟器和 iOS 设备上运行良好。)

你怎么能说“现在创建便携式动画实际上要简单得多”?我认为就我而言,它实际上变得更加困难。对于扑克和纸牌演示中也使用的旧方法,我需要一种替代方法:使用 animateLayoutAndWait 并且在动画期间没有添加/删除操作(也由程序逻辑确保)。

作为一个基本订阅者(3 年了),我必须使用最新的 CN1 版本,但是对于当前的 CN1 版本,我不再知道如何编码。我认为这是一个非常基本的问题,因为所有游戏都使用这样的功能。

【讨论】:

它不能解决 2,因为这与 Z 排序有关。只需将组件“稍后”放在组件列表中即可。您可以像往常一样使用UITimer 来延迟下一次通话。我稍微修改了我的答案。请注意,您不应将更新作为答案,而应使用更多详细信息编辑您的原始问题(不更改其“核心问题”),或者只是提交一个完全可以的新问题。 我不明白如何解决 Z 排序问题。 '将组件“稍后”放在组件列表中'是什么意思?该组件是最后添加的(及时),但它当然不能在 GridLayout 顺序中最后添加,因为它的位置具有意义:它是一个棋盘。 (为什么这在旧的 Solitaire 演示中有效?)我更新了我的第二篇文章,尝试使用 flushAnimation 和 UITimer 提出您的建议。 它需要有最后一个组件索引,因此网格布局在这个意义上有点问题,因为它不是为 z 排序而设计的。我不确定是否有网格布局的解决方案。您可能可以使用TableLayout,它在与约束一起使用时对 z 排序不敏感。尽管对于某些复杂的用法来说它更加“不稳定”。 Solitaire 应该如何解决 Z 排序问题?它在旧动画设置中有效,但在新动画设置中无效。你忽略了我最后一个问题的大部分内容,我像你问的那样编辑了我的第二个问题(参见“更新 2016-01-14”),关于同步动画和程序逻辑。这一切都非常令人失望。炫耀一些非常好的演示(我的设计基于这些演示)对用户是不公平的,这些演示由于兼容性破坏而不再起作用,而且你不能告诉我如何解决这个问题。 我只看到了一个解决方案:我敦促您尽快更新 Poker & Solitaire 演示和文档以使用新的动画管理器。请给我一个时间表!当我看到演示使用当前 CN1 再次工作时,我会从那里研究代码。

以上是关于代号一个动画麻烦(也在纸牌演示中)?的主要内容,如果未能解决你的问题,请参考以下文章

HDU 2209 翻纸牌游戏(DFS)

如何用CSS3做过渡效果(transition)与动画(animation)麻烦告诉我

数字和字母代号的意思

(转)Android ADT 离线下载技巧(告别在线安装的麻烦)

Unity动画系统Animator动态添加事件

Docker是不是遇上麻烦了?