【问题标题】:How do I get a component from a ever changing List of objects?如何从不断变化的对象列表中获取组件?
【发布时间】:2018-02-16 16:54:45
【问题描述】:

在一个 3d 无尽的跑步游戏中工作时,我遇到了这个问题。我有一个平台列表(路段/道路),当玩家在 z 方向运行时,这些平台位于玩家面前。我已经下载了一个名为 Dreamteck splines 的新资产包。所以每个平台都有一个附加的样条组件。放置平台后,玩家抓住样条线并按照样条线的模式运行。

假设玩家在第一个平台上。当玩家到达第一个平台样条线的末端时,OnEndReached() 事件处理程序被调用,它基本上说明了当到达样条线的端点时您想要发生的事情。所以我想知道到达终点后如何获得下一条样条线。

P = 玩家

如上图所示,这就是我想要完成的任务。作为对如何铺设平台的简要说明,一旦玩家进入下一条道路,他刚刚经过的道路就会被禁用,因此下次他可以随机重用玩家面前的道路。

代码:轨道管理器脚本。 公共段[] tilePrefabs; public static Segment newSegment;

    public static List<Segment> m_Segments;
    public static List<Segment> m_PastSegements;
    private int m_SafeSegmentLeft;
    private int m_PreSegments = -1;

    private float startingSegmentDistance = 4f;
    private int startingSafeSegments = 2;
    private int amtSegmentsOnScreen = 10;
    private float segmentRemovalDistace = -40f;

    private float m_TotalWorldDistance;
    private float m_CurrentSegmentDistance;

void Update ()
    {
        while (m_Segments.Count < amtSegmentsOnScreen)
        {
            SpawnNewSegment();
        }

        m_TotalWorldDistance += scaledSpeed;
        m_CurrentSegmentDistance += scaledSpeed;

        if (m_CurrentSegmentDistance > m_Segments[0].worldLength)
        {
            m_CurrentSegmentDistance -= m_Segments[0].worldLength;
            m_PastSegements.Add(m_Segments[0]);
            m_Segments.RemoveAt(0);

        }

        Vector3 currentPos;
        Quaternion currentRot;
        Transform playerTransform = playerMotor.transform;

        m_Segments[0].GetPointAtInWorldUnit(m_CurrentSegmentDistance, out currentPos, out currentRot);

        bool needRecenter = currentPos.sqrMagnitude > floatingOriginThreshold;

        if (needRecenter)
        {
            int count = m_Segments.Count;

            for (int i = 0; i < count; i++)
            {
                m_Segments[i].transform.position -= currentPos;
            }

            count = m_PastSegements.Count;
            for (int i = 0; i < count; i++)
            {
                m_PastSegements[i].transform.position -= currentPos;
            }

            m_Segments[0].GetPointAtInWorldUnit(m_CurrentSegmentDistance, out currentPos, out currentRot);
        }

        playerTransform.rotation = currentRot;
        playerTransform.position = currentPos;       

        for (int i = 0; i < m_PastSegements.Count; i++)
        {
            if ((m_PastSegements[i].transform.position - currentPos).z < segmentRemovalDistace)
            {
                m_PastSegements[i].Cleanup();
                m_PastSegements.RemoveAt(i);
                i--;
            }
        }

    }

    public void SpawnNewSegment()
    {
        int useSegment = Random.Range(0, tilePrefabs.Length);
        if (useSegment == m_PreSegments)
        {
            useSegment = (useSegment + 1) % tilePrefabs.Length;
        }

        Segment segmentToUse = tilePrefabs[useSegment];
        newSegment = Instantiate(segmentToUse, Vector3.zero, Quaternion.identity);
        Vector3 currentExitPoint;
        Quaternion currentExitRotation;

        if (m_Segments.Count > 0)
            m_Segments[m_Segments.Count - 1].GetPointAt(1.0f, out currentExitPoint, out currentExitRotation);
        else
        {
            currentExitPoint = transform.position;
            currentExitRotation = transform.rotation;
        }

        newSegment.transform.rotation = currentExitRotation;

        Vector3 entryPoint;
        Quaternion entryRotation;

        newSegment.GetPointAt(0.0f, out entryPoint, out entryRotation);

        Vector3 pos = currentExitPoint + (newSegment.transform.position - entryPoint);
        newSegment.transform.position = pos;
        newSegment.manager = this;

        newSegment.transform.localScale = new Vector3((Random.value > 0.5f ? -1 : 1), 1, 1);
        newSegment.objectRoot.localScale = new Vector3(1.0f / newSegment.transform.localScale.x, 1, 1);

        if (m_SafeSegmentLeft <= 0)
            SpawnObstacle(newSegment);
        else
            m_SafeSegmentLeft -= 1;

        m_Segments.Add(newSegment);
    }

播放器脚本

