https://github.com/kivy/kivent/wiki/Getting-Started-1:-Setting-Up-an-App
Start with the boilerplate debug code.
In [ ]:
%%file debug.kv
#:import random random
<Widget>:
canvas.after:
Color:
rgba: 1,1,1,.5
Line:
rectangle: self.x, self.y, self.width, self.height
width: 2
<DebugLabel@Button>:
size: self.parent.size
pos: self.parent.pos
background_color: random.random(), random.random(), random.random(), 0.6
text: 'debuglabel'
Now make a default kivy app consisting of main.py and yourappname.kv. (Note the reference to debug.kv in yourappname.kv and the debug label).
In [ ]:
%%file main.py
from kivy.app import App
from kivy.uix.widget import Widget
class TestGame(Widget):
pass
class YourAppNameApp(App):
def build(self):
pass
if __name__ == '__main__':
YourAppNameApp().run()
In [ ]:
%%file yourappname.kv
#:include debug.kv
#:kivy 1.9.0
TestGame:
<TestGame>:
DebugLabel:
text: "testgame"
In [ ]:
# run your app
!python main.py
Now check out two different ways to do the exact same thing with different instantiation code.
In [ ]:
%%file main.py
from kivy.app import App
from kivy.uix.widget import Widget
class TestGame(Widget):
pass
class YourAppNameApp(App):
def build(self):
return TestGame() ## change this from pass
if __name__ == '__main__':
YourAppNameApp().run()
In [ ]:
%%file yourappname.kv
#:include debug.kv
#:kivy 1.9.0
## Deleted the refernce to TestGame as the root widget
<TestGame>:
DebugLabel:
text: "testgame"
In [ ]:
# run your app. you should get the same thing.
!python main.py
In [ ]:
%%file main.py
from kivy.app import App
from kivy.uix.widget import Widget
# The TestGame class is now completely defined in the .kv file
class YourAppNameApp(App):
def build(self):
pass
if __name__ == '__main__':
YourAppNameApp().run()
In [ ]:
%%file yourappname.kv
#:include debug.kv
#:kivy 1.9.0
TestGame:
<TestGame@Widget>: ## adding makes TestGame a Widget class
DebugLabel:
text: "testgame"
In [ ]:
# run it again
!python main.py
Now in pure python with no .kv file...hmmmm...later.
In [ ]:
%%file main.py
from kivy.app import App
from kivy.uix.widget import Widget
import kivent_core ## add this import statement to kivent
class TestGame(Widget):
pass
class YourAppNameApp(App):
def build(self):
pass
if __name__ == '__main__':
YourAppNameApp().run()
First add the GameWorld Widget that will manage the game data. More specifically, it keeps track of the components and their updates.
In [ ]:
%%file yourappname.kv
#:include debug.kv
#:kivy 1.9.0
TestGame:
<TestGame@Widget>: ## adding @Widget makes this the root widget
gameworld: _gameworld ##reference to GameWorld id
DebugLabel:
text: "testgame"
GameWorld:
id: _gameworld
size_of_gameworld: 100*1024 ## required see explanation
size_of_entity_block: 128 ## required see explanation
system_count: 8 ## max number of GameSystems with components
zones: {'general':10000}
DebugLabel:
text: "gameworld"
We must configure a few options on the GameWorld. size_of_gameworld is the number of Kibibytes that will be allocated to hold static component data (the non-python components that have been cythonized extensively). size_of_entity_block is the size of the individual blocks (in kibibytes) of the memory pools for the GameWorld's entity memory. system_count is the maximum number of GameSystem with components. The size of the entities memory will be sum of the count for all zones * system_count + 1. The zones dict specifies which regions to reserve in memory and how large to make the regions. This allows you to organize your efficiencies by processing pattern so that our GameSystem can process a more contiguous arrangement of data. To learn more, read the memory handler documentation http://kivent.org/docs/memory_handlers.html.
Now add the GameScreenManager which will handle its Screens interactions with the GameWorld's state management.
In [ ]:
%%file yourappname.kv
#:include debug.kv
#:kivy 1.9.0
TestGame:
<TestGame>:
gameworld: _gameworld
DebugLabel:
text: "testgame"
GameWorld:
id: _gameworld
gamescreenmanager: _gamescreenmanager ## reference to GameScreenManager id
size_of_gameworld: 100*1024 ## number of Kibibytes allocated for the GameWorld to hold component data
size_of_entity_block: 128 ## kibibytes set aside for each entity
system_count: 8 ## max number of components
zones: {'general':10000} # regions to reserve in memory and how large to make the regions
DebugLabel:
text: "gameworld"
GameScreenManager:
id: _gamescreenmanager
size: root.size
pos: root.pos
gameworld: _gameworld
GameScreen:
name:'main'
Now for the python part.
When creating a GameWorld, we need to first call init_gameworld to finalize memory allocation and help us avoid kv creation races within our GameWorld widget tree. The first argument in this function is a list of the system_ids for GameSystems we need to ensure have finished their initialization before commencing the init_gameworld function. The callback argument allows you to set a function to be called after the initialization has finished. We will make use of that callback here:
In [ ]:
%%file main.py
from kivy.app import App
from kivy.uix.widget import Widget
import kivent_core ## add this import statement to kivent
class TestGame(Widget):
def __init__(self, **kwargs):
super(TestGame, self).__init__(**kwargs)
self.gameworld.init_gameworld([], callback = self.init_game)
def init_game(self):
self.setup_states()
self.set_state()
def setup_states(self):
self.gameworld.add_state(state_name='main',
systems_added = [],
systems_removed = [],
systems_paused = [],
systems_unpaused = [],
screenmanager_screen = 'main')
def set_state(self):
self.gameworld.state = 'main'
class YourAppNameApp(App):
def build(self):
pass
if __name__ == '__main__':
YourAppNameApp().run()
To check if things are working correctly, go ahead and run your code. Your screen output should still look the same, but now now you will get kivent logs with information telling you how much space has been allocated for your game:
[INFO ] [KivEnt ] Vertex Format: vertex_format_4f registered. Size per vertex is: 16. Format is [('pos', 2, 'float', 0, False), ('uvs', 2, 'float', 8, False)].
[INFO ] [KivEnt ] Vertex Format: vertex_format_7f registered. Size per vertex is: 28. Format is [('pos', 2, 'float', 0, False), ('uvs', 2, 'float', 8, False), ('rot', 1, 'float', 16, False), ('center', 2, 'float', 20, False)].
[INFO ] [KivEnt ] Vertex Format: vertex_format_4f4ub registered. Size per vertex is: 20. Format is [('pos', 2, 'float', 0, False), ('uvs', 2, 'float', 8, False), ('vColor', 4, 'ubyte', 16, True)].
[INFO ] [KivEnt ] Model Manager reserved space for vertex format: vertex_format_4f. 75 KiB was reserved for vertices, fitting a total of 4800. 25 KiB was reserved for indices fitting a total of 12800.
[INFO ] [KivEnt ] Model Manager reserved space for vertex format: vertex_format_7f. 75 KiB was reserved for vertices, fitting a total of 2742. 25 KiB was reserved for indices fitting a total of 12800.
[INFO ] [KivEnt ] Model Manager reserved space for vertex format: vertex_format_4f4ub. 75 KiB was reserved for vertices, fitting a total of 3840. 25 KiB was reserved for indices fitting a total of 12800.
[INFO ] [KivEnt ] We will need 684 KiB for game, we have 102400 KiB
In particular:
[INFO ] [KivEnt ] We will need 684 KiB for game, we have 102400 KiB
gives the total space we have reserved for our game, and the amount that we are actually using between all initialized GameSystems and the entity array itself. While not a count of the total memory your app will consume, as any python logic and the interpreter itself will take up a significant amount of memory as well, but it will help you reason about the size of the data your game logic is introducing and processing. This will ensure we do a minimum amount of memory allocation as a result of the game logic we will be introducing. Here we see only the amount of memory that will be required to hold 10,000 entities with 8 components each in the entities array.
[INFO ] [KivEnt ] We will need 384 KiB for game, we have 102400 KiB
In [ ]:
# insert screen output here
In [ ]:
#!python main.py
http://www.kivent.org/docs/gamesystems.html
http://www.kivent.org/docs/gamesystems.html#position-systems We'll start by adding the position system component. We'll use the class PositionSystem2D. PositionSystem2D abstracts 2 dimensional position data out into its own system so that all other GameSystem can interact with the position of an Entity without having to know specifically about anything under the hood used to determine the actual position. This GameSystem does no processing of its own, it just holds data.
When we declare the PositionSystem2D, we give it a system_id, which is the 'name' of its component, and a zones list, which tells the GameWorld how to allocate the memory for this GameSystem, the count for each zone name in the list will be reserved for this system, making the maximum number of entities that can be supported by this GameSystem the sum of the counts for each zone name.
In [ ]:
%%file yourappname.kv
#:include debug.kv
#:kivy 1.9.0
TestGame:
<TestGame>:
gameworld: _gameworld
DebugLabel:
text: "testgame"
GameWorld:
id: _gameworld
gamescreenmanager: _gamescreenmanager ## reference to GameScreenManager id
size_of_gameworld: 100*1024 ## number of Kibibytes allocated for the GameWorld to hold component data
size_of_entity_block: 128 ## kibibytes set aside for each entity
system_count: 8 ## max number of components
zones: {'general':10000} # regions to reserve in memory and how large to make the regions
DebugLabel:
text: "gameworld"
PositionSystem2D:
system_id: 'position'
gameworld: _gameworld
zones: ['general']
size_of_component_block: 128
GameScreenManager:
id: _gamescreenmanager
size: root.size
pos: root.pos
gameworld: _gameworld
GameScreen:
name:'main'
And add the corresponding initialization of the position system in main.py.
In [ ]:
%%file main.py
from kivy.app import App
from kivy.uix.widget import Widget
import kivent_core ## add this import statement to kivent
class TestGame(Widget):
def __init__(self, **kwargs):
super(TestGame, self).__init__(**kwargs)
## add the position system to the gameworld initialization
self.gameworld.init_gameworld(['position'], callback = self.init_game)
def init_game(self):
self.setup_states()
self.set_state()
def setup_states(self):
self.gameworld.add_state(state_name='main',
systems_added = [],
systems_removed = [],
systems_paused = [],
systems_unpaused = [],
screenmanager_screen = 'main')
def set_state(self):
self.gameworld.state = 'main'
class YourAppNameApp(App):
def build(self):
pass
if __name__ == '__main__':
YourAppNameApp().run()
See http://www.kivent.org/docs/gamesystems.html#rendering-systems
class kivent_core.systems.renderers.Renderer(**kwargs)
We will make this renderer static as we will use it to draw immobile background sprites. This is accomplished by setting the frame_count to 1 and updateable to False. size_of_batches controls the size of each batch submission to the GPU, Unity3d guys have recommended 1 Mib to 4 Mib sizes, here I have set it to 128 Kib. Finally we link our shader using shader_source.
In [ ]:
%%file yourappname.kv
#:include debug.kv
#:kivy 1.9.0
TestGame:
<TestGame>:
gameworld: _gameworld
DebugLabel:
text: "testgame"
GameWorld:
id: _gameworld
gamescreenmanager: _gamescreenmanager
size_of_gameworld: 100*1024
size_of_entity_block: 128
system_count: 8
zones: {'general':10000}
DebugLabel:
text: "gameworld"
PositionSystem2D:
system_id: 'position'
gameworld: _gameworld
zones: ['general']
size_of_component_block: 128
Renderer: ### add a renderer
id: _renderer
gameworld: _gameworld
system_id: 'renderer'
zones: ['general']
frame_count: 1
updateable: False ## updateable is as attribute of the GameSystem class
force_update: True
size_of_batches: 128
size_of_component_block: 128
### there's an error in the online tutorial here...it should be
shader_source: 'assets/glsl/positionshader.glsl'
GameScreenManager:
id: _gamescreenmanager
size: root.size
pos: root.pos
gameworld: _gameworld
GameScreen:
name:'main'
And now add the renderer to the gameworld initialization.
In [ ]:
%%file main.py
from kivy.app import App
from kivy.uix.widget import Widget
import kivent_core ## add this import statement to kivent
class TestGame(Widget):
def __init__(self, **kwargs):
super(TestGame, self).__init__(**kwargs)
## add the renderer system to the gameworld initialization
self.gameworld.init_gameworld(['position', 'renderer'], callback = self.init_game)
def init_game(self):
self.setup_states()
self.set_state()
def setup_states(self):
self.gameworld.add_state(state_name='main',
systems_added = [],
systems_removed = [],
systems_paused = [],
systems_unpaused = [],
screenmanager_screen = 'main')
def set_state(self):
self.gameworld.state = 'main'
class YourAppNameApp(App):
def build(self):
pass
if __name__ == '__main__':
YourAppNameApp().run()
Run your code again to make sure it works. Nothing in the appearance will have changed.
In [ ]:
!python main.py
Now, we have GameSystems set up, but they don't do anything yet. We need some assets to display. First off, load the sprites that we will want to display. To do this, use the GameWorld model_manager. The model manager handles the loading of VertexModels. You should only load model data using this ModelManager. Do not instantiate your own.
See http://www.kivent.org/docs/rendering.html?highlight=vertexformat#models for more on VertexModels.
Note the new import statements.
In [ ]:
%%file main.py
from kivy.app import App
from kivy.uix.widget import Widget
import kivent_core
from kivent_core.managers.resource_managers import texture_manager
## load the atlas with our sprites
texture_manager.load_atlas('assets/background_objects.atlas')
class TestGame(Widget):
def __init__(self, **kwargs):
super(TestGame, self).__init__(**kwargs)
self.gameworld.init_gameworld(['position', 'renderer'], callback = self.init_game)
def init_game(self):
self.setup_states()
self.set_state()
self.load_models()
def setup_states(self):
self.gameworld.add_state(state_name='main',
systems_added = [],
systems_removed = [],
systems_paused = [],
systems_unpaused = [],
screenmanager_screen = 'main')
def set_state(self):
self.gameworld.state = 'main'
def load_models(self):
### load a model of the 'star1' texture with 2 different sizes (7., 7.) and (10., 10.).
model_manager = self.gameworld.model_manager
model_manager.load_textured_rectangle('vertex_format_4f', 7., 7., 'star1', 'star1-4')
model_manager.load_textured_rectangle('vertex_format_4f', 10., 10., 'star1', 'star1-4-2')
class YourAppNameApp(App):
def build(self):
pass
if __name__ == '__main__':
YourAppNameApp().run()
All of our sprites will share the same model unless we create a copy, as we do when creating the 'star1-4-2' model, this way you can modify some entities model without affecting all entities.
In [ ]:
%%file main.py
from kivy.app import App
from kivy.uix.widget import Widget
import kivent_core
from kivent_core.managers.resource_managers import texture_manager
from random import randint, choice
from kivy.core.window import Window
## load the atlas with our sprites
texture_manager.load_atlas('assets/background_objects.atlas')
class TestGame(Widget):
def __init__(self, **kwargs):
super(TestGame, self).__init__(**kwargs)
self.gameworld.init_gameworld(['position', 'renderer'], callback = self.init_game)
def init_game(self):
self.setup_states()
self.set_state()
self.load_models()
self.draw_some_stuff()
def setup_states(self):
## add 'renderer' here to make sure the canvas knows about it and is unpaused
self.gameworld.add_state(state_name='main',
systems_added = ['renderer'],
systems_removed = [],
systems_paused = [],
systems_unpaused = ['renderer'],
screenmanager_screen = 'main')
def set_state(self):
self.gameworld.state = 'main'
def load_models(self):
### load a model of the 'star1' texture with 2 different sizes (7., 7.) and (10., 10.).
model_manager = self.gameworld.model_manager
model_manager.load_textured_rectangle('vertex_format_4f', 7., 7., 'star1', 'star1-4')
model_manager.load_textured_rectangle('vertex_format_4f', 10., 10., 'star1', 'star1-4-2')
def draw_some_stuff(self):
init_entity = self.gameworld.init_entity
for x in range(1000):
pos = randint(0, Window.width), randint(0, Window.height)
model_key = choice(['star1-4', 'star1-4-2'])
## create_dict will dictate the order to create components
create_dict = {
'position':pos,
'renderer': {'texture': 'star1', 'model_key': model_key}}
ent = init_entity(create_dict, ['position', 'renderer'])
class YourAppNameApp(App):
def build(self):
pass
if __name__ == '__main__':
YourAppNameApp().run()
If you run things now, you should be able to see stuff!!! Yay!!
In [ ]:
In [ ]:
%%file yourappname.kv
#:include debug.kv
#:kivy 1.9.0
TestGame:
<TestGame>:
gameworld: _gameworld
DebugLabel:
text: "testgame"
GameWorld:
id: _gameworld
gamescreenmanager: _gamescreenmanager
size_of_gameworld: 100*1024
size_of_entity_block: 128
system_count: 8
zones: {'general':10000}
DebugLabel:
text: "gameworld"
PositionSystem2D:
system_id: 'position'
gameworld: _gameworld
zones: ['general']
size_of_component_block: 128
Renderer: ### add a renderer
id: _renderer
gameworld: _gameworld
system_id: 'renderer'
zones: ['general']
frame_count: 1
updateable: False ## updateable is as attribute of the GameSystem class
force_update: True
size_of_batches: 128
size_of_component_block: 128
### there's an error in the online tutorial here...it should be
shader_source: 'assets/glsl/positionshader.glsl'
GameScreenManager:
id: _gamescreenmanager
size: root.size
pos: root.pos
gameworld: _gameworld
GameScreen:
name:'main'
<GameScreenManager>:
MainScreen:
id: _main_screen
<MainScreen@GameScreen>:
name: 'main'
FloatLayout:
DebugPanel:
size_hint: (.2, .1)
pos_hint: {'x': .225, 'y': .025}
<DebugPanel>:
Label:
pos: root.pos
size: root.size
font_size: root.size[1]*.5
halign: 'center'
valign: 'middle'
color: (1,1,1,1)
text: 'FPS: ' + root.fps if root.fps != None else 'FPS:'
In [ ]:
%%file main.py
from kivy.app import App
from kivy.uix.widget import Widget
import kivent_core
from kivent_core.managers.resource_managers import texture_manager
from random import randint, choice
from kivy.core.window import Window
from kivy.clock import Clock
from kivent_core.gameworld import GameWorld
from kivent_core.systems.position_systems import PositionSystem2D
from kivent_core.systems.renderers import Renderer
from kivent_core.managers.resource_managers import texture_manager
from kivy.properties import StringProperty
texture_manager.load_atlas('assets/background_objects.atlas')
class TestGame(Widget):
def __init__(self, **kwargs):
super(TestGame, self).__init__(**kwargs)
self.gameworld.init_gameworld(['position', 'renderer'], callback = self.init_game)
def init_game(self):
self.setup_states()
self.set_state()
self.load_models()
self.draw_some_stuff()
def setup_states(self):
self.gameworld.add_state(state_name='main',
systems_added = ['renderer'],
systems_removed = [],
systems_paused = [],
systems_unpaused = ['renderer'],
screenmanager_screen = 'main')
def set_state(self):
self.gameworld.state = 'main'
def load_models(self):
model_manager = self.gameworld.model_manager
model_manager.load_textured_rectangle('vertex_format_4f', 7., 7., 'star1', 'star1-4')
model_manager.load_textured_rectangle('vertex_format_4f', 10., 10., 'star1', 'star1-4-2')
def draw_some_stuff(self):
init_entity = self.gameworld.init_entity
for x in range(1000):
pos = randint(0, Window.width), randint(0, Window.height)
model_key = choice(['star1-4', 'star1-4-2'])
create_dict = {
'position':pos,
'renderer': {'texture': 'star1', 'model_key': model_key}}
ent = init_entity(create_dict, ['position', 'renderer'])
## the debug panel
class DebugPanel(Widget):
fps = StringProperty(None)
def __init__(self, **kwargs):
super(DebugPanel, self).__init__(**kwargs)
Clock.schedule_once(self.update_fps)
def update_fps(self,dt):
self.fps = str(int(Clock.get_fps()))
Clock.schedule_once(self.update_fps, .05)
class YourAppNameApp(App):
def build(self):
pass
if __name__ == '__main__':
YourAppNameApp().run()
In [ ]:
#!python main.py
Finally, the end of section 1.
In [ ]: