656 lines
23 KiB
Python
656 lines
23 KiB
Python
"""
|
||
Boids算法实现
|
||
这是一个经过优化和扩展的Boids算法实现,包含空间分区优化和高级行为规则
|
||
"""
|
||
|
||
from panda3d.core import Vec3, Point3
|
||
import random
|
||
import math
|
||
from typing import List, Dict, Tuple, Optional
|
||
|
||
class SpatialPartition:
|
||
"""
|
||
空间分区类,用于优化邻居查找算法
|
||
将3D空间划分为网格,每个网格格子存储位于其中的群体成员
|
||
"""
|
||
|
||
def __init__(self, bounds: Dict[str, float], cell_size: float):
|
||
"""
|
||
初始化空间分区
|
||
|
||
:param bounds: 边界信息 {'min_x': float, 'max_x': float, 'min_y': float, 'max_y': float, 'min_z': float, 'max_z': float}
|
||
:param cell_size: 网格格子大小
|
||
"""
|
||
self.bounds = bounds
|
||
self.cell_size = cell_size
|
||
|
||
# 计算网格尺寸
|
||
self.grid_width = int((bounds['max_x'] - bounds['min_x']) / cell_size) + 1
|
||
self.grid_height = int((bounds['max_y'] - bounds['min_y']) / cell_size) + 1
|
||
self.grid_depth = int((bounds['max_z'] - bounds['min_z']) / cell_size) + 1
|
||
|
||
# 创建网格
|
||
self.grid = {}
|
||
|
||
# 邻居格子偏移量
|
||
self.neighbor_offsets = [
|
||
(-1, -1, -1), (-1, -1, 0), (-1, -1, 1),
|
||
(-1, 0, -1), (-1, 0, 0), (-1, 0, 1),
|
||
(-1, 1, -1), (-1, 1, 0), (-1, 1, 1),
|
||
(0, -1, -1), (0, -1, 0), (0, -1, 1),
|
||
(0, 0, -1), (0, 0, 0), (0, 0, 1),
|
||
(0, 1, -1), (0, 1, 0), (0, 1, 1),
|
||
(1, -1, -1), (1, -1, 0), (1, -1, 1),
|
||
(1, 0, -1), (1, 0, 0), (1, 0, 1),
|
||
(1, 1, -1), (1, 1, 0), (1, 1, 1)
|
||
]
|
||
|
||
def get_grid_coords(self, position: Vec3) -> Tuple[int, int, int]:
|
||
"""
|
||
获取位置对应的网格坐标
|
||
|
||
:param position: 3D位置
|
||
:return: (x, y, z) 网格坐标
|
||
"""
|
||
grid_x = int((position.x - self.bounds['min_x']) / self.cell_size)
|
||
grid_y = int((position.y - self.bounds['min_y']) / self.cell_size)
|
||
grid_z = int((position.z - self.bounds['min_z']) / self.cell_size)
|
||
|
||
# 确保坐标在有效范围内
|
||
grid_x = max(0, min(grid_x, self.grid_width - 1))
|
||
grid_y = max(0, min(grid_y, self.grid_height - 1))
|
||
grid_z = max(0, min(grid_z, self.grid_depth - 1))
|
||
|
||
return (grid_x, grid_y, grid_z)
|
||
|
||
def add_member(self, member: Dict, position: Vec3):
|
||
"""
|
||
将成员添加到网格中
|
||
|
||
:param member: 成员对象
|
||
:param position: 成员位置
|
||
"""
|
||
grid_coords = self.get_grid_coords(position)
|
||
grid_key = f"{grid_coords[0]}_{grid_coords[1]}_{grid_coords[2]}"
|
||
|
||
if grid_key not in self.grid:
|
||
self.grid[grid_key] = []
|
||
self.grid[grid_key].append(member)
|
||
|
||
def remove_member(self, member: Dict, position: Vec3):
|
||
"""
|
||
从网格中移除成员
|
||
|
||
:param member: 成员对象
|
||
:param position: 成员位置
|
||
"""
|
||
grid_coords = self.get_grid_coords(position)
|
||
grid_key = f"{grid_coords[0]}_{grid_coords[1]}_{grid_coords[2]}"
|
||
|
||
if grid_key in self.grid:
|
||
if member in self.grid[grid_key]:
|
||
self.grid[grid_key].remove(member)
|
||
|
||
def update_member(self, member: Dict, old_position: Vec3, new_position: Vec3):
|
||
"""
|
||
更新成员在网格中的位置
|
||
|
||
:param member: 成员对象
|
||
:param old_position: 原位置
|
||
:param new_position: 新位置
|
||
"""
|
||
# 从旧位置移除
|
||
self.remove_member(member, old_position)
|
||
# 添加到新位置
|
||
self.add_member(member, new_position)
|
||
|
||
def get_neighbors(self, position: Vec3, radius: float) -> List[Dict]:
|
||
"""
|
||
获取指定位置周围的邻居
|
||
|
||
:param position: 中心位置
|
||
:param radius: 搜索半径
|
||
:return: 邻居列表
|
||
"""
|
||
neighbors = []
|
||
|
||
# 计算需要检查的网格格子范围
|
||
cells_to_check = int(radius / self.cell_size) + 1
|
||
|
||
center_coords = self.get_grid_coords(position)
|
||
|
||
# 检查中心格子及其邻居
|
||
for offset in self.neighbor_offsets:
|
||
grid_x = center_coords[0] + offset[0]
|
||
grid_y = center_coords[1] + offset[1]
|
||
grid_z = center_coords[2] + offset[2]
|
||
|
||
# 检查格子是否在有效范围内
|
||
if (0 <= grid_x < self.grid_width and
|
||
0 <= grid_y < self.grid_height and
|
||
0 <= grid_z < self.grid_depth):
|
||
|
||
grid_key = f"{grid_x}_{grid_y}_{grid_z}"
|
||
if grid_key in self.grid:
|
||
# 检查格子中的每个成员是否在半径范围内
|
||
for member in self.grid[grid_key]:
|
||
distance = (position - member['position']).length()
|
||
if distance <= radius and member not in neighbors:
|
||
neighbors.append(member)
|
||
|
||
return neighbors
|
||
|
||
def clear(self):
|
||
"""
|
||
清空网格
|
||
"""
|
||
self.grid.clear()
|
||
|
||
class PhysicsSimulator:
|
||
"""
|
||
物理模拟器,提供更真实的物理行为
|
||
"""
|
||
|
||
def __init__(self):
|
||
self.gravity = Vec3(0, 0, -9.81) # 重力加速度
|
||
self.air_resistance = 0.01 # 空气阻力系数
|
||
self.fluid_density = 1.225 # 空气密度 (kg/m^3)
|
||
|
||
def apply_gravity(self, member: Dict, dt: float):
|
||
"""
|
||
应用重力
|
||
"""
|
||
if 'mass' in member:
|
||
mass = member['mass']
|
||
else:
|
||
mass = 1.0 # 默认质量
|
||
|
||
gravity_force = self.gravity * mass
|
||
member['velocity'] += gravity_force * dt / mass
|
||
|
||
def apply_air_resistance(self, member: Dict, dt: float):
|
||
"""
|
||
应用空气阻力
|
||
"""
|
||
velocity = member['velocity']
|
||
speed = velocity.length()
|
||
|
||
if speed > 0:
|
||
# 空气阻力与速度平方成正比
|
||
drag_force_magnitude = 0.5 * self.fluid_density * speed * speed * 0.01 # 简化的阻力系数
|
||
drag_force = -velocity.normalized() * drag_force_magnitude
|
||
|
||
# 应用阻力
|
||
member['velocity'] += drag_force * dt
|
||
|
||
def apply_buoyancy(self, member: Dict, fluid_level: float, dt: float):
|
||
"""
|
||
应用浮力(适用于模拟水中生物)
|
||
"""
|
||
if member['position'].z < fluid_level:
|
||
# 计算浮力(简化模型)
|
||
buoyancy_force = Vec3(0, 0, 0.5) # 向上的浮力
|
||
member['velocity'] += buoyancy_force * dt
|
||
|
||
def update_physics(self, member: Dict, dt: float = 1.0/60.0):
|
||
"""
|
||
更新物理状态
|
||
"""
|
||
# 应用重力
|
||
self.apply_gravity(member, dt)
|
||
|
||
# 应用空气阻力
|
||
self.apply_air_resistance(member, dt)
|
||
|
||
class AdvancedBoidsAlgorithm:
|
||
"""
|
||
高级Boids算法实现类
|
||
包含空间分区优化、物理模拟和高级行为规则
|
||
"""
|
||
|
||
def __init__(self, config):
|
||
self.config = config
|
||
|
||
# 初始化空间分区系统
|
||
bounds = {
|
||
'min_x': -100, 'max_x': 100,
|
||
'min_y': -100, 'max_y': 100,
|
||
'min_z': -10, 'max_z': 100
|
||
}
|
||
cell_size = self.config.get('perception_radius', 10.0) # 使用感知半径作为网格大小
|
||
self.spatial_partition = SpatialPartition(bounds, cell_size)
|
||
|
||
# 初始化物理模拟器
|
||
self.physics_simulator = PhysicsSimulator()
|
||
|
||
# 用于记录性能统计
|
||
self.stats = {
|
||
'neighbor_lookups': 0,
|
||
'total_neighbors_found': 0,
|
||
'algorithm_calls': 0
|
||
}
|
||
|
||
def calculate_cohesion(self, member, neighbors):
|
||
"""
|
||
计算聚集力
|
||
个体向邻居群集中心移动
|
||
"""
|
||
if not neighbors:
|
||
return Vec3(0, 0, 0)
|
||
|
||
# 计算邻居的中心位置
|
||
center = Vec3(0, 0, 0)
|
||
for neighbor in neighbors:
|
||
center += neighbor['position']
|
||
center /= len(neighbors)
|
||
|
||
# 返回指向中心的向量
|
||
return (center - member['position']).normalized()
|
||
|
||
def calculate_separation(self, member, neighbors):
|
||
"""
|
||
计算分离力
|
||
个体避免与邻居过于靠近
|
||
"""
|
||
if not neighbors:
|
||
return Vec3(0, 0, 0)
|
||
|
||
separation = Vec3(0, 0, 0)
|
||
for neighbor in neighbors:
|
||
diff = member['position'] - neighbor['position']
|
||
distance = diff.length()
|
||
if distance > 0: # 避免除零
|
||
# 距离越近,分离力越大
|
||
separation += diff.normalized() / distance
|
||
|
||
if len(neighbors) > 0:
|
||
separation /= len(neighbors)
|
||
|
||
return separation.normalized() if separation.length() > 0 else separation
|
||
|
||
def calculate_alignment(self, member, neighbors):
|
||
"""
|
||
计算对齐力
|
||
个体与邻居的移动方向保持一致
|
||
"""
|
||
if not neighbors:
|
||
return Vec3(0, 0, 0)
|
||
|
||
# 计算邻居的平均速度方向
|
||
avg_velocity = Vec3(0, 0, 0)
|
||
for neighbor in neighbors:
|
||
avg_velocity += neighbor['velocity']
|
||
avg_velocity /= len(neighbors)
|
||
|
||
return avg_velocity.normalized()
|
||
|
||
def calculate_obstacle_avoidance(self, member, obstacles):
|
||
"""
|
||
计算避障力
|
||
避开场景中的障碍物
|
||
"""
|
||
if not obstacles:
|
||
return Vec3(0, 0, 0)
|
||
|
||
avoidance = Vec3(0, 0, 0)
|
||
for obstacle in obstacles:
|
||
diff = member['position'] - obstacle['position']
|
||
distance = diff.length()
|
||
if distance < obstacle['radius']:
|
||
avoidance += diff.normalized() / distance
|
||
|
||
return avoidance.normalized() if avoidance.length() > 0 else avoidance
|
||
|
||
def calculate_seek(self, member, target):
|
||
"""
|
||
计算寻求力
|
||
向目标位置移动
|
||
"""
|
||
desired = target - member['position']
|
||
distance = desired.length()
|
||
|
||
if distance > 0:
|
||
desired.normalize()
|
||
# 如果启用了达到行为,当接近目标时减速
|
||
if self.config.get("arrival_enabled", False):
|
||
slow_radius = self.config.get("arrival_slow_radius", 5.0)
|
||
if distance < slow_radius:
|
||
desired *= self.config.get("max_speed", 5.0) * (distance / slow_radius)
|
||
else:
|
||
desired *= self.config.get("max_speed", 5.0)
|
||
else:
|
||
desired *= self.config.get("max_speed", 5.0)
|
||
|
||
# 计算转向力
|
||
steer = desired - member['velocity']
|
||
# 限制转向力
|
||
max_force = self.config.get("max_force", 0.5)
|
||
if steer.length() > max_force:
|
||
steer.normalize()
|
||
steer *= max_force
|
||
|
||
return steer
|
||
return Vec3(0, 0, 0)
|
||
|
||
def calculate_flee(self, member, target):
|
||
"""
|
||
计算逃离力
|
||
远离目标位置
|
||
"""
|
||
desired = member['position'] - target
|
||
distance = desired.length()
|
||
|
||
if distance > 0:
|
||
desired.normalize()
|
||
desired *= self.config.get("max_speed", 5.0)
|
||
|
||
# 计算转向力
|
||
steer = desired - member['velocity']
|
||
# 限制转向力
|
||
max_force = self.config.get("max_force", 0.5)
|
||
if steer.length() > max_force:
|
||
steer.normalize()
|
||
steer *= max_force
|
||
|
||
return steer
|
||
return Vec3(0, 0, 0)
|
||
|
||
def calculate_wander(self, member):
|
||
"""
|
||
计算游走力
|
||
产生自然的随机移动
|
||
"""
|
||
# 获取游走参数
|
||
wander_radius = self.config.get("wander_radius", 5.0)
|
||
wander_distance = self.config.get("wander_distance", 10.0)
|
||
wander_jitter = self.config.get("wander_jitter", 1.0)
|
||
|
||
# 如果成员没有游走目标,创建一个
|
||
if 'wander_target' not in member:
|
||
member['wander_target'] = Vec3(
|
||
random.uniform(-1, 1),
|
||
random.uniform(-1, 1),
|
||
random.uniform(-1, 1)
|
||
).normalized() * wander_radius
|
||
|
||
# 添加随机扰动
|
||
jitter = Vec3(
|
||
random.uniform(-1, 1),
|
||
random.uniform(-1, 1),
|
||
random.uniform(-1, 1)
|
||
).normalized() * wander_jitter
|
||
|
||
member['wander_target'] += jitter
|
||
member['wander_target'].normalize()
|
||
member['wander_target'] *= wander_radius
|
||
|
||
# 计算游走目标在前方的距离
|
||
target = member['wander_target'] + Vec3(0, 0, 0) # 相对于成员前方的点
|
||
target *= wander_distance
|
||
|
||
# 转换到世界坐标系
|
||
# 这里简化处理,实际应用中需要考虑成员的朝向
|
||
desired = target - member['position']
|
||
if desired.length() > 0:
|
||
desired.normalize()
|
||
desired *= self.config.get("max_speed", 5.0)
|
||
|
||
# 计算转向力
|
||
steer = desired - member['velocity']
|
||
# 限制转向力
|
||
max_force = self.config.get("max_force", 0.5)
|
||
if steer.length() > max_force:
|
||
steer.normalize()
|
||
steer *= max_force
|
||
|
||
return steer
|
||
return Vec3(0, 0, 0)
|
||
|
||
def calculate_boundaries(self, member, bounds):
|
||
"""
|
||
计算边界力
|
||
将成员保持在指定区域内
|
||
"""
|
||
if not bounds:
|
||
return Vec3(0, 0, 0)
|
||
|
||
# 获取边界参数
|
||
min_x, max_x = bounds['min_x'], bounds['max_x']
|
||
min_y, max_y = bounds['min_y'], bounds['max_y']
|
||
min_z, max_z = bounds['min_z'], bounds['max_z']
|
||
|
||
# 边界缓冲距离
|
||
buffer = self.config.get("boundary_buffer", 5.0)
|
||
|
||
# 计算边界力
|
||
bound_force = Vec3(0, 0, 0)
|
||
|
||
if member['position'].x < min_x + buffer:
|
||
bound_force.x = 1.0
|
||
elif member['position'].x > max_x - buffer:
|
||
bound_force.x = -1.0
|
||
|
||
if member['position'].y < min_y + buffer:
|
||
bound_force.y = 1.0
|
||
elif member['position'].y > max_y - buffer:
|
||
bound_force.y = -1.0
|
||
|
||
if member['position'].z < min_z + buffer:
|
||
bound_force.z = 1.0
|
||
elif member['position'].z > max_z - buffer:
|
||
bound_force.z = -1.0
|
||
|
||
# 应用边界权重
|
||
bound_force *= self.config.get("boundary_weight", 2.0)
|
||
|
||
return bound_force
|
||
|
||
def calculate_follow_path(self, member, path):
|
||
"""
|
||
计算路径跟随力
|
||
沿着预定义路径移动
|
||
"""
|
||
if not path or len(path) < 2:
|
||
return Vec3(0, 0, 0)
|
||
|
||
# 获取路径参数
|
||
path_radius = self.config.get("path_radius", 3.0)
|
||
|
||
# 找到最近的路径点
|
||
nearest_index = 0
|
||
nearest_distance = float('inf')
|
||
|
||
for i, point in enumerate(path):
|
||
distance = (member['position'] - point).length()
|
||
if distance < nearest_distance:
|
||
nearest_distance = distance
|
||
nearest_index = i
|
||
|
||
# 预测位置
|
||
prediction = member['velocity'].normalized()
|
||
prediction *= self.config.get("prediction_distance", 10.0)
|
||
predict_pos = member['position'] + prediction
|
||
|
||
# 找到预测位置最近的路径点
|
||
predict_nearest_index = 0
|
||
predict_nearest_distance = float('inf')
|
||
|
||
for i, point in enumerate(path):
|
||
distance = (predict_pos - point).length()
|
||
if distance < predict_nearest_distance:
|
||
predict_nearest_distance = distance
|
||
predict_nearest_index = i
|
||
|
||
# 如果偏离路径太远,直接寻求最近的路径点
|
||
if nearest_distance > path_radius:
|
||
target = path[nearest_index]
|
||
return self.calculate_seek(member, target)
|
||
|
||
# 否则寻求预测位置前方的路径点
|
||
if predict_nearest_index < len(path) - 1:
|
||
target = path[predict_nearest_index + 1]
|
||
return self.calculate_seek(member, target)
|
||
|
||
return Vec3(0, 0, 0)
|
||
|
||
def calculate_flock_formation(self, member, neighbors, formation_type):
|
||
"""
|
||
计算队形保持力
|
||
维持特定的群体队形
|
||
"""
|
||
if not neighbors:
|
||
return Vec3(0, 0, 0)
|
||
|
||
# 根据队形类型计算期望位置
|
||
target_position = Vec3(0, 0, 0)
|
||
|
||
if formation_type == "V":
|
||
# V字形队形
|
||
# 简化实现,实际应用中需要更复杂的计算
|
||
if 'index' in member:
|
||
index = member['index']
|
||
angle = math.radians(index * 15) # 每个成员间隔15度
|
||
distance = 5.0 # 固定间距
|
||
target_position = Vec3(
|
||
math.cos(angle) * distance,
|
||
math.sin(angle) * distance,
|
||
0
|
||
)
|
||
elif formation_type == "line":
|
||
# 直线队形
|
||
if 'index' in member:
|
||
index = member['index']
|
||
spacing = 3.0
|
||
target_position = Vec3(0, index * spacing, 0)
|
||
elif formation_type == "circle":
|
||
# 圆形队形
|
||
if 'index' in member:
|
||
index = member['index']
|
||
total = len(neighbors) + 1
|
||
angle = 2 * math.pi * index / total
|
||
radius = 10.0
|
||
target_position = Vec3(
|
||
math.cos(angle) * radius,
|
||
math.sin(angle) * radius,
|
||
0
|
||
)
|
||
|
||
# 计算到达期望位置的力
|
||
if target_position.length() > 0:
|
||
desired = target_position - member['position']
|
||
if desired.length() > 0:
|
||
desired.normalize()
|
||
desired *= self.config.get("max_speed", 5.0)
|
||
|
||
# 计算转向力
|
||
steer = desired - member['velocity']
|
||
# 应用队形权重
|
||
steer *= self.config.get("formation_weight", 1.0)
|
||
# 限制转向力
|
||
max_force = self.config.get("max_force", 0.5)
|
||
if steer.length() > max_force:
|
||
steer.normalize()
|
||
steer *= max_force
|
||
|
||
return steer
|
||
return Vec3(0, 0, 0)
|
||
|
||
def calculate_avoid_predator(self, member, predators):
|
||
"""
|
||
计算躲避捕食者力
|
||
远离捕食者
|
||
"""
|
||
if not predators:
|
||
return Vec3(0, 0, 0)
|
||
|
||
avoidance = Vec3(0, 0, 0)
|
||
predator_radius = self.config.get("predator_radius", 15.0)
|
||
|
||
for predator in predators:
|
||
diff = member['position'] - predator['position']
|
||
distance = diff.length()
|
||
if distance < predator_radius:
|
||
# 距离越近,躲避力越大
|
||
force = diff.normalized() / (distance / predator_radius)
|
||
avoidance += force
|
||
|
||
# 应用捕食者躲避权重
|
||
avoidance *= self.config.get("predator_avoid_weight", 3.0)
|
||
|
||
return avoidance.normalized() if avoidance.length() > 0 else avoidance
|
||
|
||
def update_member(self, member, neighbors, obstacles=[], target=None, bounds=None, path=None, predators=[]):
|
||
"""
|
||
更新群体成员状态
|
||
计算所有作用力并更新成员的位置和速度
|
||
"""
|
||
# 计算基本Boids力
|
||
cohesion_force = self.calculate_cohesion(member, neighbors)
|
||
separation_force = self.calculate_separation(member, neighbors)
|
||
alignment_force = self.calculate_alignment(member, neighbors)
|
||
|
||
# 计算扩展力
|
||
obstacle_force = self.calculate_obstacle_avoidance(member, obstacles)
|
||
bound_force = self.calculate_boundaries(member, bounds)
|
||
predator_force = self.calculate_avoid_predator(member, predators)
|
||
|
||
# 计算目标导向力
|
||
seek_force = Vec3(0, 0, 0)
|
||
if target:
|
||
seek_force = self.calculate_seek(member, target)
|
||
|
||
# 计算路径跟随力
|
||
path_force = Vec3(0, 0, 0)
|
||
if path:
|
||
path_force = self.calculate_follow_path(member, path)
|
||
|
||
# 计算游走力
|
||
wander_force = Vec3(0, 0, 0)
|
||
if self.config.get("wander_enabled", False):
|
||
wander_force = self.calculate_wander(member)
|
||
|
||
# 应用权重
|
||
cohesion_force *= self.config.get("cohesion_weight", 1.0)
|
||
separation_force *= self.config.get("separation_weight", 1.5)
|
||
alignment_force *= self.config.get("alignment_weight", 1.0)
|
||
obstacle_force *= self.config.get("obstacle_weight", 2.0)
|
||
seek_force *= self.config.get("seek_weight", 1.0)
|
||
bound_force *= self.config.get("boundary_weight", 2.0)
|
||
path_force *= self.config.get("path_weight", 1.0)
|
||
wander_force *= self.config.get("wander_weight", 0.5)
|
||
predator_force *= self.config.get("predator_avoid_weight", 3.0)
|
||
|
||
# 计算总加速度
|
||
acceleration = (
|
||
cohesion_force +
|
||
separation_force +
|
||
alignment_force +
|
||
obstacle_force +
|
||
seek_force +
|
||
bound_force +
|
||
path_force +
|
||
wander_force +
|
||
predator_force
|
||
)
|
||
|
||
# 应用加速度限制
|
||
max_acceleration = self.config.get("max_acceleration", 1.0)
|
||
if acceleration.length() > max_acceleration:
|
||
acceleration.normalize()
|
||
acceleration *= max_acceleration
|
||
|
||
# 更新速度和位置
|
||
member['velocity'] += acceleration
|
||
|
||
# 限制最大速度
|
||
max_speed = self.config.get("max_speed", 5.0)
|
||
if member['velocity'].length() > max_speed:
|
||
member['velocity'].normalize()
|
||
member['velocity'] *= max_speed
|
||
|
||
member['position'] += member['velocity']
|
||
|
||
# 更新成员的朝向(使其面向移动方向)
|
||
if member['velocity'].length() > 0 and 'node' in member:
|
||
# 简化实现,实际应用中可能需要更复杂的朝向计算
|
||
member['node'].lookAt(member['position'] + member['velocity']) |