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

Unity报错:Transform child out of bounds问题排查求助

Unity中触发Grab键调用pressedButton时出现Transform越界错误

问题概述

项目其余功能正常运行,但按下Grab键触发pressedButton函数时,弹出Transform child out of bounds错误。预期行为是获取标记为按钮的GameObject的第一个子物体(索引0的空物体,初始状态SetActive(false))并将其激活,但调用transform.GetChild(0)时触发越界异常。

相关代码

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;
using UnityEngine.InputSystem;


public class PlayerControllerV01q : MonoBehaviour
{
    [Header("Basico")]
    public Rigidbody rb;
    public PlayerInput input;


    [Header("Movement")]
    private float speed;
    public float speedMax, speedMin, accelerationSpeed;
    private float acceleration;

    private Vector3         dir;
    public float            gravity;
    public float            jumpForce;
    public float            speedRotation;
    public Transform        groundPoint;
    public bool             isGrounded;
    public LayerMask        maskGround;
    public float            airTime;
    public float            bufferAirTime;
    public float            secondsInAir;


    [Header("Actions")]
    public float            killJump;
    public float            attackArea;
    public bool             holdingItem;
    public String[]         itemTags;
    public float            pickUpArea;
    public GameObject       detectedObject;
    public float            throwForce;
    public Transform        throwPlace;
    [Header("Button")]
    public GameObject       activableButton;
    public GameObject       DetectedButtonCandidate;
    public float            activableArea;
    public String[]         buttonTags;


    public Animator anim;
    [Header("Surfaces")]
    public bool             isInwall;
    public Transform        facePoint;
    public LayerMask        maskWall;
    public Vector3          dirWall;
    
    void Start()
    {

    }

    void Update()
    {
        if (input.actions["Grab"].WasPressedThisFrame())
        {
            Debug.Log("I am trying to grab");
            Collider[] allDetects = Physics.OverlapSphere(throwPlace.transform.position, pickUpArea);

            for (int i = 0; i < allDetects.Length; i++)
            {
                GameObject detectedObjectCandidate = allDetects[i].gameObject;
                if (Array.Exists(itemTags, tag => tag == detectedObjectCandidate.tag))
                {
                    detectedObject = detectedObjectCandidate;
                    holdingItem = true;
                    break;
                }
            }
        }

        if (holdingItem == true)
        {
            Debug.Log("I am actually grabbing");
            detectedObject.transform.SetParent(transform);

            if (detectedObject.GetComponent<Rigidbody>() != null)
            {
                Rigidbody detectedObjectRB = detectedObject.GetComponent<Rigidbody>();

                if (input.actions["Throw"].WasPressedThisFrame())
                {
                    Debug.Log("I am trying to throw this");
                    detectedObject.transform.parent = null;
                    detectedObjectRB.AddForce(Vector3.forward * throwForce, ForceMode.Impulse);
                    holdingItem = false;
                }
            }
        }

        Vector2 move = input.actions["Move"].ReadValue<Vector2>();

        if(move.y != 0)
        {
            anim.SetBool("Move", true);
            anim.SetFloat("Speed", move.y);
        }
        else
        {
            anim.SetBool("Move", false);
        }
        
        if(move.magnitude != 0)
        {
            acceleration = Mathf.MoveTowards(acceleration, 1.0f, Time.deltaTime * accelerationSpeed);
        }
        else
        {
            acceleration = 0;
        }

        speed = Mathf.Lerp(speedMin, speedMax, acceleration);
        dir = transform.TransformDirection(new Vector3(0, rb.velocity.y, move.y * speed));
        
        rb.velocity = dir;
        transform.Rotate(Vector3.up * move.x * speedRotation * Time.deltaTime);

        isGrounded = Physics.OverlapSphere(groundPoint.position, 0.2f, maskGround).Length > 0;

        if (isGrounded)
        {
            anim.SetBool("Ground", true);
            airTime = 0;
            if (input.actions["Jump"].WasPressedThisFrame())
            {
                rb.velocity *= 0;
                rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
            }
        }
        else
        {
            anim.SetBool("Ground", false);
            Collider[] allDetects = Physics.OverlapSphere(groundPoint.transform.position, attackArea);
            for (int i = 0; i < allDetects.Length; i++)
            {
                if (allDetects[i].GetComponent<DestroyObject>() != null)
                {
                    Debug.Log("I am jumping higher");
                    rb.AddForce(Vector3.up * killJump * 0.8f, ForceMode.Impulse);
                    airTime = 0;
                    allDetects[i].GetComponent<DestroyObject>().GetHits();
                }
            }
            airTime += Time.deltaTime;
            if (input.actions["Jump"].IsPressed() && secondsInAir >= airTime && airTime >= bufferAirTime)
            {
                rb.AddForce(Vector3.up * jumpForce * 0.8f, ForceMode.Force);
            }
            rb.AddForce(Vector3.down * gravity, ForceMode.Force);
        }

        isInwall = Physics.OverlapSphere(facePoint.position, 0.4f, maskWall).Length > 0;

        if (isInwall)  
        {
            if (input.actions["Climb"].IsPressed())
            {
                Debug.Log("Im trying to climb");
                dir = Vector3.zero;
                dirWall = transform.TransformDirection(new Vector3(0, move.y * speed, rb.velocity.y));
                rb.velocity = dirWall;
            }
        }

        if (input.actions["Grab"].WasPressedThisFrame())
        {         
            Collider[] allDetects = Physics.OverlapSphere(transform.position, activableArea);

            for (int i = 0; i < allDetects.Length; i++)
            {
                allDetects = allDetects.Where(col => col.gameObject != gameObject).ToArray();
                if (buttonTags.Contains(allDetects[i].tag))
                {
                    DetectedButtonCandidate = allDetects[i].gameObject;
                    activableButton = DetectedButtonCandidate;
                    pressedButton();
                    break;
                }
            }
        }
    }

