Ashane Alvis
Ashane Alvis

Reputation: 810

How do I get a component from a ever changing List of objects?

while working in a 3d endless runner game in unity I came across this issue. I have a List of platforms(segments/roads) that lay in front of the player while the player runs in z direction. I have downloaded a new asset package called Dreamteck splines. So each platform has a spline component attached to it. Once a platform is laid the player grabs the spline and runs according to the pattern of the spline.

Let's say that the player is on the first platform. When the player reaches the end of the first platform's spline, the OnEndReached() event handler is called, which basically says what you want to happen when the spline's endpoint is reached. So I want to know how to I get the next spline once the end is reached.

enter image description here

P = player

As seen in the image above this is what I am trying to accomplish. As a brief description of how platforms are laid is that once the player goes to the next road the one he just passed gets disabled so next time he can reuse the road in front of the player in random manner.

The code: track manager script. public Segment[] 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);
    }

The player script

//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>();
}

The segment script : Attached to each road/ platform

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

At the moment I use colliders, each road has a box collider component. Once the player reach end of the platform it does get the next spline component. It works but sometimes it fails to recognize the next spline and use the same spline which causes the player to run the same platform that he passed again and again.

So I'm out of ideas. So came here to find a solution or advice. Help would be appreciated.

Upvotes: 0

Views: 534

Answers (2)

James McGhee
James McGhee

Reputation: 131

In this case I would simply store my possible segments in a List then when I reached the end get the next segment and move the current 1st segment to the end or where ever you want to move it in the list.

That is

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

In this model you have a simple List which represents the order your segments will appear in, you always set the current segment to the next in the list e.g. index 0 and you put them back in the list when done... putting them at the end or if you want to randomize order slotting them in anyware except index 0 e.g.

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

Note that I removed the segment I am on from the list ... the list just represents the upcoming order which in runner games I find it handy to know that e.g. so I can reset things, change attributes of the segments based on performance, score, etc.

Upvotes: 1

Doh09
Doh09

Reputation: 2385

Using OnTriggerEnter, or OnTriggerEnter2D if you're dealing with 2D colliders, should do the job. But as you say you already are working with colliders, I assume this is what you have tried.

You could try:

You can also raycast with 3D objects.

What you would be using it for in this case is basically to shoot a "laser" into the ground below your player and grab an object there based on which layers you tell it to hit. So if you have a layer called "Ground" which your platform is part of, then it can only return objects from that layer.

Just remember to raycast often so it's updated to reflect the game.

Upvotes: 0

Related Questions