如何在官方Kivy Pong教程中使用Scatter移动挡板?

3
完成了官方Kivy网站上的Pong游戏教程后,我继续学习他们的速成课程。在第一个视频中,我看到了这个神奇的东西,称为Scatter,它几乎可以让你通过鼠标轻松移动UI元素。
我认为这应该提供了一种更平滑的方式来控制 Pong游戏中的挡板。最初的方法是将挡板移动逻辑放入PongGame对象的on_touch_move()方法中(PongGame类继承自Widget),这很简单:
if touch.x < self.width / 3:  # if you clicked in 1/3 of the window to the left
    player1.center_y = touch.y  # level the first paddle's center vertically with the mouse click position

这会导致一个令人不舒服的开始,如果你碰巧将鼠标光标移动到球拍的下面或上面。我认为Scatter会更好地工作。但是,到目前为止,我未能使其起作用。
我首先注释了on_touch_move()方法,然后在pong.kv文件中将Scatter对象添加为PongGame类的子级,并将现有的PongPaddle对象设置为Scatter对象的子级。像这样:
<PongGame>:
    Scatter:
        do_scale: False
        do_rotation: False
        do_translation_x: False

        PongPaddle:
            id: player_left
            x: root.x
            center_y: root.center_y  

        PongPaddle:
            id: player_right
            x: root.width - self.width
            center_y: root.center_y

由于我使用了一个单一的散点对象,而且两个挡板需要独立移动,所以我想这可能会导致问题(点击一个会使两个同时移动),但我仍然认为这是一个好的开始。

没有运气!这不会让挡板随着鼠标光标移动。它们仍然弹跳球(即使它们在小部件树中向下移动,除了在PongGame类体中注释掉on_touch_move()方法之外,我没有改变Python代码 - 我想在pong.kv文件中连接到挡板的ObjectProperty实例引用仍然有效),但它们不会移动。

完整可运行代码(我的版本带有scatter)

完整可运行代码(我的版本没有scatter)

有什么办法可以让它工作吗?


你能发布整个可运行的代码(包括你自己的版本和scatter函数)吗?我认为我知道问题出在哪里,但在发布答案之前我想要检查一下。作为额外奖励,我将在答案中包含完整的代码。 - Edvardas Dlugauskas
刚刚添加了链接。我的Pong游戏版本与教程末尾的游戏有些不同。遵循作者的建议,我添加了一个“玩家获胜”标签和一个重新开始游戏的按钮。 - z33k
1个回答

1
所以问题在于挡板会跳到新位置,on_touch_move 方法负责此操作。在没有散点图的可运行代码中,我将第84-88行修改为:
def on_touch_move(self, touch):
    if touch.x < self.width / 3:
        self.player1.center_y += touch.dy
    if touch.x > self.width - self.width / 3:
        self.player2.center_y += touch.dy

基本上,触摸包含了y的增量值(y改变了多少),所以你只需要移动与鼠标移动相同的距离,而不是将挡板中心移动到鼠标的y。这使得游戏非常流畅和美观。我真的想知道为什么他们一开始就没有这样做。
不过有一个问题——现在挡板可以离开游戏屏幕很远了。这可以通过检查挡板中心是否超出屏幕(使用PongGame高度)来轻松解决。我会把这留作练习,但如果你卡住了,可以随时问我。
所以,既然你好奇,有一种方法可以用Scatter实现这个功能。首先,Scatter本身是可拖动、可调整大小和旋转的小部件,它不是布局(虽然它可以是,但我们只需要移动挡板本身,而不是整个屏幕)。因此,Paddle继承自Scatter。删除我们用于移动挡板的on_touch
现在你会注意到一些视觉上的问题。Scatter 在某些方面很奇怪。在 .kv 文件中删除 Paddlepos: self.pos这篇 帖子总结得很好:

...特定的行为使得散点图独特,有一些需要考虑的优势/限制:

  • 子元素相对于 0,0 定位。Scatter 位置不影响子元素的位置。
因此,在 Paddle 中,画布的位置是相对于 Paddle(scatter)而不是屏幕的。
现在花一分钟欣赏一下你得到的游戏。挡板可以随意移动、旋转等。您可以使用鼠标通过右键单击设置一个红点表示的想象中的“触摸”,然后进行移动设备类型的手势来调整大小和旋转。玩得开心,你值得拥有它。我们将在您休息后修复这些“错误”。

好的,所以Scatter还有一些功能是你不需要的。在PongPaddle类的.py文件中通过x禁用缩放、旋转和拖动:

do_rotation = do_scale = do_translation_x = Property(False)

我不确定我是否已经掌握了所有内容,Scatter可以做很多事情,其中一些您不需要或不想要。与以前的版本相比,Scatter pong需要更高的精度。您仍然需要代码来检查挡板是否超出边界。总的来说,我更喜欢以前的解决方案。

在这里您将找到带有Scatter的完整代码。


只需要改变代码中的4个字符?你在开玩笑吧?: )。这正是我想要挡板移动的方式。我还让它们停留在屏幕上:https://gist.github.com/ThreefoldBurly/66ddfe5ef72b1fe8be3e9a84ac245aa6 - z33k
我已经为你的答案点赞,但由于问题实际上是关于使用Scatter使挡板移动的,所以不会将其标记为已接受。我仍然不明白为什么在Crash Course的第一个视频中标签可以轻松移动,但在这里使用Scatter却无法使挡板移动。这是因为我们在这里有一个Widget对象作为基本小部件,而那里是FloatLayout吗?还是因为在CC中Scatter是添加到基本小部件中的第一个小部件? - z33k
抱歉,我意识到使用scatter并不是很优雅,但我会编辑答案以包含解决方案。Scatter有许多含义,但它仍然有自己的位置。只是这次恐怕不行。 - Edvardas Dlugauskas
啊,所以诀窍是让PongPaddle类继承Scatter而不是将Scatter小部件添加到PongGame对象中,然后再将PongPaddle对象添加到Scatter中。我明白在Crash Course中作者需要一个标签,所以他实际上无法让它从Scatter类继承。另一方面,PonglePaddle是一个通用的Widget,其上放置了一个矩形画布,因此完全可行并且运作良好。非常感谢! - z33k
我仍然觉得这是一个有趣的想法,任何人如果在运行您的代码时没有使用“do_rotation = do_scale = do_translation_x = Property(False)”都可以轻松地自行检查。这会引起一些欢乐! :) 这也非常详细地介绍了Scatter的内部工作原理。我鼓励每个人都去尝试一下。 - z33k
Yeah,Scatter 确实很有趣,但与其他小部件和行为方式混淆不清。是的,这个解决方案之所以有效,是因为 Paddle 是一个通用小部件,我们可以将它更改为 Scatter - 它里面没有任何东西。但我很确定 Scatter 可以作为一个布局本身使用,只要在设置子元素位置时小心坐标系!在使用 Scatter 时,就像我在答案中指出的那样,这是不同的。 - Edvardas Dlugauskas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接