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

Unity3D:物体拼接与撤销功能实现问题求助

解决方案:全局单脚本统一管理物体生成与删除

问题根源

你当前的实现核心问题是每个实例化物体都挂载了Placement脚本,导致Update中的输入检测被多个脚本同时执行,触发重复生成、误删等问题。需要将分散的逻辑收拢到一个全局单脚本中,避免多脚本重复响应输入。

重构方案

我们用一个全局的ObjectManager脚本统一处理输入、生成、删除逻辑,同时用SpawnPoint组件标记每个物体的下一个拼接位置,确保生成位置正确。


步骤1:添加SpawnPoint组件(标记拼接位置)

给基础物体、Object2预制体、Object3预制体都挂载这个组件,用来指定下一个物体的生成位置:

using UnityEngine;

public class SpawnPoint : MonoBehaviour
{
    public Transform nextSpawnPosition; // 在Inspector里拖入物体上的空Transform(比如放在物体尾部)
}

步骤2:替换为全局ObjectManager脚本

删除所有物体上的Placement脚本,删除场景中的Counter脚本,创建一个空物体命名为ObjectManager,挂载以下脚本:

using UnityEngine;
using System.Collections.Generic;

public class ObjectManager : MonoBehaviour
{
    public GameObject baseObject; // 场景中的基础物体
    public GameObject object2Prefab; // 对应需求中的Object2
    public GameObject object3Prefab; // 对应需求中的Object3

    private List<GameObject> placedObjects = new List<GameObject>();
    private Transform currentSpawnPoint;

    void Start()
    {
        // 初始化获取基础物体的拼接位置
        SpawnPoint baseSpawn = baseObject.GetComponent<SpawnPoint>();
        if (baseSpawn != null)
        {
            currentSpawnPoint = baseSpawn.nextSpawnPosition;
        }
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.W))
        {
            SpawnObject(object2Prefab);
        }
        else if (Input.GetKeyDown(KeyCode.S))
        {
            SpawnObject(object3Prefab);
        }
        else if (Input.GetKeyDown(KeyCode.Q))
        {
            RemoveLastObject();
        }
    }

    // 生成物体并更新拼接位置
    private void SpawnObject(GameObject prefab)
    {
        if (currentSpawnPoint == null || prefab == null) return;

        GameObject newObject = Instantiate(prefab, currentSpawnPoint.position, currentSpawnPoint.rotation);
        placedObjects.Add(newObject);

        // 更新下一个拼接位置为新物体的SpawnPoint
        SpawnPoint newSpawn = newObject.GetComponent<SpawnPoint>();
        if (newSpawn != null)
        {
            currentSpawnPoint = newSpawn.nextSpawnPosition;
        }
    }

    // 删除最后添加的物体并回退拼接位置
    private void RemoveLastObject()
    {
        if (placedObjects.Count == 0) return;

        // 删除最后一个物体
        GameObject lastObj = placedObjects[placedObjects.Count - 1];
        placedObjects.RemoveAt(placedObjects.Count - 1);
        Destroy(lastObj);

        // 回退拼接位置到上一个物体(或基础物体)
        if (placedObjects.Count == 0)
        {
            SpawnPoint baseSpawn = baseObject.GetComponent<SpawnPoint>();
            if (baseSpawn != null)
            {
                currentSpawnPoint = baseSpawn.nextSpawnPosition;
            }
        }
        else
        {
            GameObject prevObj = placedObjects[placedObjects.Count - 1];
            SpawnPoint prevSpawn = prevObj.GetComponent<SpawnPoint>();
            if (prevSpawn != null)
            {
                currentSpawnPoint = prevSpawn.nextSpawnPosition;
            }
        }
    }
}

步骤3:配置参数

在ObjectManager的Inspector面板中:

  • 拖入场景中的基础物体到baseObject字段
  • 拖入Object2、Object3的预制体到对应的字段
  • 给每个物体(基础物体、预制体)的SpawnPoint组件指定nextSpawnPosition(比如在物体尾部创建一个空Transform作为拼接点)

效果验证

按照你给出的测试场景:

  1. 按W,W,S,W,S:生成序列为Object2 → Object2 → Object3 → Object2 → Object3
  2. 按3次Q:依次删除最后3个(Object3、Object2、Object3),剩余Object2 → Object2
  3. 再按W:在最后一个Object2的拼接位置生成新的Object2,最终序列为Object2 → Object2 → Object2

完全符合需求,同时解决了原代码的三个问题:

  • 单脚本管理输入,无重复触发
  • 每次生成仅在当前拼接位置创建一个物体
  • 删除操作仅针对最后添加的物体,无多删情况

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 21:01:04