1.优化碰撞面板

This commit is contained in:
陈横 2025-09-17 17:34:23 +08:00
parent 39a52b3953
commit f9bd83c876
2 changed files with 853 additions and 579 deletions

View File

@ -309,51 +309,81 @@ class CollisionManager:
CollisionPlane, CollisionPolygon, Plane, Vec3
)
bounds = model.getBounds()
if bounds.isEmpty():
# 获取考虑变换后的实际尺寸和中心
transformed_info = self._getTransformedModelInfo(model)
if not transformed_info:
# 默认小球体
return CollisionSphere(Point3(0, 0, 0), 1.0)
center = bounds.getCenter()
radius = bounds.getRadius()
center = transformed_info['center']
radius = transformed_info['radius']
actual_size = transformed_info['size']
scale_factor = transformed_info['scale_factor']
# 自动选择最适合的形状
if shape_type == 'auto':
shape_type = self._determineOptimalShape(model, bounds)
shape_type = self._determineOptimalShape(model, transformed_info)
if shape_type == 'sphere':
return CollisionSphere(center, kwargs.get('radius', radius))
# 优化球形碰撞体
sphere_radius = kwargs.get('radius', radius)
# 支持位置偏移
pos_offset = kwargs.get('position_offset', Vec3(0, 0, 0))
sphere_center = Point3(center.x + pos_offset.x, center.y + pos_offset.y, center.z + pos_offset.z)
return CollisionSphere(sphere_center, sphere_radius)
elif shape_type == 'box':
# 创建自定义尺寸的包围盒
width = kwargs.get('width', bounds.getMax().x - bounds.getMin().x)
length = kwargs.get('length', bounds.getMax().y - bounds.getMin().y)
height = kwargs.get('height', bounds.getMax().z - bounds.getMin().z)
# 优化盒型碰撞体 - 更精确的尺寸和位置控制(考虑缩放)
# 获取自定义尺寸,如果没有提供则使用变换后的实际尺寸
width = kwargs.get('width', actual_size.x)
length = kwargs.get('length', actual_size.y)
height = kwargs.get('height', actual_size.z)
# 计算盒子的最小和最大点
# 支持位置偏移
pos_offset = kwargs.get('position_offset', Vec3(0, 0, 0))
box_center = Point3(center.x + pos_offset.x, center.y + pos_offset.y, center.z + pos_offset.z)
# 计算盒子的最小和最大点(基于偏移后的中心)
half_width = width / 2
half_length = length / 2
half_height = height / 2
min_point = Point3(center.x - half_width, center.y - half_length, center.z - half_height)
max_point = Point3(center.x + half_width, center.y + half_length, center.z + half_height)
min_point = Point3(box_center.x - half_width, box_center.y - half_length, box_center.z - half_height)
max_point = Point3(box_center.x + half_width, box_center.y + half_length, box_center.z + half_height)
return CollisionBox(min_point, max_point)
elif shape_type == 'capsule':
# 创建自定义参数的胶囊体
custom_height = kwargs.get('height', (bounds.getMax().z - bounds.getMin().z))
custom_radius = kwargs.get('radius', min(bounds.getRadius() * 0.5, custom_height * 0.3))
# 优化胶囊体碰撞 - 更合理的比例和位置控制(考虑缩放)
# 使用变换后的实际高度,或自定义高度
custom_height = kwargs.get('height', actual_size.z)
# 计算胶囊体的两个端点
point_a = Point3(center.x, center.y, center.z - custom_height/2 + custom_radius)
point_b = Point3(center.x, center.y, center.z + custom_height/2 - custom_radius)
# 更合理的半径计算:基于变换后模型宽度的平均值
default_radius = min(actual_size.x, actual_size.y) / 2.5 # 稍微小于模型的宽度
custom_radius = kwargs.get('radius', min(default_radius, custom_height * 0.4))
# 支持位置偏移
pos_offset = kwargs.get('position_offset', Vec3(0, 0, 0))
capsule_center = Point3(center.x + pos_offset.x, center.y + pos_offset.y, center.z + pos_offset.z)
# 计算胶囊体的两个端点(确保半径不会超出高度)
effective_height = max(custom_height, custom_radius * 2.1) # 确保高度至少是半径的2倍多一点
point_a = Point3(capsule_center.x, capsule_center.y, capsule_center.z - effective_height/2 + custom_radius)
point_b = Point3(capsule_center.x, capsule_center.y, capsule_center.z + effective_height/2 - custom_radius)
return CollisionCapsule(point_a, point_b, custom_radius)
elif shape_type == 'plane':
# 创建平面(适合地面、墙面)
# 优化平面碰撞 - 支持位置偏移和更灵活的法向量
normal = kwargs.get('normal', Vec3(0, 0, 1))
point = kwargs.get('point', center)
plane = Plane(normal, point)
# 支持位置偏移
pos_offset = kwargs.get('position_offset', Vec3(0, 0, 0))
plane_point = kwargs.get('point', Point3(center.x + pos_offset.x, center.y + pos_offset.y, center.z + pos_offset.z))
# 标准化法向量
normal.normalize()
plane = Plane(normal, plane_point)
return CollisionPlane(plane)
elif shape_type == 'polygon':
@ -378,10 +408,10 @@ class CollisionManager:
print("⚠️ 多边形至少需要3个顶点回退到球体")
return CollisionSphere(center, radius)
def _determineOptimalShape(self, model, bounds):
def _determineOptimalShape(self, model, transformed_info):
"""根据模型特征自动确定最适合的碰撞体形状"""
# 获取模型尺寸比例
size = bounds.getMax() - bounds.getMin()
# 获取变换后的模型尺寸比例
size = transformed_info['size']
max_dim = max(size.x, size.y, size.z)
min_dim = min(size.x, size.y, size.z)
@ -410,6 +440,87 @@ class CollisionManager:
else: # 其他用包围盒
return 'box'
def _getTransformedModelInfo(self, model):
"""获取考虑变换后的模型信息
Args:
model: 模型节点
Returns:
dict: 包含变换后的尺寸中心半径等信息
"""
try:
# 获取原始包围盒
bounds = model.getBounds()
if bounds.isEmpty():
return None
# 获取模型的变换矩阵
transform = model.getTransform()
scale = model.getScale()
# 计算缩放因子(取三轴缩放的平均值)
scale_factor = (abs(scale.x) + abs(scale.y) + abs(scale.z)) / 3.0
# 获取原始尺寸
original_size = bounds.getMax() - bounds.getMin()
# 应用缩放到尺寸
actual_size = Vec3(
original_size.x * abs(scale.x),
original_size.y * abs(scale.y),
original_size.z * abs(scale.z)
)
# 获取变换后的中心点(在世界坐标系中)
original_center = bounds.getCenter()
if hasattr(model, 'getPos'):
# 模型在世界坐标系中的位置
world_center = model.getPos(model.getParent() if model.getParent() else model)
center = Point3(world_center.x, world_center.y, world_center.z)
else:
# 如果无法获取世界位置,使用原始中心
center = original_center
# 计算变换后的半径(考虑缩放)
original_radius = bounds.getRadius()
transformed_radius = original_radius * scale_factor
# 调试信息
print(f"🔍 模型 {model.getName()} 变换信息:")
print(f" 原始尺寸: {original_size}")
print(f" 缩放因子: {scale}")
print(f" 变换后尺寸: {actual_size}")
print(f" 变换后半径: {transformed_radius:.2f}")
return {
'center': center,
'radius': transformed_radius,
'size': actual_size,
'scale_factor': scale_factor,
'original_size': original_size,
'scale': scale,
'transform': transform
}
except Exception as e:
print(f"⚠️ 获取模型变换信息失败: {e}")
# 回退到原始包围盒
bounds = model.getBounds()
if bounds.isEmpty():
return None
original_size = bounds.getMax() - bounds.getMin()
return {
'center': bounds.getCenter(),
'radius': bounds.getRadius(),
'size': original_size,
'scale_factor': 1.0,
'original_size': original_size,
'scale': Vec3(1, 1, 1),
'transform': None
}
def createMouseRay(self, screen_x, screen_y, mask_types=['SELECTABLE']):
"""创建鼠标射线"""
# 组合掩码

File diff suppressed because it is too large Load Diff