如何在編輯器或運(yùn)行時(shí)檢測(cè)/知道對(duì)象何時(shí)已從層次結(jié)構(gòu)中刪除,并在檢查器中更新列表?

雖然有點(diǎn)長(zhǎng),但一切都是相連的。

我試圖歸檔的目標(biāo)是使層次結(jié)構(gòu)中的對(duì)象成為游戲中可交互的項(xiàng)目。

當(dāng)我右鍵單擊對(duì)象時(shí),我有一個(gè)編輯器腳本,如果我想使這個(gè)選定的對(duì)象成為可交互的項(xiàng)目,我可以在上下文菜單中選擇。一旦我將其選擇為可交互項(xiàng),它就會(huì)將對(duì)象標(biāo)記更改為可交互項(xiàng),并將對(duì)象添加到編輯器中的列表中。

列表只是為了知道并能夠看到哪些項(xiàng)目是可交互的,而不是在所有層次結(jié)構(gòu)中一直搜索它們。

問(wèn)題是,如果我意外刪除了一些對(duì)象,或者如果我想刪除它們,該怎么辦?如何更新列表?

現(xiàn)在,如果我將刪除對(duì)象,它將顯示在缺少的列表中。

編輯器腳本:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;

[CustomEditor(typeof(InteractableItems))]
public class InteractableItemsEditor : Editor
{
    private static List<GameObject> interactedobjects = new List<GameObject>();
    private static bool interacted = false;

    [MenuItem("GameObject/Make Object Interactable", false, 30)]
    public static void GeneratePickupItems()
    {
        if (Selection.gameObjects.Length > 0)
        {
            interactedobjects.Clear();

            for (int i = 0; i < Selection.gameObjects.Length; i++)
            {
                Selection.gameObjects[i].tag = "Interactable Item";

                interactedobjects.Add(Selection.gameObjects[i]);
            }

            interacted = true;
        }
    }

    private static Renderer TraverseHierarchy(Transform root, int childIndex)
    {
        Renderer renderer = new Renderer();

        if (root.childCount == 0 || root.childCount > 0)
        {
            var skinnedMesh = root.GetComponent<SkinnedMeshRenderer>();
            if (skinnedMesh) return skinnedMesh;

            var mesh = root.GetComponent<MeshRenderer>();
            if (mesh) return mesh;
        }

        if (root.childCount > 0)
        {
            foreach (Transform child in root)
            {
                // Deal with child
                if (child.GetComponent<SkinnedMeshRenderer>() != null)
                {
                    renderer = Selection.gameObjects[childIndex].GetComponentInChildren<SkinnedMeshRenderer>(true);
                }

                if (child.GetComponent<MeshRenderer>() != null)
                {
                    renderer = Selection.gameObjects[childIndex].GetComponentInChildren<MeshRenderer>(true);
                }
            }
        }

        return renderer;
    }

    private static void AddBoxCollider(Renderer renderer, int index)
    {
        Selection.gameObjects[index].AddComponent<BoxCollider>();

        var bounds = renderer.bounds;
        var size = bounds.size;
        var center = bounds.center;
        var boxCollider = Selection.gameObjects[index].GetComponent<BoxCollider>();
        size = boxCollider.transform.InverseTransformVector(size);
        center = boxCollider.transform.InverseTransformPoint(center);

        boxCollider.size = size;
        boxCollider.center = center;
    }

    InteractableItems _target;

    private ReorderableList _myList;

