雖然有點(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ì)起作用。