Source code for bascenev1lib.actor.background

# Released under the MIT License. See LICENSE for details.
#
"""Defines Actor(s)."""

from __future__ import annotations

import random
import weakref
import logging
from typing import TYPE_CHECKING, override

import bascenev1 as bs

if TYPE_CHECKING:
    from typing import Any


[docs] class Background(bs.Actor): """Simple Fading Background Actor.""" def __init__( self, fade_time: float = 0.5, start_faded: bool = False, show_logo: bool = False, ): super().__init__() self._dying = False self.fade_time = fade_time # We're special in that we create our node in the session # scene instead of the activity scene. # This way we can overlap multiple activities for fades # and whatnot. session = bs.getsession() self._session = weakref.ref(session) with session.context: self.node = bs.newnode( 'image', delegate=self, attrs={ 'fill_screen': True, 'texture': bs.gettexture('bg'), 'tilt_translate': -0.3, 'has_alpha_channel': False, 'color': (1, 1, 1), }, ) if not start_faded: bs.animate( self.node, 'opacity', {0.0: 0.0, self.fade_time: 1.0}, loop=False, ) if show_logo: logo_texture = bs.gettexture('logo') logo_mesh = bs.getmesh('logo') logo_mesh_transparent = bs.getmesh('logoTransparent') self.logo = bs.newnode( 'image', owner=self.node, attrs={ 'texture': logo_texture, 'mesh_opaque': logo_mesh, 'mesh_transparent': logo_mesh_transparent, 'scale': (0.7, 0.7), 'vr_depth': -250, 'color': (0.15, 0.15, 0.15), 'position': (0, 0), 'tilt_translate': -0.05, 'absolute_scale': False, }, ) self.node.connectattr('opacity', self.logo, 'opacity') # add jitter/pulse for a stop-motion-y look unless we're in VR # in which case stillness is better if not bs.app.env.vr: self.cmb = bs.newnode( 'combine', owner=self.node, attrs={'size': 2} ) for attr in ['input0', 'input1']: bs.animate( self.cmb, attr, {0.0: 0.693, 0.05: 0.7, 0.5: 0.693}, loop=True, ) self.cmb.connectattr('output', self.logo, 'scale') cmb = bs.newnode( 'combine', owner=self.node, attrs={'size': 2} ) cmb.connectattr('output', self.logo, 'position') # Gen some random keys for that stop-motion-y look. keys = {} timeval = 0.0 for _i in range(10): keys[timeval] = (random.random() - 0.5) * 0.0015 timeval += random.random() * 0.1 bs.animate(cmb, 'input0', keys, loop=True) keys = {} timeval = 0.0 for _i in range(10): keys[timeval] = (random.random() - 0.5) * 0.0015 + 0.05 timeval += random.random() * 0.1 bs.animate(cmb, 'input1', keys, loop=True) @override def __del__(self) -> None: # Normal actors don't get sent DieMessages when their # activity is shutting down, but we still need to do so # since our node lives in the session and it wouldn't die # otherwise. self._die() super().__del__() def _die(self, immediate: bool = False) -> None: session = self._session() if session is None and self.node: # If session is gone, our node should be too, # since it was part of the session's scene. # Let's make sure that's the case. # (since otherwise we have no way to kill it) logging.exception( 'got None session on Background _die' ' (and node still exists!)' ) elif session is not None: with session.context: if not self._dying and self.node: self._dying = True if immediate: self.node.delete() else: bs.animate( self.node, 'opacity', {0.0: 1.0, self.fade_time: 0.0}, loop=False, ) bs.timer(self.fade_time + 0.1, self.node.delete)
[docs] @override def handlemessage(self, msg: Any) -> Any: assert not self.expired if isinstance(msg, bs.DieMessage): self._die(msg.immediate) else: super().handlemessage(msg)
# Docs-generation hack; import some stuff that we likely only forward-declared # in our actual source code so that docs tools can find it. from typing import (Coroutine, Any, Literal, Callable, Generator, Awaitable, Sequence, Self) import asyncio from concurrent.futures import Future from pathlib import Path from enum import Enum