更改 public 基于碰撞的变换。根据 public 变换更改线渲染器点

Change public Transforms based on collision. Change line renderer points based on public transforms

我是新手,我似乎无法让它工作。玩家有一根长杆,如果他们戳某些物体,它就会开始与他们戳的下一个物体建立类似绳索的连接。这些对象被标记为“PokableObjects”,戳玩家会点击。我将拥有数百个不同的 pokeable 对象,我希望杆子上的脚本适用于所有这些对象。

我想我误解了如何只引用被戳的对象。我想要贝塞尔曲线脚本的点,即 public 转换,以适应并成为玩家点击的任何“PokableObject”。

这是我的脚本:

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

public class BCurve : MonoBehaviour
{
    //curved line renderer stuff
    private LineRenderer lineRenderer;
    public Transform p0;
    public Transform p1;
    public Transform p2;

    //Object name detection stuff
    bool m_Started;
    public LayerMask m_LayerMask;

    //Respawn Stuff
    private float clickCounter;
    public GameObject newPoker;

    void Start()
    {
        lineRenderer = GetComponent<LineRenderer>();
        m_Started = true;
        clickCounter = 0;
    }

    private void OnTriggerStay(Collider other)

    {
        if (other.tag == "PokableObject")

        {
            Collider[] hitColliders = Physics.OverlapBox(gameObject.transform.position, transform.localScale / 2, Quaternion.identity, m_LayerMask);

            if (Input.GetMouseButtonDown(0) && (clickCounter == 0))
            {
                p0 = Collider.gameObject.position;
                clickCounter++;
            }
                
  
            else
            {
                p2 = Collider.gameObject.position;

                //find midpoint between p0 & p2 then lower it's Y coordinate by 1
                p1 = ((p0.position.x + p2.position.x) * .05f, ((p0.position.y + p2.position.y) * .05f) - 1), (p0.position.z + p2.position.z) * .05f;

                //disable current object and spawn a new one so players can repeat
                Instantiate(newPoker, transform.position, Quaternion.Euler(0, 0, 0));
                GetComponent<BCurve>().enabled = false;
            }
        }
    }

    void Update()
    {
        DrawQuadraticBezierCurve(p0.position, p1.position, p2.position);

    }

    void DrawQuadraticBezierCurve(Vector3 p0, Vector3 p1, Vector3 p2)

    {
        lineRenderer.positionCount = 200;
        float t = 0f;
        Vector3 B = new Vector3(0, 0, 0);
        for (int i = 0; i < lineRenderer.positionCount; i++)
        {
            B = (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
            lineRenderer.SetPosition(i, B);
            t += (1 / (float)lineRenderer.positionCount);
        }
    }
}

非常感谢所有帮助。

谢谢

  • 首先从不使用==比较float值!由于浮点 (im) 精度,这可能会失败,即使在逻辑上你认为它会匹配。

    例如

     0.2f * 5f / 10f == 1.0f
    

    不一定 true 因为它实际上可能是 1.00000010.9999999.

    无论如何,使用 float 作为计数器是没有意义的。你宁愿使用

    int clickCounter;  
    
  • 那你在计算p1的时候就有一堆,了。好像这应该被包裹在 p1 = new Vector3( .... )

  • 但是无论如何,您计算出的 p1 不是 Transform 而是 Vector3 位置。你不需要这个作为一个领域!给定 DrawQuadraticBezierCurve 中的两个 Transforms p0p2 之后再简单地重新计算它,然后你就可以简单地使它成为

    var p1 = (p0.position + p2.position) * .05f - Vector3.up;
    
  • 不需要 LayerMaskOverlapBox,结果您根本没有在任何地方使用。您已经有一个标签来检查您的命中

  • Collider.gamObject 没有意义。你想要的是访问你正在与之碰撞的对象的 Transform ,它只是 other.Transform

  • 不需要GetComponent<BCurve>。这个组件已经 BCurve 所以你可以 简单地使用

    enabled = false;
    
  • 但是,一旦您获得第二个点,您就会禁用该组件,因此 Update 将永远不会被再次调用。

    要么你只想用那一刻的位置画线一次,那么只需立即调用 DrawQuadraticBezierCurve 并删除你的整个 Update 方法。

    或者,如果您的对象可能会继续移动并且您希望不断更新线条,则保持 Update 但保持此组件处于启用状态。

  • 在这两种情况下,您应该只在已经拥有两个 Transform 时才调用 DrawQuadraticBezierCurve


所以总的来说它看起来像这样

public class BCurve : MonoBehaviour
{
    //curved line renderer stuff
    [SerializeField] private LineRenderer lineRenderer;

    public Transform start;
    public Transform end;

    //Respawn Stuff
    public GameObject newPoker;

    // I would make this adjustable via the Inspctor
    [SerializeField] private int positionCount = 200;

    private void Start()
    {
        if(!lineRenderer) lineRenderer = GetComponent<LineRenderer>();
    }

    private void OnTriggerStay(Collider other)
    {
        if(start && end)
        {
            return;
        }

        // in general rather use CompareTag instead of ==
        // the second silently fails in case of typos and is slightly slower
        if (other.CompareTag("PokableObject"))
        {
            // Then you ALWAYS want to check fr the mouse click, not only 
            // if the counter is 0
            if(Input.GetMouseButtonDown(0))
            {
                // you rather want to check here
                if (!start)
                {
                    start = other.transform;
                }
                // I would add a check though to not enable to click on the same object twice
                else if (other.transform != start)
                {
                    end = other.transform;

                    // If you anyway want the line to be drawn only ONCE with the current positions 
                    // then directly call this here and delete Update completely
                    //DrawQuadraticBezierCurve(start.position, end.position);
                    //enabled = false;
                    // or even
                    //Destroy(this);

                    Instantiate(newPoker, transform.position, Quaternion.Euler(0, 0, 0));
                }
            }
        }
    }

    private void Update()
    {
        if(start && end)
        {
           DrawQuadraticBezierCurve(start.position, end.position);
        }
    }

    private void DrawQuadraticBezierCurve(Vector3 p0, Vector3 p2)
    {
        lineRenderer.positionCount = positionCount;
        var inversePositionCount = 1f / positionCount;
        var t = 0f;

        var p1 = (p0.position + p2.position) * .05f - Vector3.up;
        var positions = new Vector3[positionCount];

        for (int i = 0; i < lineRenderer.positionCount; i++)
        {
            var B = (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
            positions[i] = B;
            t += inversePositionCount;
        }

        // Note: It is WAY cheaper to call this and set all positions at once
        lineRenderer.SetPositions(positions);
    }
}

derHugo 给出了一个惊人的答案,否则无法想出这个!我添加了一些东西,它工作得很好。第一件事是确保创建一个新的 LineRender 对象,新的扑克对象将自动引用该对象并重置起点和终点:

private void Start()
    {
        if (!lineRenderer) lineRenderer = GetComponent<LineRenderer>();
        lineRenderer = Instantiate(lineObject, transform.position, Quaternion.Euler(0, 0, 0)).GetComponent<LineRenderer>();
        start = null;
        end = null;
        destroy = false;
    }

第二个是将当前扑克牌的销毁和新扑克牌的实例化都移至 LateUpdate,因为该行未被渲染:

private void LateUpdate()
    {
        if (destroy == true)
        {
            Instantiate(newPoker, transform.position, transform.rotation).transform.SetParent(GameObject.Find("MainCamera").transform);
            Destroy(this.gameObject);
        }
    }

再次感谢derHugo的帮助!