427 lines
15 KiB
Python
427 lines
15 KiB
Python
"""
|
|
行为树叶子节点
|
|
包括条件节点、动作节点、等待节点等
|
|
提供多种叶子节点实现
|
|
"""
|
|
|
|
import time
|
|
import random
|
|
import math
|
|
from typing import Optional, Callable, Any, Union
|
|
from ..core.behavior_tree import BTNode, NodeStatus, BTNodeConfig
|
|
|
|
class ConditionNode(BTNode):
|
|
"""
|
|
条件节点
|
|
检查条件是否满足
|
|
支持多种条件检查方式和缓存机制
|
|
"""
|
|
|
|
def __init__(self, config: Union[BTNodeConfig, str] = "Condition",
|
|
condition_func: Optional[Callable[[Any], bool]] = None,
|
|
cache_result: bool = False, # 是否缓存结果
|
|
cache_duration: float = 0.0): # 缓存持续时间(秒)
|
|
super().__init__(config)
|
|
self.condition_func = condition_func
|
|
self.cache_result = cache_result
|
|
self.cache_duration = cache_duration
|
|
self.cached_result: Optional[bool] = None
|
|
self.cache_time: float = 0.0
|
|
|
|
def execute(self, blackboard) -> NodeStatus:
|
|
"""执行条件检查"""
|
|
current_time = time.time()
|
|
|
|
# 检查缓存
|
|
if (self.cache_result and
|
|
self.cached_result is not None and
|
|
(self.cache_duration <= 0 or
|
|
current_time - self.cache_time < self.cache_duration)):
|
|
# 使用缓存结果
|
|
return NodeStatus.SUCCESS if self.cached_result else NodeStatus.FAILURE
|
|
|
|
# 执行条件检查
|
|
result = self._evaluate_condition(blackboard)
|
|
|
|
# 更新缓存
|
|
if self.cache_result:
|
|
self.cached_result = result
|
|
self.cache_time = current_time
|
|
|
|
return NodeStatus.SUCCESS if result else NodeStatus.FAILURE
|
|
|
|
def _evaluate_condition(self, blackboard) -> bool:
|
|
"""评估条件"""
|
|
if self.condition_func:
|
|
try:
|
|
return bool(self.condition_func(blackboard))
|
|
except Exception as e:
|
|
print(f"条件节点执行出错: {e}")
|
|
return False
|
|
else:
|
|
# 如果没有提供条件函数,默认返回成功
|
|
return True
|
|
|
|
def invalidate_cache(self) -> None:
|
|
"""使缓存失效"""
|
|
self.cached_result = None
|
|
self.cache_time = 0.0
|
|
|
|
def reset(self) -> None:
|
|
"""重置节点状态"""
|
|
super().reset()
|
|
self.invalidate_cache()
|
|
|
|
class ActionNode(BTNode):
|
|
"""
|
|
动作节点
|
|
执行具体行为
|
|
支持异步执行和进度跟踪
|
|
"""
|
|
|
|
def __init__(self, config: Union[BTNodeConfig, str] = "Action",
|
|
action_func: Optional[Callable[[Any], Any]] = None,
|
|
async_action: bool = False, # 是否异步执行
|
|
progress_callback: Optional[Callable[[float], None]] = None): # 进度回调
|
|
super().__init__(config)
|
|
self.action_func = action_func
|
|
self.async_action = async_action
|
|
self.progress_callback = progress_callback
|
|
self.is_running = False
|
|
self.start_time = 0.0
|
|
self.estimated_duration = 0.0 # 估计执行时间
|
|
|
|
def execute(self, blackboard) -> NodeStatus:
|
|
"""执行动作"""
|
|
current_time = time.time()
|
|
|
|
# 如果是异步动作且正在运行
|
|
if self.async_action and self.is_running:
|
|
# 检查动作是否完成
|
|
if self._is_async_action_complete(blackboard):
|
|
self.is_running = False
|
|
self.start_time = 0.0
|
|
return NodeStatus.SUCCESS
|
|
else:
|
|
# 更新进度
|
|
if self.progress_callback:
|
|
progress = self._calculate_progress(current_time)
|
|
try:
|
|
self.progress_callback(progress)
|
|
except Exception as e:
|
|
print(f"进度回调执行出错: {e}")
|
|
return NodeStatus.RUNNING
|
|
|
|
# 执行动作
|
|
if self.action_func:
|
|
try:
|
|
# 记录开始时间
|
|
self.start_time = current_time
|
|
|
|
if self.async_action:
|
|
# 异步执行
|
|
result = self._start_async_action(blackboard)
|
|
if result == NodeStatus.RUNNING:
|
|
self.is_running = True
|
|
return NodeStatus.RUNNING
|
|
else:
|
|
self.start_time = 0.0
|
|
return result
|
|
else:
|
|
# 同步执行
|
|
result = self.action_func(blackboard)
|
|
self.start_time = 0.0
|
|
|
|
if result is True:
|
|
return NodeStatus.SUCCESS
|
|
elif result is False:
|
|
return NodeStatus.FAILURE
|
|
elif result == NodeStatus.RUNNING:
|
|
return NodeStatus.RUNNING
|
|
else:
|
|
# 默认返回成功
|
|
return NodeStatus.SUCCESS
|
|
|
|
except Exception as e:
|
|
print(f"动作节点执行出错: {e}")
|
|
self.is_running = False
|
|
self.start_time = 0.0
|
|
return NodeStatus.FAILURE
|
|
else:
|
|
# 如果没有提供动作函数,默认返回成功
|
|
return NodeStatus.SUCCESS
|
|
|
|
def _start_async_action(self, blackboard) -> NodeStatus:
|
|
"""启动异步动作"""
|
|
# 这里应该启动异步操作,例如创建线程或任务
|
|
# 为了简化,我们假设异步动作立即开始并需要继续运行
|
|
return NodeStatus.RUNNING
|
|
|
|
def _is_async_action_complete(self, blackboard) -> bool:
|
|
"""检查异步动作是否完成"""
|
|
# 这里应该检查异步操作是否完成
|
|
# 为了简化,我们随机决定是否完成
|
|
if self.estimated_duration > 0:
|
|
current_time = time.time()
|
|
elapsed = current_time - self.start_time
|
|
return elapsed >= self.estimated_duration
|
|
else:
|
|
# 随机完成(用于演示)
|
|
return random.random() < 0.1 # 10%概率完成
|
|
|
|
def _calculate_progress(self, current_time: float) -> float:
|
|
"""计算进度"""
|
|
if self.estimated_duration > 0:
|
|
elapsed = current_time - self.start_time
|
|
progress = min(1.0, elapsed / self.estimated_duration)
|
|
return max(0.0, progress)
|
|
else:
|
|
return 0.0
|
|
|
|
def reset(self) -> None:
|
|
"""重置节点状态"""
|
|
super().reset()
|
|
self.is_running = False
|
|
self.start_time = 0.0
|
|
|
|
class WaitNode(ActionNode):
|
|
"""
|
|
等待节点
|
|
等待指定时间后返回成功
|
|
支持随机等待时间和进度回调
|
|
"""
|
|
|
|
def __init__(self, config: Union[BTNodeConfig, str] = "Wait",
|
|
duration: float = 1.0,
|
|
random_variance: float = 0.0, # 随机变化范围(秒)
|
|
progress_callback: Optional[Callable[[float], None]] = None):
|
|
super().__init__(config, None, False, progress_callback)
|
|
self.base_duration = duration
|
|
self.random_variance = random_variance
|
|
self.actual_duration = duration # 实际等待时间
|
|
self.start_time = 0.0
|
|
|
|
def execute(self, blackboard) -> NodeStatus:
|
|
"""执行等待"""
|
|
current_time = time.time()
|
|
|
|
# 如果是第一次执行,计算实际等待时间
|
|
if self.start_time == 0.0:
|
|
self.start_time = current_time
|
|
# 添加随机变化
|
|
if self.random_variance > 0:
|
|
variance = random.uniform(-self.random_variance, self.random_variance)
|
|
self.actual_duration = max(0.0, self.base_duration + variance)
|
|
else:
|
|
self.actual_duration = self.base_duration
|
|
|
|
# 检查是否等待完成
|
|
elapsed = current_time - self.start_time
|
|
if elapsed >= self.actual_duration:
|
|
# 等待完成,重置开始时间
|
|
self.start_time = 0.0
|
|
return NodeStatus.SUCCESS
|
|
else:
|
|
# 更新进度
|
|
if self.progress_callback:
|
|
progress = min(1.0, elapsed / self.actual_duration)
|
|
try:
|
|
self.progress_callback(progress)
|
|
except Exception as e:
|
|
print(f"进度回调执行出错: {e}")
|
|
return NodeStatus.RUNNING
|
|
|
|
def reset(self) -> None:
|
|
"""重置节点状态"""
|
|
super().reset()
|
|
self.start_time = 0.0
|
|
self.actual_duration = self.base_duration
|
|
|
|
class SubtreeNode(BTNode):
|
|
"""
|
|
子树节点
|
|
执行另一个行为树作为子树
|
|
"""
|
|
|
|
def __init__(self, config: Union[BTNodeConfig, str] = "Subtree",
|
|
subtree: Optional['BehaviorTree'] = None):
|
|
super().__init__(config)
|
|
self.subtree = subtree
|
|
self.is_running = False
|
|
|
|
def execute(self, blackboard) -> NodeStatus:
|
|
"""执行子树"""
|
|
if not self.subtree:
|
|
return NodeStatus.FAILURE
|
|
|
|
# 设置子树的黑板
|
|
self.subtree.set_blackboard(blackboard)
|
|
|
|
# 执行子树
|
|
try:
|
|
result = self.subtree.run()
|
|
return result
|
|
except Exception as e:
|
|
print(f"子树执行出错: {e}")
|
|
return NodeStatus.FAILURE
|
|
|
|
def set_subtree(self, subtree: 'BehaviorTree') -> None:
|
|
"""设置子树"""
|
|
self.subtree = subtree
|
|
|
|
def get_subtree(self) -> Optional['BehaviorTree']:
|
|
"""获取子树"""
|
|
return self.subtree
|
|
|
|
class ProbabilityNode(ActionNode):
|
|
"""
|
|
概率节点
|
|
根据概率决定执行结果
|
|
"""
|
|
|
|
def __init__(self, config: Union[BTNodeConfig, str] = "Probability",
|
|
success_probability: float = 0.5, # 成功概率
|
|
action_func: Optional[Callable[[Any], Any]] = None):
|
|
super().__init__(config, action_func)
|
|
self.success_probability = max(0.0, min(1.0, success_probability))
|
|
self.has_executed = False
|
|
self.result = NodeStatus.FAILURE
|
|
|
|
def execute(self, blackboard) -> NodeStatus:
|
|
"""执行概率动作"""
|
|
# 如果已经执行过,返回之前的结果
|
|
if self.has_executed:
|
|
return self.result
|
|
|
|
# 执行实际动作(如果提供了)
|
|
if self.action_func:
|
|
try:
|
|
action_result = self.action_func(blackboard)
|
|
# 如果动作返回了具体状态,使用该状态
|
|
if action_result == NodeStatus.SUCCESS:
|
|
self.result = NodeStatus.SUCCESS
|
|
elif action_result == NodeStatus.FAILURE:
|
|
self.result = NodeStatus.FAILURE
|
|
elif action_result == NodeStatus.RUNNING:
|
|
return NodeStatus.RUNNING
|
|
except Exception as e:
|
|
print(f"概率节点动作执行出错: {e}")
|
|
self.result = NodeStatus.FAILURE
|
|
else:
|
|
# 根据概率决定结果
|
|
if random.random() < self.success_probability:
|
|
self.result = NodeStatus.SUCCESS
|
|
else:
|
|
self.result = NodeStatus.FAILURE
|
|
|
|
self.has_executed = True
|
|
return self.result
|
|
|
|
def reset(self) -> None:
|
|
"""重置节点状态"""
|
|
super().reset()
|
|
self.has_executed = False
|
|
self.result = NodeStatus.FAILURE
|
|
|
|
class CounterNode(ConditionNode):
|
|
"""
|
|
计数器节点
|
|
根据执行次数决定结果
|
|
"""
|
|
|
|
def __init__(self, config: Union[BTNodeConfig, str] = "Counter",
|
|
target_count: int = 1, # 目标计数
|
|
reset_on_target: bool = True, # 达到目标后是否重置
|
|
condition_func: Optional[Callable[[Any], bool]] = None):
|
|
super().__init__(config, condition_func)
|
|
self.target_count = max(1, target_count)
|
|
self.reset_on_target = reset_on_target
|
|
self.current_count = 0
|
|
|
|
def _evaluate_condition(self, blackboard) -> bool:
|
|
"""评估条件"""
|
|
# 首先检查提供的条件函数
|
|
if self.condition_func:
|
|
try:
|
|
if not self.condition_func(blackboard):
|
|
return False
|
|
except Exception as e:
|
|
print(f"计数器节点条件检查出错: {e}")
|
|
return False
|
|
|
|
# 增加计数
|
|
self.current_count += 1
|
|
|
|
# 检查是否达到目标计数
|
|
result = self.current_count >= self.target_count
|
|
|
|
# 如果达到目标且需要重置,重置计数器
|
|
if result and self.reset_on_target:
|
|
self.current_count = 0
|
|
|
|
return result
|
|
|
|
def reset(self) -> None:
|
|
"""重置节点状态"""
|
|
super().reset()
|
|
self.current_count = 0
|
|
|
|
class TimeoutNode(ActionNode):
|
|
"""
|
|
超时节点
|
|
限制动作执行时间
|
|
"""
|
|
|
|
def __init__(self, config: Union[BTNodeConfig, str] = "Timeout",
|
|
action_func: Optional[Callable[[Any], Any]] = None,
|
|
timeout: float = 5.0): # 超时时间(秒)
|
|
super().__init__(config, action_func)
|
|
self.timeout = timeout
|
|
self.start_time = 0.0
|
|
|
|
def execute(self, blackboard) -> NodeStatus:
|
|
"""执行带超时的动作"""
|
|
current_time = time.time()
|
|
|
|
# 记录开始时间
|
|
if self.start_time == 0.0:
|
|
self.start_time = current_time
|
|
|
|
# 检查是否超时
|
|
if current_time - self.start_time >= self.timeout:
|
|
# 超时,返回失败
|
|
self.start_time = 0.0
|
|
return NodeStatus.FAILURE
|
|
|
|
# 执行动作
|
|
if self.action_func:
|
|
try:
|
|
result = self.action_func(blackboard)
|
|
|
|
# 如果动作完成,重置开始时间
|
|
if result != NodeStatus.RUNNING:
|
|
self.start_time = 0.0
|
|
|
|
if result is True:
|
|
return NodeStatus.SUCCESS
|
|
elif result is False:
|
|
return NodeStatus.FAILURE
|
|
elif result == NodeStatus.RUNNING:
|
|
return NodeStatus.RUNNING
|
|
else:
|
|
# 默认返回成功
|
|
self.start_time = 0.0
|
|
return NodeStatus.SUCCESS
|
|
except Exception as e:
|
|
print(f"超时节点动作执行出错: {e}")
|
|
self.start_time = 0.0
|
|
return NodeStatus.FAILURE
|
|
else:
|
|
# 如果没有提供动作函数,默认返回成功
|
|
self.start_time = 0.0
|
|
return NodeStatus.SUCCESS
|
|
|
|
def reset(self) -> None:
|
|
"""重置节点状态"""
|
|
super().reset()
|
|
self.start_time = 0.0 |