在与对象(项)交互时,如何创建一些层次结构



这是一个可交互项目脚本,我将它附加到我想要交互的对象:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using System;
public class InteractableItem : MonoBehaviour
{
public enum InteractableMode
{
Description,
Action,
ActionWithoutThrow
};
public InteractableMode interactableMode = InteractableMode.Description;
public float distance;
[TextArea(1, 10)]
public string description = "";
public bool IsAnyAction()
{
return interactableMode == InteractableMode.ActionWithoutThrow || interactableMode == InteractableMode.Action;
}
}

此脚本正在使用InteractiableItem脚本:

using UnityEngine;
using System;
using System.Collections;
using UnityEngine.UI;
using System.Collections.Generic;
[RequireComponent(typeof(Animator))]
public class IKControl : MonoBehaviour
{
public List<InteractableItem> lookObj = new List<InteractableItem>();
public GameObject objToThrow;
public Text text;
public float weightDamping = 1.5f;
public bool RightHandToTarget = true;
public float throwSpeed;
public bool handFinishedMove = false;
private List<InteractableItem> allDetectedItems;
private Animator animator;
private InteractableItem lastPrimaryTarget;
private float lerpEndDistance = 0.1f;
private float finalLookWeight = 0;
private bool transitionToNextTarget = false;
void Start()
{
animator = GetComponent<Animator>();
allDetectedItems = new List<InteractableItem>();
}
// Callback for calculating IK
void OnAnimatorIK()
{
if (lookObj != null)
{
lookObj.RemoveAll(x => x == null);
InteractableItem primaryTarget = null;

float closestLookWeight = 0;
// Here we find the target which is closest (by angle) to the players view line
allDetectedItems.Clear();
foreach (InteractableItem target in lookObj)
{
Vector3 lookAt = target.transform.position - transform.position;
lookAt.y = 0f;
// Filter out all objects that are too far away
if (lookAt.magnitude > target.distance) continue;
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 > 0.1f && lookWeight > closestLookWeight)
{
closestLookWeight = lookWeight;
primaryTarget = target;
allDetectedItems.Add(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)
{
if(primaryTarget.interactableMode == InteractableItem.InteractableMode.ActionWithoutThrow)
{
RightHandToTarget = true;
}
lastPrimaryTarget = primaryTarget;
//finalLookWeight = Mathf.Lerp(finalLookWeight, closestLookWeight, Time.deltaTime * weightDamping);
finalLookWeight = Mathf.Lerp(finalLookWeight, 1f, Time.deltaTime * weightDamping);
float bodyWeight = finalLookWeight * .75f;
animator.SetLookAtWeight(finalLookWeight, bodyWeight, 1f);
animator.SetLookAtPosition(primaryTarget.transform.position);
if (RightHandToTarget && primaryTarget.IsAnyAction())
{
Vector3 relativePos = primaryTarget.transform.position - transform.position;
Quaternion rotationtoTarget = Quaternion.LookRotation(relativePos, Vector3.up);
animator.SetIKRotationWeight(AvatarIKGoal.RightHand, finalLookWeight);
animator.SetIKRotation(AvatarIKGoal.RightHand, rotationtoTarget);
animator.SetIKPositionWeight(AvatarIKGoal.RightHand, finalLookWeight * 1f * closestLookWeight);
animator.SetIKPosition(AvatarIKGoal.RightHand, primaryTarget.transform.position);
// -> new code block
if (finalLookWeight > 0.95f) // here you can play with a value between 0.95f -> 1.0f
{

if(primaryTarget.interactableMode == InteractableItem.InteractableMode.Action)
// call your funtion to shoot something here
StartCoroutine(ThrowObject(objToThrow.transform, primaryTarget.transform.position, 30f));
}
if(finalLookWeight > 0.9f)
{
handFinishedMove = true;
}
}
}
// 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.transform.position);
if (RightHandToTarget)
{
Vector3 relativePos = lastPrimaryTarget.transform.position - transform.position;
Quaternion rotationtoTarget = Quaternion.LookRotation(relativePos, Vector3.up);
animator.SetIKRotationWeight(AvatarIKGoal.RightHand, finalLookWeight);
animator.SetIKRotation(AvatarIKGoal.RightHand, rotationtoTarget);
animator.SetIKPositionWeight(AvatarIKGoal.RightHand, finalLookWeight * 0.5f * closestLookWeight);
animator.SetIKPosition(AvatarIKGoal.RightHand, lastPrimaryTarget.transform.position);
}
if (finalLookWeight < lerpEndDistance)
{
transitionToNextTarget = false;
finalLookWeight = 0f;
lastPrimaryTarget = null;
}
}
// Show primary object found by the player
if (primaryTarget != null)
{
text.text = primaryTarget.description;
}
else
{
text.text = "";
}
}
}
IEnumerator ThrowObject(Transform objectToMove, Vector3 toPosition, float duration)
{
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
Vector3 currentPos = objectToMove.position;
float time = Vector3.Distance(currentPos, toPosition) / (duration - counter) * Time.deltaTime;
objectToMove.position = Vector3.MoveTowards(currentPos, toPosition, time);
yield return null;
}
}
}

问题是,当我在游戏中有两个非常靠近的对象(项目(时,例如,它们之间的距离是1或小于1。然后,IKControl将与第一个可交互项目一起工作,但永远不会与第二个接近可交互的项目一起工作。

例如,如果有两个立方体,它们之间的距离为1或小于1,或者即使它们之间的间距为100,哪一个先相互作用?Andi如果List lookObj包含20个项目,我想首先与项目15交互,然后是1,然后是5,然后是19?

如何设置与项目交互的顺序?

我想以某种方式使用可重新排序列表,但不确定这是否是解决方案以及如何做到

现在,我做了一些丑陋的工作,但很丑陋:我销毁了一个项目的脚本,并添加到另一个项目以创建一些订单:

Destroy(securityKeyPad.GetComponent<InteractableItem>());
StartCoroutine(DestroyKeyPad1());
var naviInteractable = navi.AddComponent<InteractableItem>();
naviInteractable.distance = 0.5f;
navi.GetComponent<InteractableItem>().interactableMode = InteractableItem.InteractableMode.ActionWithoutThrow;

但是第二个项目还没有添加到lookObj中,所以这是另一个问题。

我想制作一些编辑器脚本,如果我将InteractiableItem脚本附加到对象,它将在运行游戏之前自动将其添加到编辑器中IKControl脚本中的lookObj列表中!

然后在lookObj列表中可以控制项目的可交互顺序,这就是为什么我还想使用可重新排序列表,而不是只使用列表。

在这一点上我有点不知所措。

听起来你想根据某种标准选择一个特定的目标,比如"lookWeight";你在foreach循环中计算。我建议创建一个额外的";lookWeight";InteractiableItem:中的字段

public class InteractableItem : MonoBehaviour
{
//...
public float lookWeight;
}

然后你就可以保存这个";lookWeight";在foreach循环中,然后在循环结束后对其进行排序。包含Linq命名空间以支持排序:

using System.Linq;
foreach (InteractableItem target in lookObj)
{
//...
target.lookWeight = lookWeight;
allDetectedItems.Add(target);
}
// put into ascending order by look weight and set target to first item
if (allDetectedItems.Count > 0)
{
allDetectedItems = allDetectedItems.OrderBy(x => x.lookWeight).ToList();
primaryTarget = allDetectedItems.First();
}
if (primaryTarget != null)
//...

如果你想订购其他东西,而不是";lookWeight";,只需将其添加到InteractiableItem类即可。

最新更新