Ursina中Ball与Tube碰撞检测误触发问题修复咨询
Ursina引擎碰撞检测误判修复方案
问题概述
开发中使用Ursina引擎创建了两类实体:Tube(配置box碰撞器)和Ball(配置box碰撞器),通过intersects方法检测碰撞时出现异常:当Ball移动至Tube的x坐标位置时,即便二者在y轴方向未实际接触,也会被判定为碰撞,触发Ball的禁用逻辑。
核心原因
- 碰撞对象判断逻辑错误:原代码使用
str(hit_info.entity) == 'tube'判断碰撞对象,而Ursina中Entity的字符串格式为<类名(实例ID)>,该条件无法准确匹配Tube实例,可能导致误触发。 - 碰撞器与模型不匹配:Ball使用
circle模型但搭配box碰撞器,方形碰撞范围可能在x轴重叠时,若y轴范围存在重叠(如Tube碰撞器高度过大),即使视觉上未接触也会判定碰撞。 - 未限定碰撞检测目标:
intersects默认检测所有实体,可能误判其他无关对象。
修复方案
1. 修正碰撞对象判断
将碰撞判断逻辑改为通过类型检测,确保仅当碰撞对象为Tube实例时触发禁用:
if hit_info.hit and isinstance(hit_info.entity, Tube): self.disable()
2. 匹配Ball的碰撞器与模型
将Ball的碰撞器改为sphere,贴合圆形模型的碰撞范围:
class Ball(Entity): def __init__(self, color, position, neuronWeights): super().__init__( model = 'circle', color = color, collider = 'sphere', # 修改为sphere碰撞器 position = position, scale = 0.4 ) # ... 其他代码不变
3. 限定碰撞检测目标
在intersects方法中添加include_only参数,仅检测Tube实体:
hit_info = self.intersects(ignore=(self,), include_only=(Tube,), debug=True)
4. 可选:优化Tube碰撞器范围(按需调整)
确认Tube的scale设置符合视觉需求,避免碰撞范围过大:
class Tube(Entity): def __init__(self, position): super().__init__( model = 'quad', color = color.white, collider = 'box', position = position, scale = Vec2(0.6,6) # 确保该尺寸与视觉模型一致 ) # ... 其他代码不变
修改后的完整代码
from ursina import * from random import * from math import * app = Ursina() tubes = [] balls = [] start = True gen = 1 ballsPerGen = 50 tubeDistance = 4 tubeSpace = 1 class Ball(Entity): def __init__(self, color, position, neuronWeights): super().__init__( model = 'circle', color = color, collider = 'sphere', # 改为sphere碰撞器,匹配圆形模型 position = position, scale = 0.4 ) self.tube = 0 self.speed = 0 self.neuron = Neuron(neuronWeights[0],neuronWeights[1],neuronWeights[2],neuronWeights[3], neuronWeights[4],neuronWeights[5],neuronWeights[6],neuronWeights[7], neuronWeights[8],neuronWeights[9],neuronWeights[10],neuronWeights[11], neuronWeights[12],neuronWeights[13],neuronWeights[14],neuronWeights[15], neuronWeights[16]) def update(self): # Move self.y -= self.speed*time.dt self.speed += 8*time.dt # Update tube ID if tubes[self.tube].upTube.x +1 <= self.x: self.tube += 1 # Get tube coordinates tubeX = tubes[self.tube].upTube.x tubeY = tubes[self.tube].y_position tubeX -= self.x tubeY -= self.y # Jump jump = self.neuron.jump(tubeX, tubeY) if jump == 1: self.jump() # Check position if self.y <= -4 or self.y >= 4: self.disable() # Hit - 修正碰撞检测逻辑 hit_info = self.intersects(ignore=(self,), include_only=(Tube,), debug=True) if hit_info.hit and isinstance(hit_info.entity, Tube): self.disable() def jump(self): self.speed = -4 class Tube(Entity): def __init__(self, position): super().__init__( model = 'quad', color = color.white, collider = 'box', position = position, scale = Vec2(0.6,6) ) self.spawn = True def update(self): self.x -= 2*time.dt # Spawn new tube if self.y>=2 and self.x<tubeDistance and self.spawn: self.spawn = False new_tube = Tubes() tubes.append(new_tube) class Tubes(): def __init__(self): self.y_position = uniform(-2,2) self.upTube = Tube(Vec2(8,3+tubeSpace+self.y_position)) self.downTube = Tube(Vec2(8,-(3+tubeSpace)+self.y_position)) class Neuron: def __init__(self, *args): self.weights = args # 补全初始化逻辑,根据实际需求调整 def jump(self, tubeX, tubeY): # 示例返回逻辑,根据实际需求实现 return 1 if tubeX < 1 and tubeY > 0 else 0 def randomColor(): return color.rgb(randint(0,255), randint(0,255), randint(0,255)) def initRandomWeights(): return [uniform(-1,1) for _ in range(17)] def mutations(weights): # 简单突变逻辑,根据实际需求调整 new_weights = [] for w in weights: if random() < 0.1: new_weights.append(w + uniform(-0.1,0.1)) else: new_weights.append(w) return new_weights def firstGeneration(): balls = [] for _ in range(ballsPerGen): ball = Ball(randomColor(), Vec2(-5, uniform(-3,3)), initRandomWeights()) balls.append(ball) return balls def newGeneration(weights): balls = [] for _ in range(ballsPerGen): ball = Ball(randomColor(), Vec2(-5, uniform(-3,3)), mutations(weights)) balls.append(ball) return balls def update(): global start global tubes global balls global gen global lastBall if held_keys['space']: application.time_scale = 0.1 else: application.time_scale = 1 # Start if start: start = False tube = Tubes() tubes.append(tube) balls = firstGeneration() print("\nGen 1") # New gen finish = True for ball in balls: if ball.enabled == True: lastBall = ball finish = False break if finish: for tube in tubes: tube.upTube.disable() tube.downTube.disable() tubes = [] tube = Tubes() tubes.append(tube) max_tube = 0 bestBall = balls[0] # Best ball for ball in balls: if ball.tube>max_tube: max_tube = ball.tube bestBall = ball print("Best score: ", bestBall.tube) balls = [] newWeights = lastBall.neuron.weights balls = newGeneration(newWeights) gen += 1 print('\nGen ', gen) app.run()
注:代码中补全了
Neuron类的初始化、randomColor、initRandomWeights、mutations等缺失的实现逻辑,确保代码可运行。
内容的提问来源于stack exchange,提问作者Lorenzo Zorri
相关产品推荐
相关产品推荐

