代码之家  ›  专栏  ›  技术社区  ›  Ashane Alvis

如何从不断变化的对象列表中获取组件?

  •  0
  • Ashane Alvis  · 技术社区  · 7 年前

    在unity的3d无止境跑步者游戏中,我遇到了这个问题。我有一个平台(路段/道路)列表,当玩家沿z方向奔跑时,这些平台位于玩家面前。我下载了一个名为Dreamteck样条线的新资产包。因此,每个平台都有一个连接到它的样条线组件。铺设平台后,玩家抓住样条线,并根据样条线的图案运行。

    假设玩家在第一个平台上。当玩家到达第一个平台样条线的末端时 OnEndReached() 调用事件处理程序,它基本上表示当到达样条线的端点时要发生什么。所以我想知道,一旦到达终点,如何得到下一条样条曲线。

    enter image description here

    P=玩家

    如上图所示,这就是我正在努力实现的目标。关于平台铺设方式的简要描述是,一旦玩家走到下一条路,他刚刚通过的那条路就会被禁用,因此下次他可以随意重复使用玩家面前的道路。

    代码:track manager脚本。 公共段[]tilePrefabs; 公共静态段新闻段;

        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;
        }
    

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

    所以我没有主意了。所以来这里寻求解决方案或建议。我们将不胜感激。

    2 回复  |  直到 7 年前
        1
  •  1
  •   James McGhee    7 年前

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

    那就是

    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);
    

    请注意,我从列表中删除了我所在的段。。。该列表仅代表即将到来的顺序,在跑步者游戏中,我很容易知道这一点,例如,我可以重置内容,根据性能、分数等更改分段的属性。

        2
  •  0
  •   Doh09    7 年前

    如果您使用的是2D碰撞器,那么使用OnTiggerEnter或OnTiggerEnter2D应该可以完成这项工作。但正如你所说,你已经在使用对撞机,我想这就是你所尝试的。

    您可以尝试:

    也可以使用三维对象进行光线投射。

    在这种情况下,你使用它的目的基本上是向你的播放器下方的地面发射一束“激光”,然后根据你告诉它要击中的层抓住一个物体。因此,如果您有一个称为“地面”的层,您的平台是该层的一部分,那么它只能从该层返回对象。

    只需记住经常进行光线投射,以便对其进行更新以反映游戏。