You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ursina中Ball与Tube碰撞检测误触发问题修复咨询

Ursina引擎碰撞检测误判修复方案

问题概述

开发中使用Ursina引擎创建了两类实体:Tube(配置box碰撞器)和Ball(配置box碰撞器),通过intersects方法检测碰撞时出现异常:当Ball移动至Tube的x坐标位置时,即便二者在y轴方向未实际接触,也会被判定为碰撞,触发Ball的禁用逻辑。

核心原因

  1. 碰撞对象判断逻辑错误:原代码使用str(hit_info.entity) == 'tube'判断碰撞对象,而Ursina中Entity的字符串格式为<类名(实例ID)>,该条件无法准确匹配Tube实例,可能导致误触发。
  2. 碰撞器与模型不匹配:Ball使用circle模型但搭配box碰撞器,方形碰撞范围可能在x轴重叠时,若y轴范围存在重叠(如Tube碰撞器高度过大),即使视觉上未接触也会判定碰撞。
  3. 未限定碰撞检测目标: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 12:20:54