EG/demo/collision_debug.py
2025-07-02 09:49:59 +08:00

189 lines
6.7 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from direct.showbase.ShowBase import ShowBase
from panda3d.core import (
Point3, Point2, Vec3, CollisionTraverser, CollisionHandlerQueue,
CollisionNode, CollisionRay, CollisionSphere, BitMask32, CardMaker
)
class CollisionDebugTest(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# 设置相机位置
self.cam.setPos(-3.87655, -188.38084, 82.602684)
self.cam.lookAt(0, 0, 0)
print(f"相机位置: {self.cam.getPos()}")
# 创建测试立方体
self.createTestCube()
# 设置射线检测
self.setupRayPicking()
# 调试碰撞系统
self.debugCollisionSystem()
# 测试不同类型的碰撞体
self.testDifferentCollisionTypes()
def createTestCube(self):
"""创建测试立方体"""
cm = CardMaker("TestCube")
cm.setFrame(-1, 1, -1, 1)
self.cube = self.render.attachNewNode(cm.generate())
self.cube.setPos(0, 10, 3)
self.cube.setScale(3, 3, 3)
self.cube.setColor(1, 0, 0, 1)
self.cube.setTwoSided(True)
print(f"立方体世界位置: {self.cube.getPos(self.render)}")
# 添加碰撞检测
cNode = CollisionNode('TestCubeCollision')
cSphere = CollisionSphere(Point3(0, 0, 0), 4.0)
cNode.addSolid(cSphere)
cNode.setIntoCollideMask(BitMask32.bit(0))
cNode.setFromCollideMask(BitMask32.allOff())
self.cNodePath = self.cube.attachNewNode(cNode)
# 强制显示碰撞体
self.cNodePath.show()
print(f"碰撞体已强制显示")
def setupRayPicking(self):
"""设置射线检测"""
self.picker = CollisionTraverser()
self.pickerQueue = CollisionHandlerQueue()
self.pickerNode = CollisionNode('mouseRay')
self.pickerRay = CollisionRay()
self.pickerNode.addSolid(self.pickerRay)
# 设置掩码
fromMask = BitMask32.bit(0)
self.pickerNode.setFromCollideMask(fromMask)
self.pickerNode.setIntoCollideMask(BitMask32.allOff())
self.pickerNP = self.cam.attachNewNode(self.pickerNode)
self.picker.addCollider(self.pickerNP, self.pickerQueue)
print(f"射线检测器 From 掩码: {fromMask}")
print(f"立方体 Into 掩码: {self.cNodePath.node().getIntoCollideMask()}")
def debugCollisionSystem(self):
"""调试碰撞系统"""
print(f"\n=== 碰撞系统调试 ===")
# 检查碰撞遍历器
print(f"碰撞遍历器中的碰撞器数量: {self.picker.getNumColliders()}")
for i in range(self.picker.getNumColliders()):
collider = self.picker.getCollider(i)
print(f" 碰撞器 {i}: {collider}")
# 检查render下的所有碰撞节点
print(f"\nrender下的所有节点:")
self.printNodeHierarchy(self.render, 0)
def printNodeHierarchy(self, node, depth):
"""打印节点层次结构"""
indent = " " * depth
print(f"{indent}{node.getName()}: {type(node.node()).__name__}")
if isinstance(node.node(), CollisionNode):
cnode = node.node()
print(f"{indent} CollisionNode:")
print(f"{indent} IntoMask: {cnode.getIntoCollideMask()}")
print(f"{indent} FromMask: {cnode.getFromCollideMask()}")
print(f"{indent} Solids: {cnode.getNumSolids()}")
for i in range(cnode.getNumSolids()):
solid = cnode.getSolid(i)
print(f"{indent} Solid {i}: {type(solid).__name__}")
# 递归处理子节点,但限制深度
if depth < 3:
for child in node.getChildren():
self.printNodeHierarchy(child, depth + 1)
def testDifferentCollisionTypes(self):
"""测试不同类型的碰撞体"""
print(f"\n=== 测试不同碰撞体类型 ===")
camera_pos = self.cam.getPos(self.render)
cube_pos = self.cube.getPos(self.render)
# 计算直接射线
direct_direction = cube_pos - camera_pos
direct_direction.normalize()
print(f"使用直接射线: 起点={camera_pos}, 方向={direct_direction}")
# 测试1: 当前设置
print(f"\n1. 测试当前碰撞球体设置:")
self.testRayHit(camera_pos, direct_direction)
# 测试2: 更大的碰撞球体
print(f"\n2. 测试更大的碰撞球体:")
# 移除当前碰撞体
self.cNodePath.removeNode()
# 创建更大的碰撞球体
cNode2 = CollisionNode('LargeSphere')
cSphere2 = CollisionSphere(Point3(0, 0, 0), 20.0) # 更大的半径
cNode2.addSolid(cSphere2)
cNode2.setIntoCollideMask(BitMask32.bit(0))
cNode2.setFromCollideMask(BitMask32.allOff())
self.cNodePath2 = self.cube.attachNewNode(cNode2)
self.cNodePath2.show()
self.testRayHit(camera_pos, direct_direction)
# 测试3: 在render根下创建独立碰撞体
print(f"\n3. 测试独立碰撞体:")
cNode3 = CollisionNode('IndependentSphere')
cSphere3 = CollisionSphere(cube_pos, 15.0) # 直接使用世界坐标
cNode3.addSolid(cSphere3)
cNode3.setIntoCollideMask(BitMask32.bit(0))
cNode3.setFromCollideMask(BitMask32.allOff())
self.cNodePath3 = self.render.attachNewNode(cNode3)
self.cNodePath3.show()
self.testRayHit(camera_pos, direct_direction)
def testRayHit(self, origin, direction):
"""测试射线碰撞"""
# 设置射线
self.pickerRay.setOrigin(origin)
self.pickerRay.setDirection(direction)
# 清除之前的结果
self.pickerQueue.clearEntries()
# 执行碰撞检测
self.picker.traverse(self.render)
numEntries = self.pickerQueue.getNumEntries()
print(f" 检测到 {numEntries} 个碰撞")
if numEntries > 0:
self.pickerQueue.sortEntries()
for i in range(numEntries):
entry = self.pickerQueue.getEntry(i)
hitPos = entry.getSurfacePoint(self.render)
hitNode = entry.getIntoNodePath()
distance = (hitPos - origin).length()
print(f" 碰撞 {i}: {hitNode.getName()}, 距离: {distance:.2f}, 位置: {hitPos}")
else:
print(f" 没有检测到碰撞")
if __name__ == "__main__":
app = CollisionDebugTest()
print("\n调试完成!")