    public void OnEnable()
    {
        _myList = new ReorderableList(serializedObject, serializedObject.FindProperty("interactableItems"))
        {
            draggable = false,

            displayAdd = false,

            displayRemove = false,

            drawHeaderCallback = rect => EditorGUI.LabelField(rect, ""/*"My Reorderable List"*/, EditorStyles.boldLabel),

            drawElementCallback = (rect, index, isActive, isFocused) =>
            {
                if (index > _myList.serializedProperty.arraySize - 1) return;
                var element = _myList.serializedProperty.GetArrayElementAtIndex(index);

                var color = GUI.color;
                EditorGUI.BeginDisabledGroup(true);
                {
                    GUI.color = Color.green;
                    EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width - 20, EditorGUIUtility.singleLineHeight), element, GUIContent.none);
                }

                EditorGUI.EndDisabledGroup();
                GUI.color = color;
            }
        };
    }

    public override void OnInspectorGUI()
    {
        var list = serializedObject.FindProperty("interactableItems");

        serializedObject.Update();

        if (interacted)
        {
            interacted = false;
            foreach (var newEntry in interactedobjects)
            {
                // check if already contains this item
                bool alreadyPresent = false;
                for (var i = 0; i < list.arraySize; i++)
                {
                    if ((GameObject)list.GetArrayElementAtIndex(i).objectReferenceValue == newEntry)
                    {
                        alreadyPresent = true;
                        break;
                    }
                }

                if (alreadyPresent) continue;

                // Otherwise add via the serializedProperty
                list.arraySize++;
                list.GetArrayElementAtIndex(list.arraySize - 1).objectReferenceValue = newEntry;
            }
            interactedobjects.Clear();
        }

        _myList.DoLayoutList();
        serializedObject.ApplyModifiedProperties();
    }

    public object GetParent(SerializedProperty prop)
    {
        var path = prop.propertyPath.Replace(".Array.data[", "[");
        object obj = prop.serializedObject.targetObject;
        var elements = path.Split('.');
        foreach (var element in elements.Take(elements.Length - 1))
        {
            if (element.Contains("["))
            {
                var elementName = element.Substring(0, element.IndexOf("["));
                var index = Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", ""));
                obj = GetValue(obj, elementName, index);
            }
            else
            {
                obj = GetValue(obj, element);
            }
        }
        return obj;
    }

    public object GetValue(object source, string name)
    {
        if (source == null)
            return null;
        var type = source.GetType();
        var f = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
        if (f == null)
        {
            var p = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
            if (p == null)
                return null;
            return p.GetValue(source, null);
        }
        return f.GetValue(source);
    }

    public object GetValue(object source, string name, int index)
    {
        var enumerable = GetValue(source, name) as IEnumerable;
        var enm = enumerable.GetEnumerator();
        while (index-- >= 0)
            enm.MoveNext();
        return enm.Current;
    }
}

和mono腳本:

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

public class InteractableItems : MonoBehaviour
{
    public List<GameObject> interactableItems = new List<GameObject>();
}

使用mono腳本的可交互項(xiàng)目清空游戲?qū)ο罅斜憩F(xiàn)在為空:

當(dāng)我在層次中選擇一個(gè)或多個(gè)對(duì)象并右鍵單擊并選擇“使對(duì)象可交互”時(shí):

然后,我右鍵單擊多維數(shù)據(jù)集并選擇使多維數(shù)據(jù)集可交互:

現(xiàn)在,多維數(shù)據(jù)集標(biāo)記為可交互項(xiàng):

現(xiàn)在,我正在從層次結(jié)構(gòu)中刪除多維數(shù)據(jù)集,下面是它在列表中顯示的內(nèi)容:

缺少(游戲?qū)ο螅?/p>

這就是我想要處理的。如果我出于某種原因刪除了一個(gè)可交互項(xiàng),如何通過(guò)從列表中刪除該項(xiàng)來(lái)更新列表?

我可以在mono腳本中不間斷地循環(huán)列表,但這不是一個(gè)解決方案,如果列表稍微大一點(diǎn),它將導(dǎo)致性能下降。這更像是一種變通方案,而不是解決方案。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Whilefun.FPEKit;

[ExecuteInEditMode]

public class InteractableObjects : MonoBehaviour
{
    public List<GameObject> interactableObjects = new List<GameObject>();

    private void Update()
    {
        for(int i = 0; i < interactableObjects.Count; i++)
        {
            if(interactableObjects[i] == null)
            {
                interactableObjects.RemoveAt(i);
            }
        }
    }
}

最后,我有一個(gè)IK查看腳本,它有一個(gè)對(duì)象列表,我想使用這個(gè)對(duì)象,我也用這個(gè)腳本列表創(chuàng)建可交互的項(xiàng)目。因此,我需要能夠?qū)⒋藄cripy同步中的列表與InteractiableItems腳本中的列表進(jìn)行更新:

using UnityEngine;
using System;
using System.Collections;

[RequireComponent(typeof(Animator))]
public class IKControl : MonoBehaviour
{
    public Transform[] lookObj = null;
    public float weightDamping = 1.5f;

    private Animator animator;
    private Transform lastPrimaryTarget;
    private float lerpEndDistance = 0.1f;
    private float finalLookWeight = 0;
    private bool transitionToNextTarget = false;

    void Start()
    {
        animator = GetComponent<Animator>();
    }

