如何修复kivy游戏的纵横比?

How to fix aspect ratio of a kivy game?

我已经开始玩 Kivy,但我对这一切还很陌生并且已经在挣扎了。

我正在尝试制作棋盘游戏 - 我希望 window 可以调整大小,但我不想调整 window 的大小来弄乱游戏的纵横比(所以换句话说,如果 window 的大小调整为内容的预期纵横比以外的其他内容,我希望 window 在内容的上方或两侧有黑条)

我认为确保这一点的最简单方法是:

a) 锁定 Window 本身的纵横比,使其始终为 10:9(面板和所有屏幕元素的纵横比)- 即使在全屏时也是如此。

b) 使用某种 widget/surface/layout 以 window 为中心并且具有锁定的 10:9 纵横比。然后我随后将其用作放置我所有其他图像、小部件、网格等的基础。

但是,我真的不知道该怎么做。我不确定我是否可以锁定 Window 纵横比,而且我发现唯一可以锁定纵横比的 kivy 特定对象来自 kivy.graphics.image... 我不知道似乎不能用作 'base' 我的其他东西。

编辑:到目前为止,我已经编写了以下代码:它创建了一个布局(并将其着色为略带红色),无论何时调整大小时,它 'fixes' 都有自己的纵横比。但是,它仍然不在 window 的中心,而且问题更大,它会导致无限循环(可能是因为方面修复代码更正了大小,但随后 kivy 'corrects' 它的大小又恢复为window 的大小,再次触发方面修复,以为我可能是错的)。

编辑:又修改了代码,还是死循环。我虽然只参考父级的尺寸信息可以解决这个问题,但显然不是。

如果有人帮助我修复我的代码,我将不胜感激。

代码如下:

test.py

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.relativelayout import RelativeLayout
from kivy.properties import (ObjectProperty,
                             NumericProperty,
                             ReferenceListProperty)
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Rectangle

class Board(AnchorLayout):
    pass

class BackgroundLayout(RelativeLayout):
    def FixAspectRatio(self, *args):
        correctedsize = self.parent.size
        if correctedsize[0] > correctedsize[1]*(10/9):
            correctedsize[0] = correctedsize[1]*(10/9)
        elif correctedsize[0] < correctedsize[1]*(10/9):
            correctedsize[1] = correctedsize[0]/(10/9)
        return correctedsize

class test(App):
    game = ObjectProperty(None)

    def build(self):
        self.game = Board()
        return self.game

if __name__ == '__main__':
    test().run()

test.kv

<Board>
    BackgroundLayout:
        canvas.before:
            Color:
                rgba: 1, 0, 0, 0.5
            Rectangle:
                size: self.size
                pos: self.pos
        size: self.FixAspectRatio(self.parent.size)
        pos: self.parent.pos

一种方法是创建一个始终在给定纵横比的情况下最大化子项的布局。这是一个例子::

from kivy.lang import Builder
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.properties import NumericProperty

kv = """
ARLayout:
    Widget:
        canvas:
            Color:
                rgb: 1, 0, 0
            Rectangle:
                size: self.size
                pos: self.pos
"""

class ARLayout(RelativeLayout):
    # maximize the children given the ratio
    ratio = NumericProperty(10 / 9.)

    def do_layout(self, *args):
        for child in self.children:
            self.apply_ratio(child)
        super(ARLayout, self).do_layout()

    def apply_ratio(self, child):
        # ensure the child don't have specification we don't want
        child.size_hint = None, None
        child.pos_hint = {"center_x": .5, "center_y": .5}

        # calculate the new size, ensure one axis doesn't go out of the bounds
        w, h = self.size
        h2 = w * self.ratio
        if h2 > self.height:
            w = h / self.ratio
        else:
            h = h2
        child.size = w, h


class TestApp(App):
    def build(self):
        return Builder.load_string(kv)

TestApp().run()

在 kivy 聊天中 kived 的大量帮助下,我设法找到了一个令我满意的解决方案。

下面的游戏边界基于 'background' 图像,然后在此背景图像上放置一个相对布局。对于所有后续的 children,坐标 (0, 0) 将参考(纵横比锁定)背景图像的左下角,他们可以使用背景的 'magnification' 属性根据屏幕尺寸调整他们的size/position。

提供任何 gamebounds.png 图片,下面的代码将实现它。请注意,RelativeLayout 是红色阴影(透明度为 50%)只是为了显示其位置(并显示它始终匹配 gamebounds.png 图像大小和位置):

test.py

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.relativelayout import RelativeLayout
from kivy.properties import (ObjectProperty,
                             NumericProperty,
                             ReferenceListProperty,
                             ListProperty)
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Rectangle

class Game(FloatLayout):
    pass

class Background(Image):
    offset = ListProperty()
    magnification = NumericProperty(0)


class Bounds(RelativeLayout):
    pass

class test(App):
    game = ObjectProperty(None)

    def build(self):
        self.game = Game()
        return self.game

if __name__ == '__main__':
    test().run()

test.kv

<Game>
    Background:
        id: BackgroundId
        #Provide the image that will form the aspect-locked bounds of the
        #game.  Image widgets have aspect_lock set to True by default.
        source: 'gamebounds.png'
        #All the image to stretch to fill the available window space.
        allow_stretch: True
        size: self.parent.size
        #Find the coordinates of the bottom-left corner of the image from
        #the bottom-left corner of the window.  Call this the 'offset'.
        offset: [self.center_x - (self.norm_image_size[0]/2),
        self.center_y - (self.norm_image_size[1]/2)]
        #Find out the factor by which the image is magnified/shrunk from
        #its 'default' value:
        magnification: self.norm_image_size[0] / self.texture_size[0]

        Bounds:
            #The canvas below isn't needed, it's just used to show the 
            #position of the RelativeLayout
            canvas:
                Color:
                    rgba: 1, 0, 0, 0.5
                Rectangle:
                    size: self.size
                    pos: (0, 0)
            #Set the position of the RelativeLayout so it starts at the
            #bottom left of the image
            pos: self.parent.offset
            #Set the size of the RelativeLayout to be equal to the size of
            #the image in Background
            size: self.parent.norm_image_size