Ursina Engine线条与立方体碰撞检测不全问题求助
问题分析与解决方案
核心问题
Ursina引擎的raycast()方法默认只返回第一个碰撞到的实体,而你的需求是检测射线穿过的所有闪烁体,因此必须启用return_all=True参数,否则只会记录首个碰撞,甚至会出现明显穿过物体却未检测到碰撞的情况。
此外还有两个次要逻辑问题:
create_scintillator中判断闪烁体是否存在的代码参数顺序错误:isinstance(Scintillator, x)应改为isinstance(x, Scintillator),否则永远无法检测到已存在的闪烁体。calculate_collisions中多个if语句未使用elif,会导致重复判断(比如x=4时会执行所有x<=4的if块,虽颜色会被最后一个覆盖,但逻辑不严谨)。
修正后的代码
from ursina import * import numpy as np # USEFUL VARIABLES SCINTILLATOR_SIZE_X = 0.5 SCINTILLATOR_SIZE_Y = 0.5 SCINTILLATOR_SIZE_Z = 0.0254 # 1 inch objects = [] VEC0_3D = Vec3(0, 0, 0) # USEFUL FUNCTIONS def draw_wireframe_cube(pos, size, color): cube = Entity(model='wireframe_cube', world_position=pos, scale=size, color=color, mode='line') return cube def size_of_vec(vec): """ Returns the size of a given vector :param vec: 3D vector :return: Size of 3D vector "vec" """ return np.sqrt((vec.x ** 2 + vec.y ** 2 + vec.z ** 2)) def check_muons_collisions(muons): hit_entities = [] for muon in muons: hit_entities.append(muon.calculate_collisions()) return hit_entities # SCINTILLATOR (IS A CUBOID) CLASS class Scintillator(Entity): def __init__(self, level, pos, size, rotation, mesh_wire): self.pos = pos self.size = size self.rotation = rotation self.mesh_wire = mesh_wire Entity.__init__(self, scale=size, position=pos, model='cube', collider='box', color=color.pink) # CREATE SCINTILLATOR METHOD def create_scintillator(num_of_scints): """ This function will create the main scintillator :return: Returns the scintillator object """ # 修正isinstance参数顺序 scintillator_ref = [x for x in objects if isinstance(x, Scintillator)] if len(scintillator_ref) > 0: print("A scintillator is already defined!") return scint_size = Vec3(SCINTILLATOR_SIZE_X, SCINTILLATOR_SIZE_Y, SCINTILLATOR_SIZE_Z) # set in settings scint_rot = VEC0_3D # in degrees for i in range(num_of_scints): scint_pos = Vec3(0, 0, -num_of_scints / 2 + 0.5) * 0.1 + Vec3(0, 0, 1) * 0.1 * i # in meters wireframe = draw_wireframe_cube(scint_pos, scint_size * 1.01, color=color.black) scint = Scintillator(i, scint_pos, scint_size, scint_rot, wireframe) wireframe.collider = None wireframe.parent = scint # 修正:将每个闪烁体都加入objects列表 objects.append(scint) return scint # MUON (IS A LINE) CLASS class Muon(Entity): """ Object class for muons """ def __init__(self, index, start, end, energy): """ Initialization function for a muon :param start: Starting point :param end: End point :param energy: Energy, used for calculating scintillation. :param mesh: Mesh reference. """ self.start = start self.end = end self.energy = energy self.index = index self.hit_info = None Entity.__init__(self, model=Mesh(vertices=[start, end], mode='line', thickness=0.1), color=color.green) def calculate_collisions(self): # 关键修正:添加return_all=True,获取所有碰撞实体 self.hit_info = raycast( self.start, self.end - self.start, distance=size_of_vec(self.end - self.start), debug=True, return_all=True ) # 去重收集所有碰撞实体 entities = [] for hit in self.hit_info: if hit.entity not in entities: entities.append(hit.entity) x = len(entities) # 修正为elif,避免重复判断 if x == 0: self.color = color.cyan elif x == 1: self.color = color.brown elif x == 2: self.color = color.red elif x == 3: self.color = color.orange elif x == 4: self.color = color.gold elif x == 5: self.color = color.black for entity in entities: if isinstance(entity, Scintillator): # 匹配对应实体的碰撞点 hit_point = next(hit.point for hit in self.hit_info if hit.entity == entity) self.print_collision(entity, hit_point) return entities def print_collision(self, entity, position): print("*************************") print(f"Muon {self.index} hit {entity} at {position}") print("*************************") def create_n_random_muons(n): """ This function will create and print n muons of varying energies and angels :param n: Number of muons :return: Array of all muons """ muons = [] # This hold the new n muons # This holds calculated start points. I calculated some random ones for you start_points = [Vec3(-142.749, -219.022, 999.763), Vec3(-143.519, 108.823, 999.763), Vec3(-393.619, -104.295, 999.763), Vec3(1212.26, 570.423, 999.763), Vec3(-51.1669, 35.664, 999.763), Vec3(323.465, -27.7195, 999.763), Vec3(245.713, 183.488, 999.763), Vec3(363.892, 647.235, 999.763), Vec3(201.392, 574.155, 999.763), Vec3(-253.416, -743.39, 999.763), Vec3(171.414, 198.996, 999.763), Vec3(-11.5856, 12.0816, 999.763), Vec3(-46.2548, 257.973, 999.763), Vec3(761.947, -622.162, 999.763), Vec3(-496.811, -295.885, 999.763)] # This holds calculated end points. I calculated some for you end_points = [Vec3(-0.00927782, -0.0904079, -0.2373), Vec3(-0.149831, -0.241173, -0.2373), Vec3(-0.0478061, 0.051418, -0.2373), Vec3(0.17725, -0.0356445, -0.2373), Vec3(0.217475, -0.225569, -0.2373), Vec3(0.188968, -0.064771, -0.2373), Vec3(-0.19695, -0.0399446, -0.2373), Vec3(-0.154698, -0.141071, -0.2373), Vec3(0.0940816, -0.133347, -0.2373), Vec3(0.144424, 0.223723, -0.2373), Vec3(-0.161474, 0.0230457, -0.2373), Vec3(-0.0304296, -0.152948, -0.2373), Vec3(-0.00892861, 0.0107271, -0.2373), Vec3(0.138, -0.196602, -0.2373), Vec3(-0.216395, -0.0713366, -0.2373) ] # This holds randomized end points # For loop to create n muons for i in range(0, n): # Create n end vectors for Muons muon_i = Muon(i, start_points[i], end_points[i], 1) objects.append(muon_i) muons.append(muon_i) return muons def main(argv): app = Ursina(title='example') scints = create_scintillator(4) # NUMBER_OF_SCINTS = 4 muons = create_n_random_muons(10) check_muons_collisions(muons) EditorCamera() app.run() if __name__ == "__main__": import sys main(sys.argv)
关键修正点说明
- 射线检测返回所有碰撞:在
raycast中添加return_all=True,此时返回值为包含所有碰撞信息的列表,可遍历获取所有被穿过的实体。 - 修复闪烁体列表添加逻辑:原代码仅将最后一个闪烁体加入
objects列表,现在每个闪烁体都被添加,确保所有碰撞体都能被检测到。 - 修正类型判断逻辑:调整
isinstance参数顺序,正确判断对象是否为Scintillator类型。 - 优化碰撞次数判断:将多个独立
if改为elif,避免重复执行颜色赋值逻辑。
修改后,所有穿过多个闪烁体的射线都能正确检测到碰撞次数,线条颜色也会对应显示。
内容的提问来源于stack exchange,提问作者the-friendly-dude
相关产品推荐
相关产品推荐

