robotengine.engine
引擎是 robotengine 的核心部分,负责管理节点的初始化、运行和更新。
Engine 同时还存储了一些全局变量,如帧数 frame 和时间戳 timestamp等。
在 Node 类中可以通过使用 self.engine 来访问引擎。
1""" 2 3引擎是 robotengine 的核心部分,负责管理节点的初始化、运行和更新。 4 5Engine 同时还存储了一些全局变量,如帧数 frame 和时间戳 timestamp等。 6 7在 Node 类中可以通过使用 self.engine 来访问引擎。 8 9""" 10import threading 11import time 12from enum import Enum 13from robotengine.input import Input, GamepadListener 14from robotengine.node import ProcessMode 15from robotengine.tools import warning, error 16 17class InputDevice(Enum): 18 """ 输入设备枚举 """ 19 KEYBOARD = 0 20 """ 键盘输入 """ 21 MOUSE = 1 22 """ 鼠标输入 """ 23 GAMEPAD = 2 24 """ 手柄输入 """ 25 26 27class Engine: 28 """ 引擎类 """ 29 from robotengine.node import Node 30 def __init__(self, root: Node, frequency: float=240, input_devices: InputDevice=[]): 31 """ 32 初始化引擎 33 34 参数: 35 36 root (Node): 根节点 37 38 frequency (int, optional): 影响所有节点的 _process 函数的调用频率。默认值为 240。 39 40 input_devices (list, optional): 输入设备列表,当为空时,节点的 _input() 函数将不会被调用。默认值为 []。 41 """ 42 self.root = root 43 """ 根节点 """ 44 self.paused = False 45 """ 是否暂停 """ 46 47 self._frequency = frequency 48 self._frame = 0 49 self._timestamp = 0.0 50 51 self._time_frequency = 30 52 53 self.input = Input() 54 """ 输入类, 在 Engine 初始化完成后,每个 Node 都可以通过 self.input 来访问输入类 """ 55 56 self._initialize() 57 58 self._shutdown = threading.Event() 59 if input_devices: 60 if InputDevice.GAMEPAD in input_devices: 61 self._gamepad_listener = GamepadListener() 62 63 self._input_thread = threading.Thread(target=self._input, daemon=True) 64 self._input_thread.start() 65 66 self._update_thread = threading.Thread(target=self._update, daemon=True) 67 self._update_thread.start() 68 69 self._timer_thread = threading.Thread(target=self._timer, daemon=True) 70 self._timer_thread.start() 71 72 def _initialize(self): 73 from robotengine.node import Node 74 def init_recursive(node: Node): 75 for child in node.get_children(): 76 init_recursive(child) # 先初始化子节点 77 78 node.engine = self # 设置引擎引用 79 node.input = self.input # 设置输入引用 80 81 node._init() # 当前节点初始化 82 83 def ready_recursive(node: Node): 84 for child in node.get_children(): 85 ready_recursive(child) # 子节点准备完成 86 node._ready_execute() 87 88 init_recursive(self.root) 89 ready_recursive(self.root) 90 91 def _process_update(self, delta): 92 from robotengine.node import Node 93 def update_recursive(node: Node, delta): 94 for child in node.get_children(): 95 update_recursive(child, delta) 96 node._update(delta) 97 update_recursive(self.root, delta) 98 99 def _update(self): 100 self._run_loop(1, precise_control=False, process_func=self._process_update) 101 102 def _process_timer(self, delta): 103 from robotengine.node import Node 104 def timer_recursive(node: Node, delta): 105 for child in node.get_children(): 106 timer_recursive(child, delta) 107 node._timer(delta) 108 timer_recursive(self.root, delta) 109 110 def _timer(self): 111 self._run_loop(self._time_frequency, precise_control=False, process_func=self._process_timer) 112 113 def _input(self): 114 from robotengine.node import Node 115 from robotengine.input import InputEvent 116 def input_recursive(node: Node, event: InputEvent): 117 for child in node.get_children(): 118 input_recursive(child, event) 119 node._input(event) 120 121 while not self._shutdown.is_set(): 122 if self._gamepad_listener: 123 for _gamepad_event in self._gamepad_listener.listen(): 124 self.input.update(_gamepad_event) 125 126 input_recursive(self.root, _gamepad_event) 127 128 def _process(self, delta): 129 from robotengine.node import Node 130 def process_recursive(node: Node): 131 if self.paused: 132 if node.process_mode == ProcessMode.WHEN_PAUSED or node.process_mode == ProcessMode.ALWAYS: 133 node._process(delta) 134 else: 135 if node.process_mode == ProcessMode.PAUSABLE or node.process_mode == ProcessMode.ALWAYS: 136 node._process(delta) 137 for child in node.get_children(): 138 process_recursive(child) 139 140 process_recursive(self.root) 141 142 def run(self): 143 """ 开始运行引擎 """ 144 self._run_loop(self._frequency, precise_control=True, process_func=self._process, main_loop=True) 145 146 def stop(self): 147 """ 停止运行引擎 """ 148 self._shutdown.set() 149 150 def _run_loop(self, frequency, precise_control=False, process_func=None, main_loop=False): 151 interval = 1.0 / frequency 152 threshold = 0.03 153 154 last_time = time.perf_counter() 155 next_time = last_time 156 first_frame = True 157 158 while not self._shutdown.is_set(): 159 current_time = time.perf_counter() 160 delta = current_time - last_time 161 last_time = current_time 162 163 if not first_frame and process_func: 164 process_func(delta) 165 if main_loop: 166 self._frame += 1 167 self._timestamp += delta 168 else: 169 first_frame = False 170 171 next_time += interval 172 sleep_time = next_time - time.perf_counter() 173 174 if precise_control: 175 if sleep_time > threshold: 176 time.sleep(sleep_time - threshold) 177 178 while time.perf_counter() < next_time: 179 pass 180 181 else: 182 if sleep_time > 0: 183 time.sleep(max(0, sleep_time)) 184 185 if sleep_time <= 0 and main_loop: 186 warning(f"当前帧{self._frame}耗时过长,耗时{delta:.5f}s") 187 188 189 def get_frame(self) -> int: 190 """获取当前帧数""" 191 return self._frame 192 193 def get_timestamp(self) -> float: 194 """获取当前时间戳""" 195 return self._timestamp 196 197 def print_tree(self): 198 """打印节点树""" 199 from .node import Node 200 def print_recursive(node: Node, prefix="", is_last=False, is_root=False): 201 if is_root: 202 print(f"{node}") # 根节点 203 else: 204 if is_last: 205 print(f"{prefix}└── {node}") # 最后一个子节点 206 else: 207 print(f"{prefix}├── {node}") # 其他子节点 208 209 for i, child in enumerate(node.get_children()): 210 is_last_child = (i == len(node.get_children()) - 1) 211 print_recursive(child, prefix + " ", is_last=is_last_child, is_root=False) 212 213 print_recursive(self.root, is_last=False, is_root=True)
class
InputDevice(enum.Enum):
18class InputDevice(Enum): 19 """ 输入设备枚举 """ 20 KEYBOARD = 0 21 """ 键盘输入 """ 22 MOUSE = 1 23 """ 鼠标输入 """ 24 GAMEPAD = 2 25 """ 手柄输入 """
输入设备枚举
Inherited Members
- enum.Enum
- name
- value
class
Engine:
28class Engine: 29 """ 引擎类 """ 30 from robotengine.node import Node 31 def __init__(self, root: Node, frequency: float=240, input_devices: InputDevice=[]): 32 """ 33 初始化引擎 34 35 参数: 36 37 root (Node): 根节点 38 39 frequency (int, optional): 影响所有节点的 _process 函数的调用频率。默认值为 240。 40 41 input_devices (list, optional): 输入设备列表,当为空时,节点的 _input() 函数将不会被调用。默认值为 []。 42 """ 43 self.root = root 44 """ 根节点 """ 45 self.paused = False 46 """ 是否暂停 """ 47 48 self._frequency = frequency 49 self._frame = 0 50 self._timestamp = 0.0 51 52 self._time_frequency = 30 53 54 self.input = Input() 55 """ 输入类, 在 Engine 初始化完成后,每个 Node 都可以通过 self.input 来访问输入类 """ 56 57 self._initialize() 58 59 self._shutdown = threading.Event() 60 if input_devices: 61 if InputDevice.GAMEPAD in input_devices: 62 self._gamepad_listener = GamepadListener() 63 64 self._input_thread = threading.Thread(target=self._input, daemon=True) 65 self._input_thread.start() 66 67 self._update_thread = threading.Thread(target=self._update, daemon=True) 68 self._update_thread.start() 69 70 self._timer_thread = threading.Thread(target=self._timer, daemon=True) 71 self._timer_thread.start() 72 73 def _initialize(self): 74 from robotengine.node import Node 75 def init_recursive(node: Node): 76 for child in node.get_children(): 77 init_recursive(child) # 先初始化子节点 78 79 node.engine = self # 设置引擎引用 80 node.input = self.input # 设置输入引用 81 82 node._init() # 当前节点初始化 83 84 def ready_recursive(node: Node): 85 for child in node.get_children(): 86 ready_recursive(child) # 子节点准备完成 87 node._ready_execute() 88 89 init_recursive(self.root) 90 ready_recursive(self.root) 91 92 def _process_update(self, delta): 93 from robotengine.node import Node 94 def update_recursive(node: Node, delta): 95 for child in node.get_children(): 96 update_recursive(child, delta) 97 node._update(delta) 98 update_recursive(self.root, delta) 99 100 def _update(self): 101 self._run_loop(1, precise_control=False, process_func=self._process_update) 102 103 def _process_timer(self, delta): 104 from robotengine.node import Node 105 def timer_recursive(node: Node, delta): 106 for child in node.get_children(): 107 timer_recursive(child, delta) 108 node._timer(delta) 109 timer_recursive(self.root, delta) 110 111 def _timer(self): 112 self._run_loop(self._time_frequency, precise_control=False, process_func=self._process_timer) 113 114 def _input(self): 115 from robotengine.node import Node 116 from robotengine.input import InputEvent 117 def input_recursive(node: Node, event: InputEvent): 118 for child in node.get_children(): 119 input_recursive(child, event) 120 node._input(event) 121 122 while not self._shutdown.is_set(): 123 if self._gamepad_listener: 124 for _gamepad_event in self._gamepad_listener.listen(): 125 self.input.update(_gamepad_event) 126 127 input_recursive(self.root, _gamepad_event) 128 129 def _process(self, delta): 130 from robotengine.node import Node 131 def process_recursive(node: Node): 132 if self.paused: 133 if node.process_mode == ProcessMode.WHEN_PAUSED or node.process_mode == ProcessMode.ALWAYS: 134 node._process(delta) 135 else: 136 if node.process_mode == ProcessMode.PAUSABLE or node.process_mode == ProcessMode.ALWAYS: 137 node._process(delta) 138 for child in node.get_children(): 139 process_recursive(child) 140 141 process_recursive(self.root) 142 143 def run(self): 144 """ 开始运行引擎 """ 145 self._run_loop(self._frequency, precise_control=True, process_func=self._process, main_loop=True) 146 147 def stop(self): 148 """ 停止运行引擎 """ 149 self._shutdown.set() 150 151 def _run_loop(self, frequency, precise_control=False, process_func=None, main_loop=False): 152 interval = 1.0 / frequency 153 threshold = 0.03 154 155 last_time = time.perf_counter() 156 next_time = last_time 157 first_frame = True 158 159 while not self._shutdown.is_set(): 160 current_time = time.perf_counter() 161 delta = current_time - last_time 162 last_time = current_time 163 164 if not first_frame and process_func: 165 process_func(delta) 166 if main_loop: 167 self._frame += 1 168 self._timestamp += delta 169 else: 170 first_frame = False 171 172 next_time += interval 173 sleep_time = next_time - time.perf_counter() 174 175 if precise_control: 176 if sleep_time > threshold: 177 time.sleep(sleep_time - threshold) 178 179 while time.perf_counter() < next_time: 180 pass 181 182 else: 183 if sleep_time > 0: 184 time.sleep(max(0, sleep_time)) 185 186 if sleep_time <= 0 and main_loop: 187 warning(f"当前帧{self._frame}耗时过长,耗时{delta:.5f}s") 188 189 190 def get_frame(self) -> int: 191 """获取当前帧数""" 192 return self._frame 193 194 def get_timestamp(self) -> float: 195 """获取当前时间戳""" 196 return self._timestamp 197 198 def print_tree(self): 199 """打印节点树""" 200 from .node import Node 201 def print_recursive(node: Node, prefix="", is_last=False, is_root=False): 202 if is_root: 203 print(f"{node}") # 根节点 204 else: 205 if is_last: 206 print(f"{prefix}└── {node}") # 最后一个子节点 207 else: 208 print(f"{prefix}├── {node}") # 其他子节点 209 210 for i, child in enumerate(node.get_children()): 211 is_last_child = (i == len(node.get_children()) - 1) 212 print_recursive(child, prefix + " ", is_last=is_last_child, is_root=False) 213 214 print_recursive(self.root, is_last=False, is_root=True)
引擎类
Engine( root: robotengine.node.Node, frequency: float = 240, input_devices: InputDevice = [])
31 def __init__(self, root: Node, frequency: float=240, input_devices: InputDevice=[]): 32 """ 33 初始化引擎 34 35 参数: 36 37 root (Node): 根节点 38 39 frequency (int, optional): 影响所有节点的 _process 函数的调用频率。默认值为 240。 40 41 input_devices (list, optional): 输入设备列表,当为空时,节点的 _input() 函数将不会被调用。默认值为 []。 42 """ 43 self.root = root 44 """ 根节点 """ 45 self.paused = False 46 """ 是否暂停 """ 47 48 self._frequency = frequency 49 self._frame = 0 50 self._timestamp = 0.0 51 52 self._time_frequency = 30 53 54 self.input = Input() 55 """ 输入类, 在 Engine 初始化完成后,每个 Node 都可以通过 self.input 来访问输入类 """ 56 57 self._initialize() 58 59 self._shutdown = threading.Event() 60 if input_devices: 61 if InputDevice.GAMEPAD in input_devices: 62 self._gamepad_listener = GamepadListener() 63 64 self._input_thread = threading.Thread(target=self._input, daemon=True) 65 self._input_thread.start() 66 67 self._update_thread = threading.Thread(target=self._update, daemon=True) 68 self._update_thread.start() 69 70 self._timer_thread = threading.Thread(target=self._timer, daemon=True) 71 self._timer_thread.start()
初始化引擎
参数:
root (Node): 根节点
frequency (int, optional): 影响所有节点的 _process 函数的调用频率。默认值为 240。
input_devices (list, optional): 输入设备列表,当为空时,节点的 _input() 函数将不会被调用。默认值为 []。
def
run(self):
143 def run(self): 144 """ 开始运行引擎 """ 145 self._run_loop(self._frequency, precise_control=True, process_func=self._process, main_loop=True)
开始运行引擎
def
print_tree(self):
198 def print_tree(self): 199 """打印节点树""" 200 from .node import Node 201 def print_recursive(node: Node, prefix="", is_last=False, is_root=False): 202 if is_root: 203 print(f"{node}") # 根节点 204 else: 205 if is_last: 206 print(f"{prefix}└── {node}") # 最后一个子节点 207 else: 208 print(f"{prefix}├── {node}") # 其他子节点 209 210 for i, child in enumerate(node.get_children()): 211 is_last_child = (i == len(node.get_children()) - 1) 212 print_recursive(child, prefix + " ", is_last=is_last_child, is_root=False) 213 214 print_recursive(self.root, is_last=False, is_root=True)
打印节点树
class
Engine.Node:
49class Node: 50 """ Node 基类 """ 51 from robotengine.input import InputEvent 52 53 def __init__(self, name="Node"): 54 """ 初始化节点, 需要指定节点名称 """ 55 self.name = name 56 """ 节点名称 """ 57 self.owner = None 58 """ 59 节点的所有者 60 61 注意:owner的指定与节点的创建顺序有关,例如: 62 63 A = Node("A") 64 B = Node("B") 65 C = Node("C") 66 D = Node("D") 67 68 A.add_child(B) 69 A.add_child(C) 70 B.add_child(D) 71 72 此时,A的子节点为B、C,B的子节点为D,B、C、D的owner均为A。 73 74 而如果继续添加节点: 75 76 E = Node("E") 77 E.add_child(A) 78 79 此时,E的子节点为A,A的owner为E,但是B、C、D的owner仍然为A。 80 """ 81 self._children = [] 82 self._parent = None 83 84 # 全局属性 85 from robotengine.engine import Engine 86 from robotengine.input import Input 87 88 self.engine: Engine = None 89 """ 节点的 Engine 实例 """ 90 self.input: Input = None 91 """ 节点的 Input 实例 """ 92 93 self.process_mode: ProcessMode = ProcessMode.PAUSABLE 94 """ 节点的process模式 """ 95 96 # 信号 97 self.ready: Signal = Signal() 98 """ 信号,节点 _ready 执行结束后触发 """ 99 100 def add_child(self, child_node): 101 """ 添加子节点 """ 102 if child_node._parent is not None: 103 error(f"{self.name}:{child_node.name} 已经有父节点!") 104 return 105 for child in self._children: 106 if child.name == child_node.name: 107 error(f"节点 {self.name} 已经有同名子节点{child_node.name} !") 108 return 109 110 child_node._parent = self # 设置子节点的 _parent 属性 111 if self.owner is not None: 112 child_node.owner = self.owner 113 else: 114 child_node.owner = self 115 116 self._children.append(child_node) 117 118 def remove_child(self, child_node): 119 """ 移除子节点 """ 120 if child_node in self._children: 121 self._children.remove(child_node) 122 child_node._parent = None # 解除 _parent 绑定 123 else: 124 warning(f"{self.name}:{child_node.name} 并未被找到,未执行移除操作") 125 126 def _update(self, delta) -> None: 127 """ 引擎内部的节点更新函数,会以很低的频率调用 """ 128 pass 129 130 def _timer(self, delta) -> None: 131 """ 引擎内部的定时器更新函数,负责 Timer 相关的更新 """ 132 pass 133 134 def _init(self) -> None: 135 """ 初始化节点,会在 _ready() 之前被调用,尽量不要覆写此函数 """ 136 pass 137 138 def _ready(self) -> None: 139 """ 节点 _ready 函数,会在 _init() 之后被调用,可以在此函数中执行一些初始化操作 """ 140 pass 141 142 def _ready_execute(self) -> None: 143 self._ready() 144 self.ready.emit() 145 146 def _process(self, delta) -> None: 147 """ 节点 process 函数,会根据 Engine 中设置的 frequency 进行连续调用 """ 148 pass 149 150 def _input(self, event: InputEvent) -> None: 151 """ 节点 input 函数,会在接收到输入事件时被调用 """ 152 pass 153 154 def get_child(self, name) -> "Node": 155 """ 通过节点名称获取子节点 """ 156 for child in self._children: 157 if child.name == name: 158 return child 159 return None 160 161 def get_children(self) -> List["Node"]: 162 """ 获取所有子节点 """ 163 return self._children 164 165 def get_parent(self) -> "Node": 166 """ 获取父节点 """ 167 return self._parent 168 169 def rbprint(self, str, end="\n"): 170 print(f"[{self.engine.get_frame()}] {str}", end=end) 171 172 def __repr__(self): 173 return f"{self.name}"
Node 基类