    public void pressedButton()
    {
        Transform activableButtonChildTransform = activableButton.transform.GetChild(0);
        if (activableButtonChildTransform != null)
        {
            GameObject activableButtonChild = activableButtonChildTransform.gameObject;
            activableButtonChild.SetActive(true);
        }
    }
}

报错信息

UnityException: Transform child out of bounds
PlayerControllerV01q.pressedButton () (at Assets/Scripts/PlayerControllerV01.cs:218)
PlayerControllerV01q.Update () (at Assets/Scripts/PlayerControllerV01.cs:209)

问题原因

  1. 目标按钮无符合要求的子物体:调用GetChild(0)时,检测到的activableButton物体没有子物体,导致索引越界。
  2. 检测循环逻辑错误:在按钮检测的循环中,遍历过程中修改了allDetects数组(allDetects = allDetects.Where(...)),这会改变数组长度,可能导致索引错位,甚至误识别物体。
  3. Grab键重复监听:代码中两次监听Grab键的按下事件,可能导致同一按键触发两种逻辑,引发冲突。

修复方案

1. 安全获取子物体

修改pressedButton方法,先判断子物体数量,避免越界:

public void pressedButton()
{
    // 先判断按钮物体是否为空
    if (activableButton == null)
    {
        Debug.LogError("activableButton未赋值");
        return;
    }

    // 检查是否存在索引0的子物体
    if (activableButton.transform.childCount > 0)
    {
        Transform activableButtonChildTransform = activableButton.transform.GetChild(0);
        GameObject activableButtonChild = activableButtonChildTransform.gameObject;
        activableButtonChild.SetActive(true);
    }
    else
    {
        Debug.LogError($"按钮物体[{activableButton.name}]没有子物体,请检查Hierarchy结构");
    }
}

2. 修正按钮检测逻辑

将数组过滤操作移到循环外,避免遍历过程中修改数组:

if (input.actions["Grab"].WasPressedThisFrame())
{         
    Collider[] allDetects = Physics.OverlapSphere(transform.position, activableArea);
    // 先过滤掉自身,再遍历
    var filteredDetects = allDetects.Where(col => col.gameObject != gameObject).ToArray();

    for (int i = 0; i < filteredDetects.Length; i++)
    {
        if (buttonTags.Contains(filteredDetects[i].tag))
        {
            DetectedButtonCandidate = filteredDetects[i].gameObject;
            activableButton = DetectedButtonCandidate;
            pressedButton();
            break;
        }
    }
}

3. 合并Grab键监听逻辑

避免同一按键触发两种冲突逻辑,优先处理按钮交互,再处理物品拾取:

if (input.actions["Grab"].WasPressedThisFrame())
{
    bool triggeredButton = false;
    // 先检测按钮
    Collider[] buttonDetects = Physics.OverlapSphere(transform.position, activableArea);
    var filteredButtonDetects = buttonDetects.Where(col => col.gameObject != gameObject).ToArray();
    for (int i = 0; i < filteredButtonDetects.Length; i++)
    {
        if (buttonTags.Contains(filteredButtonDetects[i].tag))
        {
            DetectedButtonCandidate = filteredButtonDetects[i].gameObject;
            activableButton = DetectedButtonCandidate;
            pressedButton();
            triggeredButton = true;
            break;
        }
    }

    // 未触发按钮时,再处理物品拾取
    if (!triggeredButton)
    {
        Debug.Log("I am trying to grab");
        Collider[] allDetects = Physics.OverlapSphere(throwPlace.transform.position, pickUpArea);
        for (int i = 0; i < allDetects.Length; i++)
        {
            GameObject detectedObjectCandidate = allDetects[i].gameObject;
            if (Array.Exists(itemTags, tag => tag == detectedObjectCandidate.tag))
            {
                detectedObject = detectedObjectCandidate;
                holdingItem = true;
                break;
            }
        }
    }
}

4. 验证按钮物体结构

在Unity编辑器中选中目标按钮GameObject,检查Hierarchy面板,确认它存在索引为0的子物体,且该子物体未被意外删除或禁用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 10:47:03