//Current tile segment;    
private Segment currentSegment;
//Spline Follower
private SplineFollower follower
//For Dreamteck spline -->
private Segment nextSegment;

void Start()
    {
        playerCollider = GetComponent<CapsuleCollider>();
        anim = GetComponent<Animator>();
        follower = GetComponent<SplineFollower>();

        moveLane = currentLane;
        follower.onEndReached += Follower_onEndReached;

    }

private void Follower_onEndReached()
{
        currentSegment = nextSegment;
        follower.computer = currentSegment.spline;
}

void OnTriggerEnter(Collider col)
{
        nextSegment = col.GetComponentInParent<Segment>();
}

路段脚本:附加到每条道路/平台

 public SplineComputer spline;
    public static Segment next;
    SplinePoint[] points;

void Start()
    {
        spline = GetComponentInChildren<SplineComputer>();
        spline.space = SplineComputer.Space.Local;

        points = spline.GetPoints();

        if (points.Length == 0)
            return;
    }

在我使用碰撞器的那一刻,每条道路都有一个盒子碰撞器组件。一旦玩家到达平台的末端,它就会得到下一个样条线组件。它可以工作,但有时它无法识别下一个样条线并使用相同的样条线,这导致玩家一次又一次地运行他经过的同一个平台。

所以我没有想法。所以来这里寻找解决方案或建议。帮助将不胜感激。

【问题讨论】:

  • 您愿意展示代表您上面描述的代码吗?没有它,我们就看不到你实际在做什么。
  • “它可以工作,但有时它会失败”不是对 SO 问题的合适解释。由于您的解决方案中可能没有代码,因此您不太可能提供minimal reproducible example...因此,该问题可能不适合SO-如果您可以edit 发布实际代码/任何演示问题就好了,否则考虑删除并在 Unity 上寻找 论坛 来发布您的问题会更好(SO 是问答网站,不适合讨论解决问题的可能方法)
  • 我没有看到SplineFollower.onEndReached事件是在哪里引发的,你能发布相关代码吗?
  • @Galandil 这是一个事件处理程序。该脚本在我购买的资产包中。这部分代码有效。一旦玩家到达样条线的末端,我只想要一种不同的方式来获取下一个样条线组件。基本上如图所示,一旦玩家到达当前平台或道路的末端,我如何获取任何组件。

标签: c# list unity3d unity5


【解决方案1】:

在这种情况下,我只需将可能的片段存储在列表中,然后当我到达末尾时获取下一个片段并将当前的第一个片段移动到末尾或您想要将其移动到列表中的任何位置。

那是

public Segment currentSegment;
public List<Segment> segments;
void OnEndReached()
{
    //Put the completed segmetn back in the list ... probably at the end or randomized anywhere but at the start
    segments.Insert(segments.Count-1, currentSegment);
    //Now get the next segment
    currentSegment = segments[0];
    segments.RemoveAt(0);
}

在此模型中,您有一个简单的列表,它表示您的分段将出现的顺序,您始终将当前分段设置为列表中的下一个,例如索引 0 并在完成后将它们放回列表中...将它们放在最后,或者如果您想随机化顺序将它们放入除索引 0 之外的任何软件中,例如

segments.Insert(UnityEngine.Random.Range(1, segments.Count), currentSegment);

请注意,我从列表中删除了我所在的部分...该列表仅代表即将到来的顺序,在跑步游戏中我发现它很容易知道,例如所以我可以重置事物,根据表现、分数等更改细分的属性。

【讨论】:

  • 非常感谢。这帮助我完成了它。 :D
【解决方案2】:

使用 OnTriggerEnter 或 OnTriggerEnter2D(如果您正在处理 2D 对撞机)应该可以完成这项工作。但正如你所说,你已经在使用对撞机,我认为这就是你尝试过的。

你可以试试:

您还可以使用 3D 对象进行光线投射。

在这种情况下,您将使用它基本上是向玩家下方的地面发射“激光”,然后根据您告诉它击中的层来抓取那里的对象。因此,如果您的平台属于一个名为“Ground”的层,那么它只能从该层返回对象。

记得经常进行光线投射,以便更新以反映游戏。

【讨论】:

  • 嗨。它的 3d。我的错。我应该添加代码。不过应该试试光线投射。但是会影响性能吗?
  • 我怀疑它会对您所感受到的性能产生任何影响。除非您同时将它添加到许多不同的对象中。但只要你只在一个玩家或几个物体上使用它就可以了。
  • 好的,我试试。谢谢。如果这行得通。我会标记为答案。
  • 记住使用 3D 物理而不是 2D,因为您是在 3D 中工作的。
猜你喜欢
  • 2011-06-26
  • 1970-01-01
  • 2013-04-13
  • 2017-03-08
  • 2019-07-03
  • 1970-01-01
  • 2019-08-24
  • 2019-02-06
  • 2011-09-08
相关资源
最近更新 更多