0

我很新,我似乎无法让它工作。玩家有一根长杆,如果他们戳某些物体,它会与他们戳的下一个物体建立类似字符串的连接。这些对象被标记为“PokableObjects”,并且要戳玩家会点击。我将拥有数百个不同的可戳对象,并且我希望杆上的脚本适用于所有这些对象。

我想我误解了如何只引用被戳的对象。我希望 Bezier 曲线脚本的点(公共变换)能够适应并成为玩家点击的任何“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);
        }
    }
}

非常感谢所有帮助。

谢谢

4

2 回答 2

0
  • 首先,永远不要float使用==! 由于浮点(im)精度,即使在逻辑上您认为它会匹配,这也可能会失败。

    例如

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

    不一定true因为它实际上可能是1.0000001or 0.9999999

    无论如何,这表示将 afloat用作计数器是没有意义的。你宁愿使用

    int clickCounter;  
    
  • ,然后在计算时你有一堆p1。似乎这应该被包裹在一个p1 = new Vector3( .... )

  • 但是无论如何,p1您计算的是没有Transform,而是宁愿成为一个Vector3职位。你不需要这个作为一个字段!稍后只需在给定两个Transformsp0和insidep2的情况下重新计算它DrawQuadraticBezierCurve,然后您就可以简单地制作它

    var p1 = (p0.position + p2.position) * .05f - Vector3.up;
    
  • 没有必要这样做,LayerMask并且OverlapBox您根本不会在任何地方使用哪个结果。你已经有一个标签来检查你的命中

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

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

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

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

    或者,如果您的对象可能会继续移动并且您希望不断更新该行,那么请Update保持启用此组件。

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


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

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);
    }
}
于 2022-02-25T07:57:00.543 回答
0

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 的帮助!

于 2022-02-26T03:19:26.530 回答