I've been making an Android Snake game using kivy
and buildozer
and after many iterations of using different values, layouts and approaches(and many searches on Google, StackOverflow and kivy
forums/documentation) I've reached a point where I am seeking help here myself.
The issue is so;
the grid looks great when the kivy window is 800x600, with the goal of the rectangles being perfect, even sided squares. The moment the window maximizes and the window turns into 1920x1080(16:9), the squares become wide rectangles and thus uneven. Unfortunately, trying the game on my OnePlus 7 Pro, with the aspect ratio of 19:5:9 and resolution of 1440x3120 the undesired result is even more extreme.
currently I am using a GridLayout
populated by Rectangle
to achieve the level's grid. I've tried making formulas based on Window.size
, Window.width
, I've tried using size_hint
, size
, width
, cols_minimum
and kivy.metrics
so far with no success.
As a side note, if anything of what I am trying to achieve or in the code below can be done in a better way, please let me know. Any alternatives would be appreciated, I am very inexperienced and new to both kivy
and Python
.
My code:
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.properties import ListProperty
from kivy.uix.gridlayout import GridLayout
from kivy.uix.widget import Widget
class GridCell(Widget):
color = ListProperty([1, 1, 1, 1])
size = ListProperty([0, 0])
pos = ListProperty([0, 0])
def __init__(self, size, x, y, **kwargs):
super().__init__(**kwargs)
self.color = (0.0, 0.0, 0.0, 1.0)
self.size = (size, size)
self.pos = (x, y)
class Level(GridLayout):
def __init__(self, **kwargs):
super(Level, self).__init__(**kwargs)
self.cols = 30
self.cell_size = 28
self.spacing = 2
self.positions = [(row, column) for row in range(self.cols) for column
in range(self.cols)]
self.grid_cells = {}
self.create_grid()
def create_grid(self):
for position in self.positions:
self.grid_cells[position] = GridCell(self.cell_size, *position)
self.add_widget(self.grid_cells[position])
if __name__ == '__main__':
from kivy.app import App
Builder.load_file('grid_cell.kv')
Window.clearcolor = (1.0, 1.0, 1.0, 1.0)
class LevelApp(App):
def build(self):
self.title = 'Level'
level = Level()
return level
level_app = LevelApp()
level_app.run()
grid_cell.kv:
<GridCell>:
canvas:
Color:
rgba: self.color
Rectangle:
pos: self.pos
size: self.size
Note that you cannot fill a non-square window with a square grid of square cells. But, to keep the cells square and base their size on the GridLayout
, try changing the GridCell
class to:
class GridCell(Widget):
color = ListProperty([1, 1, 1, 1])
def __init__(self, x, y, **kwargs):
super().__init__(**kwargs)
self.color = (0.0, 0.0, 0.0, 1.0)
self.pos = (x, y)
Note that you don't need to define pos
and size
properties since Widget
already has them, and I have removed size
from the __init__()
method.
And change Level
to:
class Level(GridLayout):
def __init__(self, **kwargs):
super(Level, self).__init__(**kwargs)
self.cols = 30
self.rows = 30
self.spacing = 2
self.positions = [(row, column) for row in range(self.rows) for column
in range(self.cols)]
self.grid_cells = {}
self.create_grid()
def create_grid(self):
for position in self.positions:
self.grid_cells[position] = GridCell(*position)
self.add_widget(self.grid_cells[position])
Then in your kv
:
<GridCell>:
grid_cols: self.parent.cols if self.parent and self.parent.cols else 1
grid_rows: self.parent.rows if self.parent and self.parent.rows else 1
tmp_size: min(self.parent.width/self.grid_cols - self.parent.spacing[0], self.parent.height/self.grid_rows - self.parent.spacing[1]) if self.parent else 0
size_hint: None, None
size: self.tmp_size, self.tmp_size
canvas:
Color:
rgba: self.color
Rectangle:
pos: self.pos
size: self.size
This calculates a tmp_size
based on the GridLayout
size
, rows
, and cols
, and assigns that size to the GridCell
. The if
statements in the kv
are necessary because the <GridCell>
rule is applied before the GridCell
gets its parent
assigned.