与Unity中的游戏对象互动

时间:2018-09-29 16:42:10

标签: c# unity3d

我想与Unity中的游戏对象互动。这些对象可能是钥匙,门,箱子,...

比方说桌子上有钥匙。玩家靠近时,应使用着色器勾勒轮廓,并显示“拾取键”文字。

我创建了可交互游戏对象的界面。

public interface IInteractable
{
    string InteractabilityInfo { get; }

    void ShowInteractability();

    void Interact();
}

这样做,我可以将接口组件添加到我的Key脚本中

public class Key : MonoBehaviour, IInteractable
{
    public string InteractabilityInfo { get { return "some text here"; } }

    public void ShowInteractability()
    {
        // Outline Shader maybe?
    }

    public void Interact()
    {
        // Do something with the key
    }
}

当涉及检查某些事物是否可交互的脚本时,我创建了一个脚本,该脚本创建了一个检查可交互对象的光线投射。 (我将此脚本附加到FPS相机上)

public class InteractabilityCheck : MonoBehaviour
{
    private const int RANGE = 3;

    private void Update()
    {
        RaycastHit hit;
        if (Physics.Raycast(transform.position, transform.forward, out hit, RANGE))
        {
            IInteractable interactable = hit.collider.GetComponent<IInteractable>();

            if (interactable != null)
            {
                interactable.ShowInteractability();

                if (Input.GetKeyDown(KeyCode.E))
                {
                    interactable.Interact();
                }
            }
        }
    }
}

此脚本尝试获取接口组件,如果不为null,则从中调用方法。

此代码可以正常工作,但我不喜欢Raycast每帧触发一个。还有另一种实现交互性的方法吗?

2 个答案:

答案 0 :(得分:1)

是的,光线投射是“昂贵的”,将其称为每一帧都不酷,有许多方法可以避免这种情况。在您的情况下,您可以像这样简单地重组代码:

private void Update()
{

    if (Input.GetKeyDown(KeyCode.E))
    {
      RaycastHit hit;
      if (Physics.Raycast(transform.position, transform.forward, out hit, RANGE))
      {
          IInteractable interactable = hit.collider.GetComponent<IInteractable>();

          if (interactable != null)
          {
              interactable.ShowInteractability();
              interactable.Interact();      
          }
      }
  }
}

另外一个好主意是采用一种分离的方法来获得游戏逻辑,以便您以后可以更轻松地修改它并避免使用spaggeti xaxa。像这样:

public void interactWithYourObject()
{
    RaycastHit hit;
    if (Physics.Raycast(transform.position, transform.forward, out hit, RANGE))
      {
          IInteractable interactable = hit.collider.GetComponent<IInteractable>();

          if (interactable != null)
          {
              interactable.ShowInteractability();
              interactable.Interact();      
          }
      }
}

,然后在您的更新中,只要您的条件是真的,就调用它:

private void Update()
{
   RaycastHit hit;
   if (Physics.Raycast(transform.position, transform.forward, out hit, RANGE))
   {
      interactWithYourObject()
   }
}

答案 1 :(得分:1)

另一种方法是将球形触发器放置在可交互对象上,然后您可以通过球形触发器的radius控制范围。 这类似于我刚刚回答的另一个问题,所以我重新编写了代码。

using UnityEngine;

[RequireComponent(typeof(SphereCollider))]
internal abstract class CollisionTrigger : MonoBehaviour
{
    private bool _isPlayerInsideTrigger = false;

    private void Awake()
    {
        if(!GetComponent<SphereCollider>().isTrigger)
        {
            Debug.LogError("Please set the sphere collider to a trigger.");
            enabled = false;
            return;
        }
    }

    private void Update()
    {
        if(_isPlayerInsideTrigger)
        {
            FakeOnTriggerStay();
        }
    }

    private void OnTriggerEnter(Collider collider)
    {
        if(!collider.CompareTag("Player")) return;
        _isPlayerInsideTrigger = true;
    }

    public abstract void FakeOnTriggerStay();

    private void OnTriggerExit(Collider collider)
    {
        if(!collider.CompareTag("Player")) return;
        _isPlayerInsideTrigger = false;
    }
}

这是一个示例,用于演示使用上面提供的类的Key类的外观。

internal class Key : CollisionTrigger
{
    public override void FakeOnTriggerStay()
    {
        // Show the user they can now interact with it.
        // Outline Shader maybe?

        if(Input.GetKeyDown(KeyCode.E))
        {
            // Do something with the key.
        }
    }
}