youwish
youwish

Reputation: 43

Flickering entities in curses python

Hey i got a lot of entities drawn with curses, they move dynamically and after i changed some code to async (for movement speed functionality) entities sometimes are flickering. What could be the potential issue? I will try to provide most important code snippets (imo), but you can also take a look at the source code on github.

main.py

def main(stdscr) -> None:
    win = stdscr

    width = win.getmaxyx()[1]
    height = win.getmaxyx()[0]

    ov = Overworld(win, width, height)
    system = System(ov, sysinfo)

    return asyncio.run(system.run())


if __name__ == "__main__":
    stdscr = setup_stdscr()
    main(stdscr)

system

async def refresh_overworld(self):
        while self._running:
            self.overworld.draw(force_static=self._static_update_iter % REFRESH_STATIC_AFTER == 0)
            await asyncio.sleep(0.3)

    async def update_system_info(self):
        while self._running:
            self.system_info.draw()
            await asyncio.sleep(0.1)

    async def refresh_stdscr(self):
        while self._running:
            self.overworld.stdscr.refresh()
            await asyncio.sleep(0.1)

    async def run(self) -> None:
        self.overworld.spawn_entities()
        self.overworld.stdscr.nodelay(True)

        update_task = asyncio.create_task(self.overworld.update())
        refresh_task = asyncio.create_task(self.refresh_overworld())

        refresh_stdscr = asyncio.create_task(self.refresh_stdscr())

        try:
            while self._running:
                self._static_update_iter += 1
                await asyncio.sleep(MINUTE_LENGTH)
        finally:
            refresh_stdscr.cancel()

            update_task.cancel()
            refresh_task.cancel()

            key_listener_task.cancel()

            self.overworld.stdscr.nodelay(False)
            self.overworld.stdscr.clear()
            self.overworld.stdscr.refresh()

            curses.endwin()
    ```

# overworld
```python
def _draw_entity(self, entity: Entity, position: Position):
        biome = self.biome.get_biome_by_coords(position.x, position.y)
        biome_color = self.biome.get_biome_color(biome)

        char = entity.representation

        try:
            self.stdscr.addstr(position.y, position.x, char, biome_color)
        except curses.error:
            pass

    def draw(self, force_static: bool = False):
        """
        Draw all the existing entities on the board.
        """
        logging.debug("Drawing entities in the overworld.")
        if not self._static_drawn or force_static:
            self.biome.draw()

            static_entities = [entity for entity in self.entities if not entity.dynamic]
            for entity in static_entities:
                self._draw_entity(entity, entity.position)
            self._static_drawn = True

        dynamic_entities = [entity for entity in self.entities if entity.dynamic]

        for entity in dynamic_entities:
            self._draw_entity(entity, entity.position)
        logging.debug("Entities drawn.")

    def end(self):
        """
        End the overworld 😲.
        """
        logging.debug("Ending the overworld. Clearing screen.")
        self.stdscr.clear()

    def get_entity_at_position(
        self, position: Position, dynamic_only: bool = True, range: int = 2
    ):
        entities = [
            entity for entity in self.entities if not dynamic_only or entity.dynamic
        ]
        for entity in entities:
            if entity.position.is_within_range(position, range):
                return entity
        return None

    def get_nearby_entities(self, entity: Entity, perception_range: int):
        nearby_entities = []
        for other_entity in self.entities:
            if entity.position.is_within_range(other_entity.position, perception_range):
                nearby_entities.append(entity)
        return nearby_entities

    def is_occupied(self, position: Position):
        return position in [entity.position for entity in self.entities]

    async def update_entity(self, entity):
        while True:
            await entity.update(self, self.biome)
            await asyncio.sleep(MINUTE_LENGTH / entity.properties.movement_speed)

    async def update(self):
        """
        Update all the entities in the overworld.
        """
        logging.debug("Updating entities in the overworld.")
        update_tasks = [asyncio.create_task(self.update_entity(entity)) for entity in self.entities if entity.dynamic]

        await asyncio.gather(*update_tasks)

        logging.info("Entities updated.")

    def _spawn_entities(self):
        for entity_class in ENTITIES:
            cap = self._calculate_entity_cap(entity_class.frequency)
            for _ in range(round(cap)):
                self.spawn_entity(entity_class)

    def spawn_entity(self, entity: Entity, position: Position = None):
        """
        Create new entity and add it to the overworld.

        Attributes:
            entity: the entity to add to the overworld
            position: the position to add the entity to
        """
        if not position:
            position = self._calculate_position()
            biome = self.biome.get_biome_by_coords(position.x, position.y)
        else:
            biome = self.biome.get_biome_by_coords(position.x, position.y)

        spawn_rate = self._get_spawn_rate(entity, biome)

        if random.random() < spawn_rate:
            entity = entity.create(position, biome)
            self.entities.append(entity)

vid: https://streamable.com/6xid97 entire code: https://github.com/style77/EcoSphere

Upvotes: 0

Views: 62

Answers (0)

Related Questions