简体   繁体   中英

How can I create a instantiate my border around a “bronze” when it is the closest and destroy the border when no longer the closest?

I've been stuck at this for a while. What I want is for my outline object to be instantiated at the location of my bronze Base game object and for it to destroy when the bronze base is no longer the closest to the player.

I'm willing to completely restart my bronze script if it means I can make this easier.

Thanks in advance!

Find Closest Bronze Script

using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;

public class FindBronze : MonoBehaviour
{
    void Update()
    {
        FindClosestBronze();
    }
    void FindClosestBronze()
    {
        float distanceToClosestBronze = Mathf.Infinity;
        Bronze closestBronze = null;
        Bronze[] allBronze = GameObject.FindObjectsOfType<Bronze>();

        foreach (Bronze currentBronze in allBronze)
        {
            float distanceToBronze = (currentBronze.transform.position - this.transform.position).sqrMagnitude;
            if (distanceToBronze < distanceToClosestBronze)
            {
                distanceToClosestBronze = distanceToBronze;
                closestBronze = currentBronze;
            }
            if (distanceToBronze > distanceToClosestBronze)
            {
                closestBronze.GetComponent<Bronze>().notSelected();
                
            }

            closestBronze.GetComponent<Bronze>().Selected();
        }
    }
}

Bronze (includes outline) script

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

public class Bronze : MonoBehaviour
{
    public bool isSelected = false;
    public Animator anim;
    [SerializeField]
    public GameObject selectedBox;
    public GameObject bronzeBase;
    private GameObject clone;
    // Update is called once per frame
    void Awake()
    {
        clone = (GameObject)Instantiate(selectedBox, bronzeBase.transform);

    }
    public void Selected()
    {
        if (!isSelected)
        {

           clone = (GameObject)Instantiate(selectedBox, bronzeBase.transform);
            isSelected = true;

        }
        
        else
        {
            Destroy(clone);
            isSelected = false;
        }
    }
    public void notSelected()
    {
       
        Destroy(selectedBox);
    }   

}

In the Bronze in notSelected you are destroying the prefab selectBox !

You probably rather wanted to destroy the clone instance.


Anyway I would suggest a few things that I would do different

  • Instead of Instantiate and Destroy all the time rather only use SetActive
  • Instead of using FindObjectOfType in Update store them in a HashSet event driven: Each Bronze instance registers and unregisters itself
  • Depends on personal taste but I would use Linq to find the closest instance

This could look somewhat like

public class Bronze : MonoBehaviour
{
    // Every instance of this component registers and unregisters itself here
    public static readonly HashSet<Bronze> Instances = new HashSet<Bronze>();

    [Header("References")]
    public Animator anim;
    [SerializeField] private GameObject selectedBox;
    [SerializeField] private GameObject bronzeBase;

    [Header("Debugging")]
    [SerializeField] bool _isSelected = false;

    private GameObject clone;

    // Have a property for the selection
    public bool IsSelected
    {
        // when something reads this property return _isSelected
        get => _isSelected;
        // This is executed everytime someone changes the value of IsSelected
        set
        {
            if(_isSelected == value) return;

            _isSelected = value;

            clone.SetActive(_isSelected);
        }
    }

    // Update is called once per frame
    void Awake()
    {
        // Instantiate already returns the type of the given prefab
        clone = Instantiate(selectedBox, bronzeBase.transform);

        // Register yourself to the alive instances
        Instances.Add(this);
    }

    private void OnDestroy ()
    {
        // Remove yourself from the Instances
        Instances.Remove(this);
    }
}

And then use it

using System.Linq;

public class FindBronze : MonoBehaviour
{
    private Bronze currentSelected;

    private void Update()
    {
        UpdateClosestBronze();
    }

    void UpdateClosestBronze()
    {
        if(Bronze.Instances.Count ==0) return;

        // This takes the instances
        // orders them by distance ascending using the sqrMagnitude
        // of the vector between the Instance and you
        // sqrMagnitude is more efficient than Vector3.Distance when you only need to compare instead of the actual distance
        // then finally it takes the first item 
        var newClosest = Bronze.Instances.OrderBy(b => (b.transform.position - transform.position).sqrMagnitude).First();

        // skip if the result is the same as last time
        if(newClosest == currentSelected) return;

        // otherwise first deselect the current selection if there is one
        if(currentSelected)
        {
            currentSelected.IsSelected = false;
        }
   
        // Then set the new selection     
        currentSelected = newSelected;
        currentSelected.IsSelected = true;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM