From 39a52b3953ebc56cd7eadc0ddfb3e4f4b3e9a843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=A8=AA?= <2938139566@qq.com> Date: Wed, 17 Sep 2025 15:41:55 +0800 Subject: [PATCH] =?UTF-8?q?1.=E5=A2=9E=E5=8A=A0=E7=A2=B0=E6=92=9E=E9=9D=A2?= =?UTF-8?q?=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- RenderPipelineFile/config/daytime.yaml | 2 +- core/collision_manager.py | 29 +- ui/property_panel.py | 1086 +++++++++++++++++++++++- 3 files changed, 1106 insertions(+), 11 deletions(-) diff --git a/RenderPipelineFile/config/daytime.yaml b/RenderPipelineFile/config/daytime.yaml index 58bbc54e..ac5efef2 100644 --- a/RenderPipelineFile/config/daytime.yaml +++ b/RenderPipelineFile/config/daytime.yaml @@ -17,7 +17,7 @@ control_points: scattering: sun_intensity: [[[0.0000000000,0.0000000000],[0.0041666667,0.0000000000],[0.0083333333,0.0000000000],[0.0125000000,0.0000000000],[0.0166666667,0.0000000000],[0.0208333333,0.0000000000],[0.0250000000,0.0000000000],[0.0291666667,0.0000000000],[0.0333333333,0.0000000000],[0.0375000000,0.0000000000],[0.0416666667,0.0000000000],[0.0458333333,0.0000000000],[0.0500000000,0.0000000000],[0.0541666667,0.0000000000],[0.0583333333,0.0000000000],[0.0625000000,0.0000000000],[0.0666666667,0.0000000000],[0.0708333333,0.0000000000],[0.0750000000,0.0000000000],[0.0791666667,0.0000000000],[0.0833333333,0.0000000000],[0.0875000000,0.0000000000],[0.0916666667,0.0000000000],[0.0958333333,0.0000000000],[0.1000000000,0.0000000000],[0.1041666667,0.0000000000],[0.1083333333,0.0000000000],[0.1125000000,0.0000000000],[0.1166666667,0.0000000000],[0.1208333333,0.0000000000],[0.1250000000,0.0000000000],[0.1291666667,0.0000000000],[0.1333333333,0.0000000000],[0.1375000000,0.0000000000],[0.1416666667,0.0000000000],[0.1458333333,0.0000000000],[0.1500000000,0.0000000000],[0.1541666667,0.0000000000],[0.1583333333,0.0000028805],[0.1625000000,0.0003577724],[0.1666666667,0.0013331400],[0.1708333333,0.0029671803],[0.1750000000,0.0052963381],[0.1791666667,0.0083550556],[0.1833333333,0.0121755589],[0.1875000000,0.0167876159],[0.1916666667,0.0222183530],[0.1958333333,0.0284919947],[0.2000000000,0.0356297193],[0.2041666667,0.0436494349],[0.2083333333,0.0525656099],[0.2125000000,0.0623891610],[0.2166666667,0.0731272461],[0.2208333333,0.0847831708],[0.2250000000,0.0973563167],[0.2291666667,0.1108419698],[0.2333333333,0.1252313631],[0.2375000000,0.1405115250],[0.2416666667,0.1566653434],[0.2458333333,0.1736715009],[0.2500000000,0.1915046014],[0.2541666667,0.2101350464],[0.2583333333,0.2295292930],[0.2625000000,0.2496498145],[0.2666666667,0.2704552670],[0.2708333333,0.2919006662],[0.2750000000,0.3139375192],[0.2791666667,0.3365139497],[0.2833333333,0.3595750662],[0.2875000000,0.3830630359],[0.2916666667,0.4069173972],[0.2958333333,0.4310753462],[0.3000000000,0.4554720417],[0.3041666667,0.4800408236],[0.3083333333,0.5047136020],[0.3125000000,0.5294212108],[0.3166666667,0.5540936424],[0.3208333333,0.5786605298],[0.3250000000,0.6030514553],[0.3291666667,0.6271963182],[0.3333333333,0.6510256858],[0.3375000000,0.6744711982],[0.3416666667,0.6974659988],[0.3458333333,0.7199450163],[0.3500000000,0.7418453485],[0.3541666667,0.7631067095],[0.3583333333,0.7836717291],[0.3625000000,0.8034862953],[0.3666666667,0.8224999302],[0.3708333333,0.8406661079],[0.3750000000,0.8579425235],[0.3791666667,0.8742914270],[0.3833333333,0.8896799131],[0.3875000000,0.9040801386],[0.3916666667,0.9174695289],[0.3958333333,0.9298310650],[0.4000000000,0.9411533765],[0.4041666667,0.9514309312],[0.4083333333,0.9606641691],[0.4125000000,0.9688595571],[0.4166666667,0.9760296330],[0.4208333333,0.9821930708],[0.4250000000,0.9873746114],[0.4291666667,0.9916050060],[0.4333333333,0.9949209310],[0.4375000000,0.9973647924],[0.4416666667,0.9989845508],[0.4458333333,0.9998334497],[0.4500000000,0.9999696949],[0.4541666667,0.9994560801],[0.4583333333,0.9983595429],[0.4625000000,0.9967506613],[0.4666666667,0.9947030614],[0.4708333333,0.9922927758],[0.4750000000,0.9895975125],[0.4791666667,0.9866958610],[0.4833333333,0.9836664262],[0.4875000000,0.9805868867],[0.4916666667,0.9775330316],[0.4958333333,0.9745777179],[0.5000000000,0.9717898417],[0.5041666667,0.9692332877],[0.5083333333,0.9669658924],[0.5125000000,0.9650384806],[0.5089595376,0.9690650222],[0.5208333333,0.9623666659],[0.5250000000,0.9616814371],[0.5291666667,0.9614534423],[0.5333333333,0.9616877089],[0.5375000000,0.9623790807],[0.5416666667,0.9635123329],[0.5458333333,0.9650624244],[0.5500000000,0.9669949804],[0.5541666667,0.9692669864],[0.5583333333,0.9718275065],[0.5625000000,0.9746185969],[0.5666666667,0.9775762863],[0.5708333333,0.9806315864],[0.5750000000,0.9837115661],[0.5791666667,0.9867403433],[0.5833333333,0.9896401655],[0.5875000000,0.9923323562],[0.5916666667,0.9947382579],[0.5958333333,0.9967800977],[0.6000000000,0.9983817820],[0.6041666667,0.9994696263],[0.6083333333,0.9999730028],[0.6125000000,0.9998249266],[0.6166666667,0.9989625601],[0.6208333333,0.9973276624],[0.6250000000,0.9948669567],[0.6291666667,0.9915324664],[0.6333333333,0.9872817545],[0.6375000000,0.9820781426],[0.6416666667,0.9758908775],[0.6458333333,0.9686952146],[0.6500000000,0.9604725211],[0.6541666667,0.9512102537],[0.6583333333,0.9409019858],[0.6625000000,0.9295473441],[0.6666666667,0.9171518878],[0.6708333333,0.9037270619],[0.6750000000,0.8892899902],[0.6791666667,0.8738633008],[0.6833333333,0.8574749656],[0.6875000000,0.8401579787],[0.6916666667,0.8219502453],[0.6958333333,0.8028941798],[0.7000000000,0.7830364456],[0.7041666667,0.7624277344],[0.7083333333,0.7411222520],[0.7125000000,0.7191776044],[0.7166666667,0.6966542563],[0.7208333333,0.6736152714],[0.7250000000,0.6501259629],[0.7291666667,0.6262533880],[0.7333333333,0.6020661121],[0.7375000000,0.5776338043],[0.7416666667,0.5530267796],[0.7458333333,0.5283156992],[0.7500000000,0.5035711751],[0.7541666667,0.4788634341],[0.7583333333,0.4542618347],[0.7625000000,0.4298347613],[0.7666666667,0.4056490351],[0.7708333333,0.3817697830],[0.7750000000,0.3582600107],[0.7791666667,0.3351803495],[0.7833333333,0.3125888445],[0.7875000000,0.2905406366],[0.7916666667,0.2690876955],[0.7958333333,0.2482787388],[0.8000000000,0.2281588906],[0.8041666667,0.2087696425],[0.8083333333,0.1901486315],[0.8125000000,0.1723295359],[0.8166666667,0.1553419918],[0.8208333333,0.1392115328],[0.8250000000,0.1239595144],[0.8291666667,0.1096030703],[0.8333333333,0.0961551918],[0.8375000000,0.0836246599],[0.8416666667,0.0720161369],[0.8458333333,0.0613302273],[0.8500000000,0.0515635598],[0.8541666667,0.0427088803],[0.8583333333,0.0347551990],[0.8625000000,0.0276878920],[0.8666666667,0.0214889271],[0.8708333333,0.0161369711],[0.8750000000,0.0116076130],[0.8791666667,0.0078735477],[0.8833333333,0.0049047927],[0.8875000000,0.0026688977],[0.8916666667,0.0011311782],[0.8958333333,0.0002549473],[0.9000000000,0.0000000000],[0.9041666667,0.0000000000],[0.9083333333,0.0000000000],[0.9125000000,0.0000000000],[0.9166666667,0.0000000000],[0.9208333333,0.0000000000],[0.9250000000,0.0000000000],[0.9291666667,0.0000000000],[0.9333333333,0.0000000000],[0.9375000000,0.0000000000],[0.9416666667,0.0000000000],[0.9458333333,0.0000000000],[0.9500000000,0.0000000000],[0.9541666667,0.0000000000],[0.9583333333,0.0000000000],[0.9625000000,0.0000000000],[0.9666666667,0.0000000000],[0.9708333333,0.0000000000],[0.9750000000,0.0000000000],[0.9791666667,0.0000000000],[0.9833333333,0.0000000000],[0.9875000000,0.0000000000],[0.9916666667,0.0000000000],[0.9958333333,0.0000000000]]] sun_color: [[[0.5010435645,0.5818710306],[0.0433100000,0.8999700000],[0.8635787716,0.9130000000],[0.1785000000,0.8973600000],[0.8099800000,0.8651100000],[0.2360800000,0.7712700000],[0.6583432177,0.8485126184],[0.1266806142,0.9648102053],[0.9558541267,0.9090909091],[0.5568400771,0.7353760446]],[[0.5001318426,0.5160300000],[0.0572700000,0.6541600000],[0.2395000000,0.5976800000],[0.8104600000,0.6009000000],[0.6967400000,0.5483900000]],[[0.0862400000,0.4257800000],[0.4955600000,0.4033000000],[0.8234200000,0.4340200000]]] - sun_azimuth: [[[0.5000000000,0.5000000000]]] + sun_azimuth: [[[0.5000000000,0.4944444444]]] sun_altitude: [[[0.5000000000,0.8555555556]]] extinction: [[[0.4913294798,0.6378830084]]] volumetrics: diff --git a/core/collision_manager.py b/core/collision_manager.py index 041571bf..853ad45b 100644 --- a/core/collision_manager.py +++ b/core/collision_manager.py @@ -325,18 +325,29 @@ class CollisionManager: return CollisionSphere(center, kwargs.get('radius', radius)) elif shape_type == 'box': - # 创建包围盒 - min_point = bounds.getMin() - max_point = bounds.getMax() + # 创建自定义尺寸的包围盒 + 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) + + # 计算盒子的最小和最大点 + 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) return CollisionBox(min_point, max_point) elif shape_type == 'capsule': - # 创建胶囊体(适合角色) - height = kwargs.get('height', (bounds.getMax().z - bounds.getMin().z)) - radius = kwargs.get('radius', min(bounds.getRadius() * 0.5, height * 0.3)) - point_a = Point3(center.x, center.y, bounds.getMin().z + radius) - point_b = Point3(center.x, center.y, bounds.getMax().z - radius) - return CollisionCapsule(point_a, point_b, radius) + # 创建自定义参数的胶囊体 + 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)) + + # 计算胶囊体的两个端点 + 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) + return CollisionCapsule(point_a, point_b, custom_radius) elif shape_type == 'plane': # 创建平面(适合地面、墙面) diff --git a/ui/property_panel.py b/ui/property_panel.py index d7262ee6..063f1a25 100644 --- a/ui/property_panel.py +++ b/ui/property_panel.py @@ -1213,6 +1213,9 @@ class PropertyPanelManager: self.transform_group.setLayout(transform_layout) self._propertyLayout.addWidget(self.transform_group) + # 碰撞检测面板 + self._addCollisionPanel(model) + # 动画和太阳方位角面板 self._addAnimationPanel(model) self._addSunAzimuthPanel() @@ -8683,4 +8686,1085 @@ except Exception as e: if actor: actor.stop() actor.cleanup() - actor.removeNode() \ No newline at end of file + actor.removeNode() + + def _addCollisionPanel(self, model): + """添加碰撞检测面板""" + try: + # 创建碰撞检测组 + collision_group = QGroupBox("碰撞检测") + collision_layout = QGridLayout() + + # 检查模型是否已有碰撞 + has_collision = self._hasCollision(model) + + # 碰撞状态标签 + status_label = QLabel("状态:") + collision_layout.addWidget(status_label, 0, 0) + + # 状态文本(需要保存引用以便更新) + self.collision_status_text = QLabel("已启用" if has_collision else "未启用") + self.collision_status_text.setStyleSheet("color: green;" if has_collision else "color: red;") + collision_layout.addWidget(self.collision_status_text, 0, 1) + + # 形状选择标签(始终显示) + self.collision_shape_label = QLabel("碰撞形状:") + collision_layout.addWidget(self.collision_shape_label, 1, 0) + + # 形状选择下拉框(始终显示) + self.collision_shape_combo = QComboBox() + self.collision_shape_combo.addItems([ + "球形 (Sphere)", + "盒型 (Box)", + "胶囊体 (Capsule)", + "平面 (Plane)", + "自动选择 (Auto)" + ]) + collision_layout.addWidget(self.collision_shape_combo, 1, 1) + + # 保存布局引用,用于动态添加/移除控件 + self.collision_layout = collision_layout + self.collision_group = collision_group + + current_row = 2 # 下一行的索引 + + # 显示/隐藏切换按钮(只有有碰撞时才显示) + if has_collision: + # 检查碰撞的当前可见性 + is_collision_visible = self._isCollisionVisible(model) + + # 显示当前碰撞类型并设置为只读 + current_shape = self._getCurrentCollisionShape(model) + self._setComboToShape(current_shape) + self.collision_shape_combo.setEnabled(False) + + # 添加碰撞参数调整控件 + current_row = self._addCollisionParameterControls(model, collision_layout, current_row, current_shape) + + # 显示/隐藏切换按钮 + self.collision_visibility_button = QPushButton("隐藏碰撞" if is_collision_visible else "显示碰撞") + self.collision_visibility_button.clicked.connect(lambda: self._toggleCollisionVisibility(model)) + collision_layout.addWidget(self.collision_visibility_button, current_row, 0, 1, 2) + current_row += 1 + + # 移除碰撞按钮 + self.collision_button = QPushButton("移除碰撞") + self.collision_button.clicked.connect(lambda: self._removeCollisionAndUpdate(model)) + collision_layout.addWidget(self.collision_button, current_row, 0, 1, 2) + else: + # 如果没有碰撞,设置默认选择并允许编辑 + self.collision_shape_combo.setCurrentText("球形 (Sphere)") + self.collision_shape_combo.setEnabled(True) + + # 清理之前的参数控件 + self._clearCollisionParameterControls() + + # 隐藏显示/隐藏按钮 + if hasattr(self, 'collision_visibility_button'): + self.collision_visibility_button.setVisible(False) + + # 添加碰撞按钮 + self.collision_button = QPushButton("添加碰撞") + self.collision_button.clicked.connect(lambda: self._addCollisionAndUpdate(model)) + collision_layout.addWidget(self.collision_button, current_row, 0, 1, 2) + collision_group.setLayout(collision_layout) + self._propertyLayout.addWidget(collision_group) + + except Exception as e: + print(f"创建碰撞面板失败: {e}") + def _addCollisionParameterControls(self, model, layout, start_row, shape_type): + """添加碰撞参数调整控件""" + try: + current_row = start_row + + # 位置调整控件(所有类型都有) + pos_label = QLabel("位置偏移:") + layout.addWidget(pos_label, current_row, 0) + current_row += 1 + + # X, Y, Z 位置调整 + self.collision_pos_x = self._createCollisionSpinBox(-100, 100, 2) + self.collision_pos_y = self._createCollisionSpinBox(-100, 100, 2) + self.collision_pos_z = self._createCollisionSpinBox(-100, 100, 2) + + layout.addWidget(QLabel("X:"), current_row, 0) + layout.addWidget(self.collision_pos_x, current_row, 1) + current_row += 1 + + layout.addWidget(QLabel("Y:"), current_row, 0) + layout.addWidget(self.collision_pos_y, current_row, 1) + current_row += 1 + + layout.addWidget(QLabel("Z:"), current_row, 0) + layout.addWidget(self.collision_pos_z, current_row, 1) + current_row += 1 + + # 连接位置变化信号 + self.collision_pos_x.valueChanged.connect(lambda v: self._updateCollisionPosition(model, 'x', v)) + self.collision_pos_y.valueChanged.connect(lambda v: self._updateCollisionPosition(model, 'y', v)) + self.collision_pos_z.valueChanged.connect(lambda v: self._updateCollisionPosition(model, 'z', v)) + + # 根据形状类型添加特定参数 + if shape_type == 'sphere': + current_row = self._addSphereParameters(model, layout, current_row) + elif shape_type == 'box': + current_row = self._addBoxParameters(model, layout, current_row) + elif shape_type == 'capsule': + current_row = self._addCapsuleParameters(model, layout, current_row) + elif shape_type == 'plane': + current_row = self._addPlaneParameters(model, layout, current_row) + + # 获取并设置当前参数值 + self._loadCurrentCollisionParameters(model, shape_type) + + return current_row + + except Exception as e: + print(f"添加碰撞参数控件失败: {e}") + return start_row + + def _createCollisionSpinBox(self, min_val, max_val, decimals=2): + """创建碰撞参数调整用的SpinBox""" + spinbox = QDoubleSpinBox() + spinbox.setRange(min_val, max_val) + spinbox.setDecimals(decimals) + spinbox.setSingleStep(0.1) + return spinbox + + def _addSphereParameters(self, model, layout, start_row): + """添加球形碰撞参数""" + current_row = start_row + + # 半径调整 + radius_label = QLabel("半径:") + layout.addWidget(radius_label, current_row, 0) + + self.collision_radius = self._createCollisionSpinBox(0.1, 100, 2) + self.collision_radius.valueChanged.connect(lambda v: self._updateSphereRadius(model, v)) + layout.addWidget(self.collision_radius, current_row, 1) + current_row += 1 + + return current_row + + def _addBoxParameters(self, model, layout, start_row): + """添加盒型碰撞参数""" + current_row = start_row + + size_label = QLabel("尺寸:") + layout.addWidget(size_label, current_row, 0) + current_row += 1 + + # 宽度、长度、高度 + self.collision_width = self._createCollisionSpinBox(0.1, 100, 2) + self.collision_length = self._createCollisionSpinBox(0.1, 100, 2) + self.collision_height = self._createCollisionSpinBox(0.1, 100, 2) + + layout.addWidget(QLabel("宽度:"), current_row, 0) + layout.addWidget(self.collision_width, current_row, 1) + current_row += 1 + + layout.addWidget(QLabel("长度:"), current_row, 0) + layout.addWidget(self.collision_length, current_row, 1) + current_row += 1 + + layout.addWidget(QLabel("高度:"), current_row, 0) + layout.addWidget(self.collision_height, current_row, 1) + current_row += 1 + + # 连接信号 + self.collision_width.valueChanged.connect(lambda v: self._updateBoxSize(model, 'width', v)) + self.collision_length.valueChanged.connect(lambda v: self._updateBoxSize(model, 'length', v)) + self.collision_height.valueChanged.connect(lambda v: self._updateBoxSize(model, 'height', v)) + + return current_row + + def _addCapsuleParameters(self, model, layout, start_row): + """添加胶囊体碰撞参数""" + current_row = start_row + + # 半径和高度 + radius_label = QLabel("半径:") + layout.addWidget(radius_label, current_row, 0) + + self.collision_capsule_radius = self._createCollisionSpinBox(0.1, 100, 2) + self.collision_capsule_radius.valueChanged.connect(lambda v: self._updateCapsuleRadius(model, v)) + layout.addWidget(self.collision_capsule_radius, current_row, 1) + current_row += 1 + + height_label = QLabel("高度:") + layout.addWidget(height_label, current_row, 0) + + self.collision_capsule_height = self._createCollisionSpinBox(0.1, 100, 2) + self.collision_capsule_height.valueChanged.connect(lambda v: self._updateCapsuleHeight(model, v)) + layout.addWidget(self.collision_capsule_height, current_row, 1) + current_row += 1 + + return current_row + + def _addPlaneParameters(self, model, layout, start_row): + """添加平面碰撞参数""" + current_row = start_row + + # 法向量 + normal_label = QLabel("法向量:") + layout.addWidget(normal_label, current_row, 0) + current_row += 1 + + self.collision_normal_x = self._createCollisionSpinBox(-1, 1, 2) + self.collision_normal_y = self._createCollisionSpinBox(-1, 1, 2) + self.collision_normal_z = self._createCollisionSpinBox(-1, 1, 2) + + layout.addWidget(QLabel("Nx:"), current_row, 0) + layout.addWidget(self.collision_normal_x, current_row, 1) + current_row += 1 + + layout.addWidget(QLabel("Ny:"), current_row, 0) + layout.addWidget(self.collision_normal_y, current_row, 1) + current_row += 1 + + layout.addWidget(QLabel("Nz:"), current_row, 0) + layout.addWidget(self.collision_normal_z, current_row, 1) + current_row += 1 + + # 连接信号 + self.collision_normal_x.valueChanged.connect(lambda v: self._updatePlaneNormal(model, 'x', v)) + self.collision_normal_y.valueChanged.connect(lambda v: self._updatePlaneNormal(model, 'y', v)) + self.collision_normal_z.valueChanged.connect(lambda v: self._updatePlaneNormal(model, 'z', v)) + + return current_row + def _hasCollision(self, model): + """检查模型是否已有碰撞体""" + try: + from panda3d.core import CollisionNode + + # 检查模型及其子节点是否有碰撞节点 + collision_nodes = model.findAllMatches("**/+CollisionNode") + has_collision = collision_nodes.getNumPaths() > 0 + + print(f"碰撞检查:模型 {model.getName()} - {'有' if has_collision else '无'}碰撞 (找到{collision_nodes.getNumPaths()}个碰撞节点)") + + return has_collision + except Exception as e: + print(f"检查碰撞失败: {e}") + return False + + def _isCollisionVisible(self, model): + """检查碰撞是否可见""" + try: + collision_nodes = model.findAllMatches("**/+CollisionNode") + for collision_np in collision_nodes: + # 检查碰撞节点是否隐藏 + return not collision_np.isHidden() + return False + except Exception as e: + print(f"检查碰撞可见性失败: {e}") + return False + + def _toggleCollisionVisibility(self, model): + """切换碰撞可见性""" + try: + collision_nodes = model.findAllMatches("**/+CollisionNode") + is_visible = self._isCollisionVisible(model) + + for collision_np in collision_nodes: + if is_visible: + collision_np.hide() + print(f"隐藏碰撞:{model.getName()}") + else: + collision_np.show() + print(f"显示碰撞:{model.getName()}") + + # 立即更新按钮状态 + self._updateCollisionVisibilityButton(model) + + except Exception as e: + print(f"切换碰撞可见性失败: {e}") + + def _updateCollisionVisibilityButton(self, model): + """更新碰撞可见性按钮状态""" + try: + if hasattr(self, 'collision_visibility_button'): + is_visible = self._isCollisionVisible(model) + self.collision_visibility_button.setText("隐藏碰撞" if is_visible else "显示碰撞") + print(f"更新可见性按钮:{model.getName()} - {'可见' if is_visible else '隐藏'}") + except Exception as e: + print(f"更新碰撞可见性按钮失败: {e}") + + def _getCurrentCollisionShape(self, model): + """获取当前模型的碰撞形状类型""" + try: + from panda3d.core import CollisionNode, CollisionSphere, CollisionBox, CollisionCapsule, CollisionPlane + + collision_nodes = model.findAllMatches("**/+CollisionNode") + for collision_np in collision_nodes: + collision_node = collision_np.node() + if collision_node.getNumSolids() > 0: + solid = collision_node.getSolid(0) + solid_type = type(solid).__name__ + + if solid_type == "CollisionSphere": + return "sphere" + elif solid_type == "CollisionBox": + return "box" + elif solid_type == "CollisionCapsule": + return "capsule" + elif solid_type == "CollisionPlane": + return "plane" + + return "sphere" # 默认返回球形 + except Exception as e: + print(f"获取碰撞形状失败: {e}") + return "sphere" + + def _setComboToShape(self, shape_type): + """根据形状类型设置下拉框选择""" + shape_map = { + "sphere": "球形 (Sphere)", + "box": "盒型 (Box)", + "capsule": "胶囊体 (Capsule)", + "plane": "平面 (Plane)", + "auto": "自动选择 (Auto)" + } + + if shape_type in shape_map: + self.collision_shape_combo.setCurrentText(shape_map[shape_type]) + else: + self.collision_shape_combo.setCurrentText("球形 (Sphere)") + + def _getSelectedCollisionShape(self): + """获取选中的碰撞形状类型""" + if hasattr(self, 'collision_shape_combo'): + shape_text = self.collision_shape_combo.currentText() + # 从显示文本中提取形状类型 + if "球形" in shape_text: + return 'sphere' + elif "盒型" in shape_text: + return 'box' + elif "胶囊体" in shape_text: + return 'capsule' + elif "平面" in shape_text: + return 'plane' + elif "自动选择" in shape_text: + return 'auto' + return 'sphere' # 默认返回球形 + + def _addCollisionAndUpdate(self, model): + """添加指定形状的碰撞体并更新界面""" + try: + # 防止重复调用 + if getattr(self, '_adding_collision', False): + print("正在添加碰撞,跳过重复调用") + return + + self._adding_collision = True + + if hasattr(self.world, 'scene_manager'): + # 获取选中的碰撞形状 + shape_type = self._getSelectedCollisionShape() + + # 参考scene_manager的setupCollision方法 + from panda3d.core import CollisionNode, BitMask32 + + # 创建碰撞节点 + cNode = CollisionNode(f'modelCollision_{model.getName()}') + + # 设置碰撞掩码 + cNode.setIntoCollideMask(BitMask32.bit(2)) # 用于鼠标选择 + + # 如果启用了模型间碰撞检测,添加额外的掩码 + if (hasattr(self.world, 'collision_manager') and + hasattr(self.world.collision_manager, 'model_collision_enabled') and + self.world.collision_manager.model_collision_enabled): + current_mask = cNode.getIntoCollideMask() + model_collision_mask = BitMask32.bit(6) # MODEL_COLLISION + cNode.setIntoCollideMask(current_mask | model_collision_mask) + + # 创建指定形状的碰撞体 + if hasattr(self.world, 'collision_manager'): + collision_shape = self.world.collision_manager.createCollisionShape(model, shape_type) + else: + # 回退方案:创建简单球体 + from panda3d.core import CollisionSphere, Point3 + bounds = model.getBounds() + if bounds.isEmpty(): + collision_shape = CollisionSphere(Point3(0, 0, 0), 1.0) + else: + center = bounds.getCenter() + radius = bounds.getRadius() + if radius <= 0: + radius = 1.0 + collision_shape = CollisionSphere(center, radius) + + cNode.addSolid(collision_shape) + + # 将碰撞节点附加到模型上 + cNodePath = model.attachNewNode(cNode) + + # 根据调试设置决定是否显示碰撞体 + if hasattr(self.world, 'debug_collision') and self.world.debug_collision: + cNodePath.show() + else: + cNodePath.hide() + + # 为模型添加碰撞相关标签 + model.setTag("has_collision", "true") + model.setTag("collision_shape", shape_type) + if 'radius' in locals(): + model.setTag("collision_radius", str(radius)) + + print(f"✅ 为模型 {model.getName()} 添加了 {shape_type} 碰撞体") + + # 简单更新按钮状态,不调用完整的状态更新 + if hasattr(self, 'collision_button'): + self.collision_button.setText("移除碰撞") + try: + self.collision_button.clicked.disconnect() + except: + pass + self.collision_button.clicked.connect(lambda: self._removeCollisionAndUpdate(model)) + + if hasattr(self, 'collision_status_text'): + self.collision_status_text.setText("已启用") + self.collision_status_text.setStyleSheet("color: green;") + + if hasattr(self, 'collision_shape_combo'): + self.collision_shape_combo.setEnabled(False) + + else: + print("场景管理器未初始化") + + except Exception as e: + print(f"添加碰撞失败: {e}") + import traceback + traceback.print_exc() + finally: + # 确保标志被重置 + self._adding_collision = False + + def _removeCollisionAndUpdate(self, model): + """移除模型的碰撞体并更新界面""" + try: + # 查找并移除碰撞节点 + collision_nodes = model.findAllMatches("**/+CollisionNode") + for collision_np in collision_nodes: + collision_np.removeNode() + + print(f"移除了模型 {model.getName()} 的碰撞体") + + # 重置状态并更新界面 + self._previous_collision_state = True # 强制刷新 + self._updateCollisionPanelState(model) + + except Exception as e: + print(f"移除碰撞失败: {e}") + + def _updateCollisionPanelState(self, model): + """更新碰撞面板状态""" + try: + # 防止重复调用 + if getattr(self, '_updating_collision_panel', False): + return + + self._updating_collision_panel = True + + if hasattr(self, 'collision_button') and hasattr(self, 'collision_status_text') and hasattr(self, 'collision_shape_combo'): + has_collision = self._hasCollision(model) + + # 更新状态文本和颜色 + self.collision_status_text.setText("已启用" if has_collision else "未启用") + self.collision_status_text.setStyleSheet("color: green;" if has_collision else "color: red;") + + if has_collision: + # 有碰撞:显示移除按钮,下拉框变为只读并显示当前类型 + self.collision_button.setText("移除碰撞") + + # 先断开所有连接,再重新连接 + try: + self.collision_button.clicked.disconnect() + except: + pass + self.collision_button.clicked.connect(lambda: self._removeCollisionAndUpdate(model)) + + # 获取并显示当前碰撞类型,设置为只读 + current_shape = self._getCurrentCollisionShape(model) + self._setComboToShape(current_shape) + self.collision_shape_combo.setEnabled(False) + + # 确保参数控件存在 - 只在没有参数控件时才添加 + if not hasattr(self, 'collision_pos_x'): + print("添加碰撞参数控件") + self._addParameterControlsToExistingPanel(model, current_shape) + + # 显示/隐藏按钮状态更新 + if not hasattr(self, 'collision_visibility_button'): + # 创建可见性按钮 + self._addVisibilityButtonToExistingPanel(model) + else: + self.collision_visibility_button.setVisible(True) + self._updateCollisionVisibilityButton(model) + + print(f"碰撞面板状态更新:有碰撞 - {current_shape}") + + else: + # 无碰撞:显示添加按钮,下拉框变为可编辑 + self.collision_button.setText("添加碰撞") + + # 先断开所有连接,再重新连接 + try: + self.collision_button.clicked.disconnect() + except: + pass + self.collision_button.clicked.connect(lambda: self._addCollisionAndUpdate(model)) + + # 恢复为可编辑状态 + self.collision_shape_combo.setEnabled(True) + + # 隐藏并清理参数控件 - 只在有参数控件时才清理 + if hasattr(self, 'collision_pos_x'): + print("清理碰撞参数控件") + self._hideCollisionParameterControls() + + # 隐藏显示/隐藏按钮 + if hasattr(self, 'collision_visibility_button'): + self.collision_visibility_button.setVisible(False) + + print(f"碰撞面板状态更新:无碰撞 - 可编辑") + + except Exception as e: + print(f"更新碰撞面板状态失败: {e}") + import traceback + traceback.print_exc() + finally: + # 确保标志被重置 + self._updating_collision_panel = False + + def _loadCurrentCollisionParameters(self, model, shape_type): + """加载当前碰撞参数到界面""" + try: + collision_nodes = model.findAllMatches("**/+CollisionNode") + for collision_np in collision_nodes: + collision_node = collision_np.node() + if collision_node.getNumSolids() > 0: + solid = collision_node.getSolid(0) + + # 获取碰撞节点的位置 + pos = collision_np.getPos() + self.collision_pos_x.setValue(pos.x) + self.collision_pos_y.setValue(pos.y) + self.collision_pos_z.setValue(pos.z) + + if shape_type == 'sphere': + self._loadSphereParameters(solid) + elif shape_type == 'box': + self._loadBoxParameters(solid) + elif shape_type == 'capsule': + self._loadCapsuleParameters(solid) + elif shape_type == 'plane': + self._loadPlaneParameters(solid) + break + + except Exception as e: + print(f"加载碰撞参数失败: {e}") + + def _loadSphereParameters(self, solid): + """加载球形参数""" + try: + from panda3d.core import CollisionSphere + if isinstance(solid, CollisionSphere): + radius = solid.getRadius() + self.collision_radius.setValue(radius) + except Exception as e: + print(f"加载球形参数失败: {e}") + + def _loadBoxParameters(self, solid): + """加载盒型参数""" + try: + from panda3d.core import CollisionBox + if isinstance(solid, CollisionBox): + min_point = solid.getMin() + max_point = solid.getMax() + width = max_point.x - min_point.x + length = max_point.y - min_point.y + height = max_point.z - min_point.z + + self.collision_width.setValue(width) + self.collision_length.setValue(length) + self.collision_height.setValue(height) + except Exception as e: + print(f"加载盒型参数失败: {e}") + + def _loadCapsuleParameters(self, solid): + """加载胶囊体参数""" + try: + from panda3d.core import CollisionCapsule + if isinstance(solid, CollisionCapsule): + radius = solid.getRadius() + point_a = solid.getPointA() + point_b = solid.getPointB() + height = (point_b - point_a).length() + + self.collision_capsule_radius.setValue(radius) + self.collision_capsule_height.setValue(height) + except Exception as e: + print(f"加载胶囊体参数失败: {e}") + + def _loadPlaneParameters(self, solid): + """加载平面参数""" + try: + from panda3d.core import CollisionPlane + if isinstance(solid, CollisionPlane): + plane = solid.getPlane() + normal = plane.getNormal() + + self.collision_normal_x.setValue(normal.x) + self.collision_normal_y.setValue(normal.y) + self.collision_normal_z.setValue(normal.z) + except Exception as e: + print(f"加载平面参数失败: {e}") + + def _updateCollisionPosition(self, model, axis, value): + """更新碰撞位置""" + try: + collision_nodes = model.findAllMatches("**/+CollisionNode") + for collision_np in collision_nodes: + current_pos = collision_np.getPos() + if axis == 'x': + collision_np.setPos(value, current_pos.y, current_pos.z) + elif axis == 'y': + collision_np.setPos(current_pos.x, value, current_pos.z) + elif axis == 'z': + collision_np.setPos(current_pos.x, current_pos.y, value) + print(f"更新碰撞位置 {axis}: {value}") + break + except Exception as e: + print(f"更新碰撞位置失败: {e}") + + def _updateSphereRadius(self, model, radius): + """更新球形半径""" + try: + self._recreateCollisionShape(model, 'sphere', radius=radius) + print(f"更新球形半径: {radius}") + except Exception as e: + print(f"更新球形半径失败: {e}") + + def _updateBoxSize(self, model, dimension, value): + """更新盒型尺寸""" + try: + # 获取当前所有尺寸 + width = self.collision_width.value() if hasattr(self, 'collision_width') else value + length = self.collision_length.value() if hasattr(self, 'collision_length') else value + height = self.collision_height.value() if hasattr(self, 'collision_height') else value + + self._recreateCollisionShape(model, 'box', width=width, length=length, height=height) + print(f"更新盒型{dimension}: {value}") + except Exception as e: + print(f"更新盒型尺寸失败: {e}") + + def _updateCapsuleRadius(self, model, radius): + """更新胶囊体半径""" + try: + height = self.collision_capsule_height.value() if hasattr(self, 'collision_capsule_height') else 2.0 + self._recreateCollisionShape(model, 'capsule', radius=radius, height=height) + print(f"更新胶囊体半径: {radius}") + except Exception as e: + print(f"更新胶囊体半径失败: {e}") + + def _updateCapsuleHeight(self, model, height): + """更新胶囊体高度""" + try: + radius = self.collision_capsule_radius.value() if hasattr(self, 'collision_capsule_radius') else 0.5 + self._recreateCollisionShape(model, 'capsule', radius=radius, height=height) + print(f"更新胶囊体高度: {height}") + except Exception as e: + print(f"更新胶囊体高度失败: {e}") + + def _updatePlaneNormal(self, model, axis, value): + """更新平面法向量""" + try: + # 获取当前法向量 + normal_x = self.collision_normal_x.value() if hasattr(self, 'collision_normal_x') else 0 + normal_y = self.collision_normal_y.value() if hasattr(self, 'collision_normal_y') else 0 + normal_z = self.collision_normal_z.value() if hasattr(self, 'collision_normal_z') else 1 + + self._recreateCollisionShape(model, 'plane', normal=(normal_x, normal_y, normal_z)) + print(f"更新平面法向量 {axis}: {value}") + except Exception as e: + print(f"更新平面法向量失败: {e}") + + def _recreateCollisionShape(self, model, shape_type, **kwargs): + """重新创建碰撞形状(保持位置和可见性)""" + try: + # 保存当前状态 + collision_nodes = model.findAllMatches("**/+CollisionNode") + if not collision_nodes: + return + + collision_np = collision_nodes[0] + current_pos = collision_np.getPos() + is_visible = not collision_np.isHidden() + + # 移除旧的碰撞体 + collision_np.removeNode() + + # 创建新的碰撞体 + from panda3d.core import CollisionNode, BitMask32 + cNode = CollisionNode(f'modelCollision_{model.getName()}') + cNode.setIntoCollideMask(BitMask32.bit(2)) + + # 创建新形状 + if hasattr(self.world, 'collision_manager'): + collision_shape = self.world.collision_manager.createCollisionShape(model, shape_type, **kwargs) + else: + # 回退方案 + from panda3d.core import CollisionSphere, Point3 + collision_shape = CollisionSphere(Point3(0, 0, 0), kwargs.get('radius', 1.0)) + + cNode.addSolid(collision_shape) + + # 重新附加并恢复状态 + new_collision_np = model.attachNewNode(cNode) + new_collision_np.setPos(current_pos) + + if is_visible: + new_collision_np.show() + else: + new_collision_np.hide() + + except Exception as e: + print(f"重新创建碰撞形状失败: {e}") + import traceback + traceback.print_exc() + + def _clearCollisionParameterControls(self): + """清理碰撞参数控件""" + try: + # 位置控件 + for attr in ['collision_pos_x', 'collision_pos_y', 'collision_pos_z']: + if hasattr(self, attr): + widget = getattr(self, attr) + if widget and widget.parent(): + widget.setParent(None) + widget.deleteLater() + delattr(self, attr) + + # 球形参数控件 + if hasattr(self, 'collision_radius'): + if self.collision_radius and self.collision_radius.parent(): + self.collision_radius.setParent(None) + self.collision_radius.deleteLater() + delattr(self, 'collision_radius') + + # 盒型参数控件 + for attr in ['collision_width', 'collision_length', 'collision_height']: + if hasattr(self, attr): + widget = getattr(self, attr) + if widget and widget.parent(): + widget.setParent(None) + widget.deleteLater() + delattr(self, attr) + + # 胶囊体参数控件 + for attr in ['collision_capsule_radius', 'collision_capsule_height']: + if hasattr(self, attr): + widget = getattr(self, attr) + if widget and widget.parent(): + widget.setParent(None) + widget.deleteLater() + delattr(self, attr) + + # 平面参数控件 + for attr in ['collision_normal_x', 'collision_normal_y', 'collision_normal_z']: + if hasattr(self, attr): + widget = getattr(self, attr) + if widget and widget.parent(): + widget.setParent(None) + widget.deleteLater() + delattr(self, attr) + + print("清理碰撞参数控件完成") + + except Exception as e: + print(f"清理碰撞参数控件失败: {e}") + + def _refreshCollisionPanel(self, model): + """刷新整个碰撞面板(简化版)""" + try: + print("使用简化的面板刷新") + # 直接调用状态更新,不删除面板 + self._updateCollisionPanelState(model) + + except Exception as e: + print(f"刷新碰撞面板失败: {e}") + import traceback + traceback.print_exc() + + def _addParameterControlsToExistingPanel(self, model, shape_type): + """向现有面板添加参数控件""" + try: + if not hasattr(self, 'collision_layout'): + return + + layout = self.collision_layout + + # 首先清理可能存在的旧控件 + self._hideCollisionParameterControls() + + # 找到插入位置(在按钮之前) + current_row = 2 + + # 位置调整控件 + pos_label = QLabel("位置偏移:") + pos_label.setVisible(True) # 确保可见 + layout.addWidget(pos_label, current_row, 0) + current_row += 1 + + # X, Y, Z 位置调整 + self.collision_pos_x = self._createCollisionSpinBox(-100, 100, 2) + self.collision_pos_y = self._createCollisionSpinBox(-100, 100, 2) + self.collision_pos_z = self._createCollisionSpinBox(-100, 100, 2) + + x_label = QLabel("X:") + x_label.setVisible(True) + layout.addWidget(x_label, current_row, 0) + self.collision_pos_x.setVisible(True) + layout.addWidget(self.collision_pos_x, current_row, 1) + current_row += 1 + + y_label = QLabel("Y:") + y_label.setVisible(True) + layout.addWidget(y_label, current_row, 0) + self.collision_pos_y.setVisible(True) + layout.addWidget(self.collision_pos_y, current_row, 1) + current_row += 1 + + z_label = QLabel("Z:") + z_label.setVisible(True) + layout.addWidget(z_label, current_row, 0) + self.collision_pos_z.setVisible(True) + layout.addWidget(self.collision_pos_z, current_row, 1) + current_row += 1 + + # 连接位置变化信号 + self.collision_pos_x.valueChanged.connect(lambda v: self._updateCollisionPosition(model, 'x', v)) + self.collision_pos_y.valueChanged.connect(lambda v: self._updateCollisionPosition(model, 'y', v)) + self.collision_pos_z.valueChanged.connect(lambda v: self._updateCollisionPosition(model, 'z', v)) + + # 根据形状类型添加特定参数 + if shape_type == 'sphere': + current_row = self._addSphereParametersToExisting(model, layout, current_row) + elif shape_type == 'box': + current_row = self._addBoxParametersToExisting(model, layout, current_row) + elif shape_type == 'capsule': + current_row = self._addCapsuleParametersToExisting(model, layout, current_row) + elif shape_type == 'plane': + current_row = self._addPlaneParametersToExisting(model, layout, current_row) + + # 重新定位按钮 + self._repositionButtons(current_row) + + # 加载参数值 + self._loadCurrentCollisionParameters(model, shape_type) + + except Exception as e: + print(f"添加参数控件到现有面板失败: {e}") + import traceback + traceback.print_exc() + + def _addSphereParametersToExisting(self, model, layout, start_row): + """向现有面板添加球形参数""" + current_row = start_row + + radius_label = QLabel("半径:") + radius_label.setVisible(True) + layout.addWidget(radius_label, current_row, 0) + + self.collision_radius = self._createCollisionSpinBox(0.1, 100, 2) + self.collision_radius.setVisible(True) + self.collision_radius.valueChanged.connect(lambda v: self._updateSphereRadius(model, v)) + layout.addWidget(self.collision_radius, current_row, 1) + current_row += 1 + + return current_row + + def _addBoxParametersToExisting(self, model, layout, start_row): + """向现有面板添加盒型参数""" + current_row = start_row + + size_label = QLabel("尺寸:") + size_label.setVisible(True) + layout.addWidget(size_label, current_row, 0) + current_row += 1 + + self.collision_width = self._createCollisionSpinBox(0.1, 100, 2) + self.collision_length = self._createCollisionSpinBox(0.1, 100, 2) + self.collision_height = self._createCollisionSpinBox(0.1, 100, 2) + + width_label = QLabel("宽度:") + width_label.setVisible(True) + layout.addWidget(width_label, current_row, 0) + self.collision_width.setVisible(True) + layout.addWidget(self.collision_width, current_row, 1) + current_row += 1 + + length_label = QLabel("长度:") + length_label.setVisible(True) + layout.addWidget(length_label, current_row, 0) + self.collision_length.setVisible(True) + layout.addWidget(self.collision_length, current_row, 1) + current_row += 1 + + height_label = QLabel("高度:") + height_label.setVisible(True) + layout.addWidget(height_label, current_row, 0) + self.collision_height.setVisible(True) + layout.addWidget(self.collision_height, current_row, 1) + current_row += 1 + + # 连接信号 + self.collision_width.valueChanged.connect(lambda v: self._updateBoxSize(model, 'width', v)) + self.collision_length.valueChanged.connect(lambda v: self._updateBoxSize(model, 'length', v)) + self.collision_height.valueChanged.connect(lambda v: self._updateBoxSize(model, 'height', v)) + + return current_row + + def _addCapsuleParametersToExisting(self, model, layout, start_row): + """向现有面板添加胶囊体参数""" + current_row = start_row + + radius_label = QLabel("半径:") + layout.addWidget(radius_label, current_row, 0) + + self.collision_capsule_radius = self._createCollisionSpinBox(0.1, 100, 2) + self.collision_capsule_radius.valueChanged.connect(lambda v: self._updateCapsuleRadius(model, v)) + layout.addWidget(self.collision_capsule_radius, current_row, 1) + current_row += 1 + + height_label = QLabel("高度:") + layout.addWidget(height_label, current_row, 0) + + self.collision_capsule_height = self._createCollisionSpinBox(0.1, 100, 2) + self.collision_capsule_height.valueChanged.connect(lambda v: self._updateCapsuleHeight(model, v)) + layout.addWidget(self.collision_capsule_height, current_row, 1) + current_row += 1 + + return current_row + + def _addPlaneParametersToExisting(self, model, layout, start_row): + """向现有面板添加平面参数""" + current_row = start_row + + normal_label = QLabel("法向量:") + layout.addWidget(normal_label, current_row, 0) + current_row += 1 + + self.collision_normal_x = self._createCollisionSpinBox(-1, 1, 2) + self.collision_normal_y = self._createCollisionSpinBox(-1, 1, 2) + self.collision_normal_z = self._createCollisionSpinBox(-1, 1, 2) + + layout.addWidget(QLabel("Nx:"), current_row, 0) + layout.addWidget(self.collision_normal_x, current_row, 1) + current_row += 1 + + layout.addWidget(QLabel("Ny:"), current_row, 0) + layout.addWidget(self.collision_normal_y, current_row, 1) + current_row += 1 + + layout.addWidget(QLabel("Nz:"), current_row, 0) + layout.addWidget(self.collision_normal_z, current_row, 1) + current_row += 1 + + # 连接信号 + self.collision_normal_x.valueChanged.connect(lambda v: self._updatePlaneNormal(model, 'x', v)) + self.collision_normal_y.valueChanged.connect(lambda v: self._updatePlaneNormal(model, 'y', v)) + self.collision_normal_z.valueChanged.connect(lambda v: self._updatePlaneNormal(model, 'z', v)) + + return current_row + + def _addVisibilityButtonToExistingPanel(self, model): + """向现有面板添加可见性按钮""" + try: + if hasattr(self, 'collision_layout'): + layout = self.collision_layout + is_collision_visible = self._isCollisionVisible(model) + + # 找到合适的行位置 + current_row = layout.rowCount() + + self.collision_visibility_button = QPushButton("隐藏碰撞" if is_collision_visible else "显示碰撞") + self.collision_visibility_button.clicked.connect(lambda: self._toggleCollisionVisibility(model)) + layout.addWidget(self.collision_visibility_button, current_row - 1, 0, 1, 2) + + except Exception as e: + print(f"添加可见性按钮失败: {e}") + + def _repositionButtons(self, new_row): + """重新定位按钮位置""" + try: + if hasattr(self, 'collision_layout'): + layout = self.collision_layout + + # 移动可见性按钮 + if hasattr(self, 'collision_visibility_button'): + layout.addWidget(self.collision_visibility_button, new_row, 0, 1, 2) + new_row += 1 + + # 移动主按钮 + if hasattr(self, 'collision_button'): + layout.addWidget(self.collision_button, new_row, 0, 1, 2) + + except Exception as e: + print(f"重新定位按钮失败: {e}") + + def _hideCollisionParameterControls(self): + """隐藏碰撞参数控件(保留按钮)""" + try: + # 清理属性引用,但保留按钮 + param_attrs = [ + 'collision_pos_x', 'collision_pos_y', 'collision_pos_z', + 'collision_radius', + 'collision_width', 'collision_length', 'collision_height', + 'collision_capsule_radius', 'collision_capsule_height', + 'collision_normal_x', 'collision_normal_y', 'collision_normal_z' + ] + + # 隐藏并删除参数控件 + for attr in param_attrs: + if hasattr(self, attr): + widget = getattr(self, attr) + if widget: + widget.setVisible(False) + widget.setParent(None) + widget.deleteLater() + delattr(self, attr) + + # 同时清理可能的标签控件 + if hasattr(self, 'collision_layout'): + layout = self.collision_layout + + # 收集需要移除的控件(不包括基本控件和按钮) + widgets_to_remove = [] + + for i in range(layout.rowCount()): + if i >= 2: # 从第3行开始检查 + for j in range(layout.columnCount()): + item = layout.itemAtPosition(i, j) + if item: + widget = item.widget() + if widget and hasattr(widget, 'text'): + # 检查是否是参数相关的标签 + text = widget.text() + if any(keyword in text for keyword in ['位置偏移', 'X:', 'Y:', 'Z:', '半径:', '尺寸:', '宽度:', '长度:', '高度:', '法向量:', 'Nx:', 'Ny:', 'Nz:']): + widgets_to_remove.append(widget) + + # 移除参数标签 + for widget in widgets_to_remove: + widget.setVisible(False) + widget.setParent(None) + widget.deleteLater() + + print("隐藏碰撞参数控件完成(保留按钮)") + + except Exception as e: + print(f"隐藏碰撞参数控件失败: {e}") + import traceback + traceback.print_exc() \ No newline at end of file