kivy / kivent

KivEnt is an entity-based game engine for Kivy
http://www.kivent.org
573 stars 94 forks source link

Examples does not work with kivy 2.1 #289

Open stasalcatraz opened 2 years ago

stasalcatraz commented 2 years ago

Can not run examples with kivy 2.1.0

Traceback (most recent call last): File "/home/alcatraz/devel/python/alcatraz/myrpg-env/src/kivent_test/examples/2_basic_app/./main.py", line 79, in YourAppNameApp().run() File "/home/alcatraz/devel/python/alcatraz/myrpg-env/lib/python3.10/site-packages/kivy/app.py", line 954, in run self._run_prepare() File "/home/alcatraz/devel/python/alcatraz/myrpg-env/lib/python3.10/site-packages/kivy/app.py", line 923, in _run_prepare self.load_kv(filename=self.kv_file) File "/home/alcatraz/devel/python/alcatraz/myrpg-env/lib/python3.10/site-packages/kivy/app.py", line 696, in load_kv root = Builder.load_file(rfilename) File "/home/alcatraz/devel/python/alcatraz/myrpg-env/lib/python3.10/site-packages/kivy/lang/builder.py", line 305, in load_file return self.load_string(data, **kwargs) File "/home/alcatraz/devel/python/alcatraz/myrpg-env/lib/python3.10/site-packages/kivy/lang/builder.py", line 405, in load_string widget.apply_class_lang_rules( File "/home/alcatraz/devel/python/alcatraz/myrpg-env/lib/python3.10/site-packages/kivy/uix/widget.py", line 470, in apply_class_lang_rules Builder.apply( File "/home/alcatraz/devel/python/alcatraz/myrpg-env/lib/python3.10/site-packages/kivy/lang/builder.py", line 540, in apply self._apply_rule( File "/home/alcatraz/devel/python/alcatraz/myrpg-env/lib/python3.10/site-packages/kivy/lang/builder.py", line 662, in _apply_rule self._apply_rule( File "/home/alcatraz/devel/python/alcatraz/myrpg-env/lib/python3.10/site-packages/kivy/lang/builder.py", line 659, in _apply_rule widget.add_widget(child) File "kivent_core/gameworld.pyx", line 610, in kivent_core.gameworld.GameWorld.add_widget kivy.uix.widget.WidgetException: Cannot add <kivent_core.systems.position_systems.PositionSystem2D object at 0x7f37dab9b100>, it already has a parent 1.0

jameslutt commented 2 years ago

me too.

jameslutt commented 2 years ago

In the file "yourappname.kv" comment out -> #TestGame: on line 6 will make it works.

stasalcatraz commented 1 year ago

In the file "yourappname.kv" comment out -> #TestGame: on line 6 will make it works.

Yes, because no TestGame widget and no gameworld is created. So example just does show empty Widget object as root widget. If you override the build() method of YourAppNameApp class and return new TestGame widget as root you've got the same error

amstelchen commented 1 year ago

So, I tried everything to get rid of the abovementioned WidgetException, rebuilt Kivy (both 2.1.0 and 2.2.0.dev0), rebuilt kivent_core, used older Python versions, without result. Is there any solution?

AccelQuasarDragon commented 6 months ago

I spent my whole morning on this. TL:DR; one issue is building (cymunk doesnt build properly, rip)

this works on WSL (so on the ubuntu side), using vcxsrv to see the linux windows on Windows, .xlaunch file is here:

<?xml version="1.0" encoding="UTF-8"?>
<XLaunch WindowMode="MultiWindow" ClientMode="NoClient" LocalClient="False" Display="-1" LocalProgram="xcalc" RemoteProgram="xterm" RemotePassword="" PrivateKey="" RemoteHost="" RemoteUser="" XDMCPHost="" XDMCPBroadcast="False" XDMCPIndirect="False" Clipboard="True" ClipboardPrimary="True" ExtraParams="" Wgl="True" DisableAC="True" XDMCPTerminate="False"/>

the issue lies when you add a system to the gameworld:

PositionSystem2D:
          system_id: 'position'
          gameworld: gameworld
          zones: ['general']
          size_of_component_block: 128

best guess: game systems are treated as kivy widgets and mess up the widget tree, hence the widget parent is a FLOAT, not even a widget as seen in this error: Cannot add <kivent_core.systems.position_systems.PositionSystem2D object at 0x7f37dab9b100>, it already has a parent 1.0

the original exception is here in gameworld.pyx:

raise WidgetException('Cannot add %r, it already has a parent %r'
                                  % (widget, parent))

THIS WORKS (but has a blank screen)

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.clock import Clock
from kivy.core.window import Window
from random import randint, choice, randrange
import kivent_core
from kivent_core.gameworld import GameWorld
from kivent_core.systems.position_systems import PositionSystem2D
from kivent_core.systems.renderers import Renderer
from kivent_core.systems.gamesystem import GameSystem
from kivent_core.managers.resource_managers import texture_manager
from kivy.properties import StringProperty, ObjectProperty
from kivy.factory import Factory
from kivent_core.managers.resource_managers import texture_manager
from os.path import dirname, join, abspath

texture_manager.load_atlas(join(dirname(dirname(abspath(__file__))), 'test', 'assets', 
    'stars.atlas'))

def lerp(v0, v1, t):
    return (1.-t)*v0 + t * v1

class FadingSystem(GameSystem):
    make_entity = ObjectProperty(None)

    def update(self, dt):
        entities = self.gameworld.entities
        for component in self.components:
            if component is not None:
                entity_id = component.entity_id
                entity = entities[entity_id]
                color_comp = entity.color
                component.current_time += dt
                current_time = component.current_time
                fade_out_start = component.fade_out_start
                time = component.time
                fade_out_time = time - fade_out_start
                if current_time >= time:
                    self.gameworld.remove_entity(entity_id)
                    self.make_entity()
                if current_time < fade_out_start:
                    color_comp.a = lerp(0., 255., 
                        current_time / fade_out_start)
                else:
                    color_comp.a = lerp(255., 0., 
                        (current_time - fade_out_start) / fade_out_time)

Factory.register('FadingSystem', cls=FadingSystem)

class TestGame(Widget):

    def init_game(self):
        self.setup_states()
        self.load_resources()
        self.set_state()
        self.draw_some_stuff()

    def load_resources(self):
        keys = ['star1', 'star2', 'star3', 'star_circle', 'star_square']
        self.model_keys = model_keys = []
        mk_a = model_keys.append
        model_manager = self.gameworld.model_manager
        load_textured_rectangle = model_manager.load_textured_rectangle
        for x in range(100):
            model_key = 'star_m_' + str(x)
            tex_key = choice(keys)
            wh = randrange(1., 7.)
            real_name = load_textured_rectangle(
                'vertex_format_4f', wh, wh, tex_key, model_key)
            mk_a((model_key, tex_key))

    def draw_some_stuff(self):
        init_entity = self.gameworld.init_entity
        for x in range(1000):
            self.draw_a_star()

    def draw_a_star(self):
        model_to_use = choice(self.model_keys)
        pos = randint(0, Window.width), randint(0, Window.height)
        fade_in = randrange(10., 15.)
        fade_out = randrange(10., 15.)
        create_dict = {
            'position': pos,
            'color': (255, 255, 255, 0),
            'renderer': {'texture': model_to_use[1], 
                'model_key': model_to_use[0]},
            'fade': {'time': fade_in + fade_out,
                'fade_out_start': fade_in, 
                'current_time': 0,},
        }
        ent = self.gameworld.init_entity(create_dict, ['position', 'color', 
            'renderer', 'fade'])

    def setup_states(self):
        self.gameworld.add_state(state_name='main', 
            systems_added=['renderer'],
            systems_removed=[], systems_paused=[],
            systems_unpaused=['renderer', 'fade'],
            screenmanager_screen='main')

    def set_state(self):
        self.gameworld.state = 'main'

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):
        Window.clearcolor = (0, 0, 0, 1.)

if __name__ == '__main__':
    YourAppNameApp().run()
#:import path os.path
#:import dirname os.path.dirname
#:import main __main__

TestGame:

<TestGame>:
    gameworld: gameworld
    GameWorld:
        id: gameworld
        gamescreenmanager: gamescreenmanager
        size_of_gameworld: 100*1024
        size_of_entity_block: 128
        system_count: 4
        size: root.size
        pos: root.pos
        zones: {'general': 10000}

    GameScreenManager:
        id: gamescreenmanager
        size: root.size
        pos: root.pos
        gameworld: gameworld

<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:'

so does this (also black screen)

import kivy
from kivy.app import App
from kivy.uix.widget import Widget
import kivent_core

class TestGame(Widget):
    def on_kv_post(self, *args):
        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()
#:kivy 1.11.0

TestGame:

<TestGame>:
    gameworld: gameworld
    GameWorld:
        id: gameworld
        gamescreenmanager: gamescreenmanager
        size_of_gameworld: 100*1024
        size_of_entity_block: 128
        system_count: 2
        zones: {'general': 10000}
    GameScreenManager:
        id: gamescreenmanager
        size: root.size
        pos: root.pos
        gameworld: gameworld
        GameScreen:
            name: 'main'