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

Unity中随机生成目标边界与布娃娃碰撞穿模问题求助

问题描述

我正在使用ML-Agents库,尝试为随机生成的目标对象创建边界。已完成边界制作并修改脚本使其与目标同位置生成,但该边界经常与ragdoll(布娃娃)对象重叠穿模,导致布娃娃无法移动。我是Unity新手,欢迎提供任何基础修复方案。

已尝试的方法
  • 添加rigidbody(刚体):边界出现严重问题,穿模仍存在
  • 调整碰撞设置:对穿模问题毫无作用
  • 计算目标与布娃娃的距离并重新生成随机位置:无效果
  • 添加计时器触发位置重新生成:边界的四个光束开始疯狂瞬移
目标控制器脚本
using UnityEngine;
using Random = UnityEngine.Random;
using Unity.MLAgents;
using UnityEngine.Events;

namespace Unity.MLAgentsExamples
{
    /// <summary>
    /// Utility class to allow target placement and collision detection with an agent
    /// Add this script to the target you want the agent to touch.
    /// Callbacks will be triggered any time the target is touched with a collider tagged as 'tagToDetect'
    /// </summary>
    public class TargetController : MonoBehaviour
    {

        [Header("Collider Tag To Detect")]
        public string tagToDetect = "agent"; //collider tag to detect

        [Header("Target Placement")]
        public float spawnRadius; //The radius in which a target can be randomly spawned.
        public bool respawnIfTouched; //Should the target respawn to a different position when touched

        [Header("Target Fell Protection")]
        public bool respawnIfFallsOffPlatform = true; //If the target falls off the platform, reset the position.
        public float fallDistance = 5; //distance below the starting height that will trigger a respawn

        private Vector3 m_startingPos; //the starting position of the target

        [System.Serializable]
        public class TriggerEvent : UnityEvent<Collider>
        {
        }

        [Header("Trigger Callbacks")]
        public TriggerEvent onTriggerEnterEvent = new TriggerEvent();
        public TriggerEvent onTriggerStayEvent = new TriggerEvent();
        public TriggerEvent onTriggerExitEvent = new TriggerEvent();

        [System.Serializable]
        public class CollisionEvent : UnityEvent<Collision>
        {
        }

        [Header("Collision Callbacks")]
        public CollisionEvent onCollisionEnterEvent = new CollisionEvent();
        public CollisionEvent onCollisionStayEvent = new CollisionEvent();
        public CollisionEvent onCollisionExitEvent = new CollisionEvent();
        public GameObject boundary;
        public GameObject ragdoll;
        
        // Start is called before the first frame update
        void OnEnable()
        {
            m_startingPos = transform.position;
            if (respawnIfTouched)
            {
                MoveTargetToRandomPosition();
            }
        }
        
        void Update()
        {
            if (respawnIfFallsOffPlatform)
            {
                if (transform.position.y < m_startingPos.y - fallDistance)
                {
                    Debug.Log($"{transform.name} Fell Off Platform");
                    MoveTargetToRandomPosition();
                }
            }
        }

        /// <summary>
        /// Moves target to a random position within specified radius.
        /// </summary>
        public void MoveTargetToRandomPosition()
        {
            var newTargetPos = m_startingPos + (Random.insideUnitSphere * spawnRadius);
            float dis = Vector3.Distance(newTargetPos, ragdoll.transform.position);
            while (dis < 10){
                newTargetPos = m_startingPos + (Random.insideUnitSphere * spawnRadius);
                dis = Vector3.Distance(newTargetPos, ragdoll.transform.position);
            }
            newTargetPos.y = m_startingPos.y;
            transform.position = newTargetPos;
            boundary.transform.position = newTargetPos + new Vector3(0f, -0.75f,-2f);
        }

        private void OnCollisionEnter(Collision col)
        {
            if (col.transform.CompareTag(tagToDetect))
            {
                onCollisionEnterEvent.Invoke(col);
                if (respawnIfTouched)
                {
                    MoveTargetToRandomPosition();
                }
            }
        }

        private void OnCollisionStay(Collision col)
        {
            if (col.transform.CompareTag(tagToDetect))
            {
                onCollisionStayEvent.Invoke(col);
            }
        }

        private void OnCollisionExit(Collision col)
        {
            if (col.transform.CompareTag(tagToDetect))
            {
                onCollisionExitEvent.Invoke(col);
            }
        }

        private void OnTriggerEnter(Collider col)
        {
            if (col.CompareTag(tagToDetect))
            {
                onTriggerEnterEvent.Invoke(col);
            }
        }

        private void OnTriggerStay(Collider col)
        {
            if (col.CompareTag(tagToDetect))
            {
                onTriggerStayEvent.Invoke(col);
            }
        }

        private void OnTriggerExit(Collider col)
        {
            if (col.CompareTag(tagToDetect))
            {
                onTriggerExitEvent.Invoke(col);
            }
        }
    }
}
基础修复方案

1. 修正距离判断逻辑

你当前只检查了目标根节点和布娃娃的距离,但边界是偏移生成的,可能目标离得远,但边界还是和布娃娃重叠。把边界的最终位置纳入距离计算:

public void MoveTargetToRandomPosition()
{
    Vector3 newTargetPos;
    Vector3 newBoundaryPos;
    float dis;
    do
    {
        newTargetPos = m_startingPos + (Random.insideUnitSphere * spawnRadius);
        newTargetPos.y = m_startingPos.y;
        newBoundaryPos = newTargetPos + new Vector3(0f, -0.75f,-2f);
        // 计算边界和布娃娃的距离,也可以用布娃娃碰撞体的包围盒中心更准确
        dis = Vector3.Distance(newBoundaryPos, ragdoll.transform.position);
        // 根据边界实际大小调整阈值,比如边界尺寸是5,就设为5+2的安全距离
    } while (dis < 12);

    transform.position = newTargetPos;
    boundary.transform.position = newBoundaryPos;
}

2. 给边界设置正确的碰撞体

  • 不要给边界加Rigidbody,把边界碰撞体的Is Trigger设为false。
  • 在Edit > Project Settings > Physics里,给布娃娃和边界分别设置专属层(比如"Ragdoll"和"Boundary"),并开启这两层的碰撞交互。
  • 如果边界是多个光束组成,确保每个光束都有贴合模型的碰撞体。

3. 解决边界瞬移问题

计时器触发瞬移是因为重复调用了移动方法,给移动逻辑加冷却限制:

private float respawnCooldown = 2f;
private float lastRespawnTime;

public void MoveTargetToRandomPosition()
{
    if (Time.time - lastRespawnTime < respawnCooldown) return;
    lastRespawnTime = Time.time;

    // 这里放原来的位置计算逻辑
}

4. 检查布娃娃的碰撞配置

  • 确保布娃娃每个肢体的CharacterJoint/ConfigurableJoint设置正确,避免僵硬卡死。
  • 布娃娃的Rigidbody要设为Kinematic = false,需要重力的话开启Use Gravity。

内容的提问来源于stack exchange,提问作者Zelix75

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 06:34:54