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作为拼接点)
效果验证
按照你给出的测试场景:
- 按
W,W,S,W,S:生成序列为Object2 → Object2 → Object3 → Object2 → Object3 - 按3次
Q:依次删除最后3个(Object3、Object2、Object3),剩余Object2 → Object2 - 再按
W:在最后一个Object2的拼接位置生成新的Object2,最终序列为Object2 → Object2 → Object2
完全符合需求,同时解决了原代码的三个问题:
- 单脚本管理输入,无重复触发
- 每次生成仅在当前拼接位置创建一个物体
- 删除操作仅针对最后添加的物体,无多删情况
内容的提问来源于stack exchange,提问作者Asfiris
相关产品推荐
相关产品推荐

