如何在 Kivy 中使用等距/正交视图?

Posted

技术标签:

【中文标题】如何在 Kivy 中使用等距/正交视图?【英文标题】:How to work with a isometric / orthogonal view in Kivy? 【发布时间】:2019-07-19 09:26:32 【问题描述】:

下图是一个带有按钮的 10 x 10 GridLayout。

我想在等距/正交二维视图中创建相同的网格。

这意味着每个按钮,而不是一个正方形,它可能像一个菱形,如下图:

我该怎么做?

【问题讨论】:

到目前为止,我只遇到过一个这样的例子。它是六边形而不是菱形,但希望它会帮助你。万一链接断开,它是 YouTube youtube.com/watch?v=hM9qbW8-roE 上标题为“用 Kivy (Dorian Pula) 用 Python 编写战争游戏的艺术”的视频 【参考方案1】:

我认为您实际上不能在 kivy UIX 小部件上进行 3D 旋转,但您可以进行 2D 旋转和缩放。下面是一个在 build() 方法中执行此操作的 App 示例:

from kivy.app import App
from kivy.graphics.context_instructions import PushMatrix, Rotate, Scale, PopMatrix
from kivy.properties import BooleanProperty
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
import numpy as np


def matrixToNumpy(mat):
    a = []
    for i in range(4):
        b = []
        for j in range(4):
            b.append(mat[i*4+j])
        a.append(b)
    npmat = np.mat(a)
    return npmat


class MyButton(Button):

    def on_touch_down(self, touch):
        if not self.parent.touched:
            self.parent.touched = True
            if self.parent.mat is None:
                scale = matrixToNumpy(self.parent.sca.matrix)
                rotate = matrixToNumpy(self.parent.rot.matrix)
                self.parent.mat = np.matmul(rotate, scale)
                self.parent.inv_mat = self.parent.mat.I
            npTouch = np.mat([touch.x, touch.y, 0, 1.0])
            convTouch = np.matmul(npTouch, self.parent.inv_mat)
            touch.x = convTouch[0,0]
            touch.y = convTouch[0,1]
        return super(MyButton, self).on_touch_down(touch)

    def on_touch_up(self, touch):
        self.parent.touched = False
        return super(MyButton, self).on_touch_up(touch)


class MyGridLayout(GridLayout):
    touched = BooleanProperty(False)

    def __init__(self, **kwargs):
        super(MyGridLayout, self).__init__(**kwargs)
        self.mat = None
        self.inv_mat = None


class MyApp(App):
    def build(self):
        layout = MyGridLayout(cols=10)
        with layout.canvas.before:
            PushMatrix()
            layout.sca = Scale(1.0, 0.5, 1.0)
            layout.rot = Rotate(angle=45, axis=(0,0,1), origin=(400,300,0))
        with layout.canvas.after:
            PopMatrix()
        for i in range (1, 101):
            layout.add_widget(MyButton(text=str(i)))
        return layout

MyApp().run()

我怀疑通过巧妙地应用这两个kivy.graphics.context_instructions,您可以模拟您想要的。 PushMatrix()PopMatrix()ScaleRotate 的效果仅限于 MyGridLayout。您应该能够使用 layout.scalayout.rot 引用来调整这些值。

在我最初的回答之后,我注意到Buttons 看起来不错,但不再起作用。我添加了一堆代码来解决这个问题。所有numpy 矩阵的东西只是为了让鼠标按下位置与MyGridLayout 相同的坐标。不幸的是,Kivy 事件不会自动考虑应用Canvas 缩放和旋转,因此需要额外的代码。

这是它的样子:

【讨论】:

抱歉,我是 Kivy 的初学者,能否请您使用整个“来自 kivy”的导入来完成您的代码? 有几件事要记住。首先,这仅适用于包含MyButton 实例的MyGridLayout(中间没有其他布局)。其次,如果动态更改RotateScale的值,则需要在MyGridLayout实例中将self.mat设置为None,以触发矩阵的重新计算。 实际上你可以做 3D 旋转,但我认为这仍然是一个更方便的解决方案,尤其是在这类游戏中,你通常需要等距 3D(如图所示)有时称为 2.5D ,而不是全 3D。 @Tshirtman,我看到了对象的 3D 旋转(如 MonkeyHead 示例),但没有看到 GUI 元素。我尝试过 GUI 元素的 3D 旋转,但我永远无法让它工作。如果你有如何做的指针,我很乐意看到它。 @RogérioDec,@Tshirtman 提到的kivy3dgui 看起来像您正在寻找的东西。它使用 kivy gui 元素进行真正的 3D。如果您正在制作某种 3D 游戏,我认为这比我的回答更适合您。

以上是关于如何在 Kivy 中使用等距/正交视图?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Kivy 中制作动态地图视图?

SDL Tile 和 Sprite 渲染地形

如何将两个按钮与 UIView 的中心等距对齐

如何在 Kivy 中动态更改标签背景颜色

如何通过拖动滑块来控制我的 Kivy 滚动视图?

如何在 Kivy 的 Scrollable GridLayout 中显示图像