    // Callback for calculating IK
    void OnAnimatorIK()
    {
        if (lookObj != null)
        {
            Transform primaryTarget = null;
            float closestLookWeight = 0;

            // Here we find the target which is closest (by angle) to the players view line
            foreach (Transform target in lookObj)
            {
                Vector3 lookAt = target.position - transform.position;
                lookAt.y = 0f;
                float dotProduct = Vector3.Dot(new Vector3(transform.forward.x, 0f, transform.forward.z).normalized, lookAt.normalized);
                float lookWeight = Mathf.Clamp(dotProduct, 0f, 1f);
                if (lookWeight > closestLookWeight)
                {
                    closestLookWeight = lookWeight;
                    primaryTarget = target;
                }
            }

            if (primaryTarget != null)
            {
                if ((lastPrimaryTarget != null) && (lastPrimaryTarget != primaryTarget) && (finalLookWeight > 0f))
                {
                    // Here we start a new transition because the player looks already to a target but
                    // we have found another target the player should look at
                    transitionToNextTarget = true;
                }
            }

            // The player is in a neutral look position but has found a new target
            if ((primaryTarget != null) && !transitionToNextTarget)
            {
                lastPrimaryTarget = primaryTarget;
                finalLookWeight = Mathf.Lerp(finalLookWeight, closestLookWeight, Time.deltaTime * weightDamping);
                float bodyWeight = finalLookWeight * .75f;
                animator.SetLookAtWeight(finalLookWeight, bodyWeight, 1f);
                animator.SetLookAtPosition(primaryTarget.position);
            }

            // Let the player smoothly look away from the last target to the neutral look position
            if ((primaryTarget == null && lastPrimaryTarget != null) || transitionToNextTarget)
            {
                finalLookWeight = Mathf.Lerp(finalLookWeight, 0f, Time.deltaTime * weightDamping);
                float bodyWeight = finalLookWeight * .75f;
                animator.SetLookAtWeight(finalLookWeight, bodyWeight, 1f);
                animator.SetLookAtPosition(lastPrimaryTarget.position);
                if (finalLookWeight < lerpEndDistance)
                {
                    transitionToNextTarget = false;
                    finalLookWeight = 0f;
                    lastPrimaryTarget = null;
                }
            }

        }
    }
}

這樣我就可以構(gòu)建一個(gè)小型的可交互系統(tǒng)。

? 最佳回答:

對(duì)于編輯器,您可以嘗試處理EditorApplication.hierarchyChanged事件。

但實(shí)際上,如果我需要這樣的功能,我將創(chuàng)建一個(gè)腳本并將其添加到每個(gè)可交互對(duì)象中,該對(duì)象將在其Awake()中的列表中注冊(cè),并在OnDestroy()中注銷。對(duì)我來(lái)說(shuō),這更為明顯,并且在運(yùn)行時(shí)也會(huì)起作用。

主站蜘蛛池模板: 高清一区二区三区日本久| 亚洲国产成人久久综合一区| 香蕉一区二区三区观| 无码国产精品一区二区免费虚拟VR | 亚洲AV无码国产一区二区三区| 亚洲一区精品中文字幕| eeuss鲁片一区二区三区| 日本一区二区三区在线观看 | 国产成人精品一区在线| 无码av中文一区二区三区桃花岛| 日本成人一区二区| 久久精品一区二区影院| 国产福利在线观看一区二区| 精品欧美一区二区在线观看| 精品国产乱子伦一区二区三区| 污污内射在线观看一区二区少妇 | 成人无号精品一区二区三区| 国产精品高清一区二区人妖| 一区二区三区视频在线观看| 精品国产福利第一区二区三区| 福利一区二区在线| 一区二区三区无码高清| 国产婷婷一区二区三区| 爆乳熟妇一区二区三区霸乳| 亚洲免费一区二区| 99久久国产精品免费一区二区| 久久精品无码一区二区app| 波多野结衣一区二区三区aV高清| 日韩精品一区二区三区影院| 一区二区三区免费视频观看 | 无码一区二区三区免费视频| 国产精品无码一区二区在线观 | 无码人妻精品一区二区三区蜜桃| 久久伊人精品一区二区三区| 一区二区不卡在线| 日韩精品免费一区二区三区| 影院无码人妻精品一区二区| 久久精品国产一区二区三| 精品少妇ay一区二区三区| 精品久久久久久中文字幕一区| 国产传媒一区二区三区呀|