Уничтожение клона сборного с помощью кнопки, которая порождается другой кнопкой

#c# #unity3d

#c# #unity3d

Вопрос:

Я работаю над проектом Unity, в котором 3D-объекты, такие как куб, Сфера и цилиндр, появляются в центре экрана при выборе из выпадающего списка кнопок. Сценарий создания экземпляра работает нормально, но все 3 объекта появляются и сливаются друг с другом. Ожидается, что при создании экземпляра одного из них будут уничтожены 2 других. Проблема, с которой я сталкиваюсь, заключается в том, что созданный объект является клоном, и я не могу заставить его быть уничтоженным. Я новичок в Unity и пытаюсь учиться. Я вставил приведенный ниже код, я создал 3 из них для каждой кнопки и изменил параметры в соответствии с объектом.

 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Cube_Inst : MonoBehaviour
{
    public GameObject box;
    public Transform pos;
    public bool trigger;
    public Button yourButton;
    public GameObject Sphere_Destroy;
    public GameObject Cylinder_Destroy;



    void Start()
    {
        Button btn = yourButton.GetComponent<Button>();
        btn.onClick.AddListener(TaskOnClick);
    }
    void TaskOnClick()
    {
        trigger = true;
        Destroy(Sphere_Destroy);
        Destroy(Cylinder_Destroy);
    }

    public void Update()
    {
        if (trigger == true)
        {
            Instantiate(box, pos.position, pos.rotation);
            trigger = false;
        }
    }

}
  

Ответ №1:

Вы должны скорее реализовать все централизованно («Шаблон единой ответственности»).

Не позволяйте отдельным кнопкам иметь отдельные скрипты с множеством перекрестных ссылок между ними.

Лучше иметь один компонент центрального контроллера и позволять кнопкам передавать разные параметры в общий метод создания.

Также вы должны делать это на основе событий — вы уже знаете момент нажатия кнопки — вместо опроса, проверяющего флаг в Update методе. Я бы использовал что-то вроде

 // Put this on ONE object that is always active in the Scene
public class SpawnController : MonoBehaviour
{
    private GameObject _currentClone;
    private GameObject _currentPrefab;

    public void SetInstance(GameObject prefab, Vector3 position, Quaternion rotation)
    {
        // Was the same prefab passed again? -> Ignore
        if(prefab == _currentPrefab) return;

        _currentPrefab = prefab;

        // Already exists a clone? -> Destroy it
        if(_currentClone) Destroy(_currentClone);

        // Create and store the new clone
        _currentClone = Instantiate (prefab, position, rotation);
    }
}
  

Поэтому, если SetInstance вызывается, он автоматически уничтожает любой уже существующий экземпляр.

А затем иметь один и тот же компонент на каждой кнопке, но только настраивать их соответствующим образом

 public class SpawnButton : MonoBehaviour
{
    // Already reference this via the Inspector if possible
    // If not we will get it on runtime as fallback
    [SerializeField] private Button _button; 

    // For each different button reference a different prefab
    [SerializeField] private GameObject _prefab;  
    // Reference the spawn point
    [SerializeField] private Transform _spawnPoint;

    // Here drag in the SpawnController from the scene if possible
    // if not, we will find it on runtime as fallback
    [SerializeField] private SpawnController _spawnController;

    private void Awake()
    {
        if(!_button) _button = Get component<Button>();

        _button.onClick.AddListener(DoSpawn());

        if(!_spawnController) _spawnController = FindObjectOfType<SpawnController>();
    }

    private void DoSpawn()
    {
        // Only tell the SpawnController to do its thing
        // This button doesn't have to know or care what that means for the Scene

        _spawnController.SetInstance(_prefab, _spawnPoint.position, _spawnPoint.rotation);
    }
}
  

Вы могли бы даже сделать шаг дальше и вообще не сообщать кнопкам о SpawnController и его секретном SetInstance методе, а скорее сделать что-то вроде

 public class SpawnController : MonoBehaviour
{
    private GameObject _currentClone;
    private Button _lastClickedButton;

    private void Awake()
    {
        SpawnButton.OnSpawnButtonClicked  = SetInstance;
    }

    private void SetInstance(Button button, GameObject prefab, Vector3 position, Quaternion rotation)
    {
        // Was the same button pressed again? -> Ignore
        if(button == _lastClickedButton) return;

        _lastClickedButton = button;

        // Already exists a clone? -> Destroy it
        if(_currentClone) Destroy(_currentClone);

        // Create and store the new clone
        _currentClone = Instantiate (prefab, position, rotation);
    }
}
  

и затем

 public class SpawnButton : MonoBehaviour
{
    public static event Action<Button, GameObject, Vector3, Quaternion> OnSpawnButtonClicked;

    // Already reference this via the Inspector if possible
    // If not we will get it on runtime as fallback
    [SerializeField] private Button _button; 

    // For each different button reference a different prefab
    [SerializeField] private GameObject _prefab;  
    // Reference the spawn point
    [SerializeField] private Transform _spawnPoint;

    private void Awake()
    {
        if(!_button) _button = Get component<Button>();

        _button.onClick.AddListener(DoSpawn());
    }

    private void DoSpawn()
    {
        // Only Invoke the event
        // You don't care if or who is listening
        OnSpawnButtonClicked?.Invoke(this, _prefab, _spawnPoint.position, _spawnPoint.rotation);
    }
}
  

Комментарии:

1. Большое вам спасибо за это. Действительно ценится