Unity RPG Core Combat - Failure to reposition player when loading new scene

To clarify. When the code below runs, after the coroutine is initiated and the scene is beginning to load the whole process fails when trying to find a correct “portal” (in my code the names of functions and variables have been renamed to protect the innocent) in the loading scene. Take a look.

public class TransitionZone : MonoBehaviour
{
enum ZoneID
{ AN, BN, CN, DN, EN, FN}

[SerializeField] int destinationMap = -1;
[SerializeField] Transform targetPosition;
[SerializeField] ZoneID zoneID;
[SerializeField] float length = 1;
[SerializeField] float height = 1;
[SerializeField] float depth = 1;
GameObject player;
BoxCollider zone;
bool playerInPosition = false;

void Awake()
{
    zone = GetComponent<BoxCollider>();
    zone.isTrigger = true;
    zone.size = new Vector3(length, height, depth);
    player = GameObject.FindWithTag("Player");
}

void OnTriggerEnter(Collider other)
{
    if (other.tag == "Player")
    {
        Debug.Log(other.name.ToString() + " has entered " + gameObject.name + " Transfer Zone.");

        StartCoroutine(TransferPlayer());
    }
}

IEnumerator TransferPlayer()
{
    Debug.Log("Couroutine TransferPlayer started.");

    if (destinationMap < 0)
    {
        Debug.LogError("Destination map not selected.");
        yield break;
    }

    DontDestroyOnLoad(gameObject);
    TransitionZone targetZone = GetTargetZone();
    yield return SceneManager.LoadSceneAsync(destinationMap);
    
    Debug.Log("TargetZone == " + targetZone.name);
    Debug.Log("Couroutine TransferPlayer :: targetZone == " + targetZone.name.ToString());
    
    PositionPlayer(targetZone);

    Debug.Log(player.name + "'s position is " + player.transform.position);
    Debug.Log("Coroutine executed.");
  
    Destroy(this);
    
}

void PositionPlayer(TransitionZone targetZone)
{

    Debug.Log(player.name + " initial position is " + player.transform.position);

    player.GetComponent<IAstarAI>().canMove = false;

    player.transform.position = targetZone.targetPosition.position;
    player.transform.rotation = targetZone.targetPosition.rotation;

    Debug.Log("PositionPlayer :: targetPosition.position = " + targetZone.targetPosition.position);
    Debug.Log("PositionPlayer :: targetPosition.rotation = " + targetZone.targetPosition.rotation);
    Debug.Log(player.name.ToString() + " final position is " + player.transform.position);

    player.GetComponent<IAstarAI>().canMove = true;
    playerInPosition = true;

    Debug.Log("PlayerInPosition = " + playerInPosition);
}   

TransitionZone GetTargetZone()
{
    foreach (TransitionZone zone in FindObjectsOfType<TransitionZone>()) 
    {
        if (zone == this)
        {
            Debug.LogError("Same zone selected. " + zone.name);
            continue;
        }

/* This is where the trouble begins. */

        if (zone.zoneID != zoneID)
        {
            Debug.LogError("Zone ID conflict. " + zone.zoneID + " " + zoneID);
            continue;
        }

        print(zone.zoneID);
        return zone;
    }

    Debug.LogError("Failed to get a TargetZone.");
    return null;
}

void OnDrawGizmos()
{
    Gizmos.color = Color.green;
    Gizmos.DrawWireCube(transform.position, new Vector3(length, height, depth));
}

}

Here is the Console window:

Screenshot 2024-06-13 224002

The scene does load with all assets. No scripts are affected (as to be expected). My script just doesn’t understand the assignment.

What seems to be the problem is that my GetTargetZone blasts through all TransitionZones while the scene is loading then returns null because it has already skipped beyond the intended target. Thus, the player’s unit is never moved from the position it was manually placed by editor, prior to pushing play.

The terrains in use are set at different base heights. so, if you remove the
player.GetComponent().canMove = false;
line, since the current game object never finishes its coroutine it is never destroyed, and our player will either plummet hundreds of units or suddenly appear in the lowest level of Hell (global position). It is amusing… Twice.

Just twice.

If I turn the fellow around and head back to the previous scene we get an encore performance of this behavior, except now we have two undestroyed, lingering game objects / scripts. This can go on as long as you want to tolerate.

I have failed many corrective attempts so far. I have also tried stripping the scene down to just two entrance/egress zones for each map and deactivating all superfluous activities and/or objects. Same behavior, quicker loading time.

I did get excited about a post describing an identical issue by another student. Alas, his clever solution did not satisfy my own frustration.

This is a repost of a currently unsolved mystery. I reposted for clarity and to update information.
If I am posting out of decorum I do apologize. I still be-a-struggling with editing a post on a lecture’s Q&A board.

The most obvious answer here would appear to be that in the new scene that you are transitioning to, there is not a corresponding TransitionZone with an identifier of CN… It looks like you have 4 other zones in the new scene one each of EN, DN, BN, and AN.

I think nay. I have been ever attentive to the fields in the inspector… But, you did get me thinking. Look again at the error logging. The second error recorded claims that CN was indeed selected, then discarded because it was the same zone.
TransitionZone_00-01CN is the same zone triggered to load the new scene. Are all of the successive zones from the current scene? I am going to open my project as soon as I can and get a little more specific with error logging then report back.

BTW, as always, thanks for taking the time. You got our backs.

My suspicion was well placed. Check it out.

This function should return a TransitionZone labeled TransitionZone_01-00AN as opposed to TransitionZone_00-01AN
Is a piston firing out of place? Perhaps the GetTargetZone function is grabbing before the new zones are instantiated, returns null, thus the player moves nowhere. The scene finishes loading and there are tears inside my heart.

This is evidenced from where I mentioned about needing set the agent’s canMove value to false. The player isn’t told to stay put while the scene is loading, vertical manslaughter ensues. I am still scratching my head a bit about that though. If told to stay put the unit remains on terra firma despite the over 100-unit height differential, otherwise it’s off to another screaming, areal death. (Note to self: Get sound FX.)

I am using 22.3.2. I have not done one bit of looking into the change log archive. I do not know if this is relevant in how Async is handled.

Don’t know if any of this strikes a cord…

And Now! Time for the next riveting chapter in: Coding Under a Dunce Cap

Did some reordering in the code. No remarkable changes but where things snag moved a line down in execution. I am nulling out. I will show.

namespace Function
{
public class TransitionZone : MonoBehaviour
{
enum ZoneID
{ AN, BN, CN, DN, EN, FN}

    [SerializeField] int destinationMap = -1;
    [SerializeField] Transform arrivalPoint;
    [SerializeField] ZoneID zoneID;
    [SerializeField] float length = 1f;
    [SerializeField] float height = 1f;
    [SerializeField] float depth = 1f;
    GameObject player;
    BoxCollider zone;
    bool playerInPosition = false;
   
    void Awake()
    {
        zone = GetComponent<BoxCollider>();
        zone.isTrigger = true;
        zone.size = new Vector3(length, height, depth);
        player = GameObject.FindWithTag("Player");
    }

    void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Player") 
        { 
            StartCoroutine(TransferPlayer());
            Debug.Log(other.name.ToString() + " has entered " + gameObject.name + " TransitionZone.");
        }
    }

    IEnumerator TransferPlayer()
    {
        DontDestroyOnLoad(this);
        Debug.Log("Couroutine TransferPlayer started.");

        if (destinationMap < 0)
        {
            Debug.LogError("Destination map not selected.");
            yield break;
        }
        
        yield return SceneManager.LoadSceneAsync(destinationMap);

        TransitionZone targetZone = GetTargetZone();
        Debug.Log("Couroutine TransferPlayer :: targetZone == " + targetZone.name.ToString());
        
        /* Now, I am paying attention here */
        PositionPlayer(targetZone);
        while (!playerInPosition)
        {
            yield return null;
        }

        Debug.Log(player.name + "'s position is " + player.transform.position);
        Debug.Log("Coroutine executed.");

        Destroy(this); 
    }

    void PositionPlayer(TransitionZone targetZone)
    {
        Debug.Log(player.name + " initial position is " + player.transform.position);

        player.transform.position = targetZone.arrivalPoint.position;
        player.transform.rotation = targetZone.arrivalPoint.rotation;

        Debug.Log("PositionPlayer :: arrivalPoint.position == " + targetZone.arrivalPoint.position);
        Debug.Log("PositionPlayer :: arrivalPoint.rotation == " + targetZone.arrivalPoint.rotation);
        Debug.Log(player.name.ToString() + " final position is " + player.transform.position);

        playerInPosition = true;

        Debug.Log("PlayerInPosition == " + playerInPosition);
    }   

    TransitionZone GetTargetZone()
    {
        foreach (TransitionZone zone in FindObjectsOfType<TransitionZone>()) 
        {
            if (zone == this)
            {
                Debug.LogError("Same zone selected. " + zone.name + "\n" + " the zoneID from the inpector is " + zoneID);
                continue;
            }

            if (zone.zoneID != zoneID)
            {
                Debug.Log("In the lines below, The first values are the intended zone name and zoneID"); 
                Debug.LogError("Zone ID conflict. " + zone.name + "/" + zone.zoneID + "\n" + " &  the zoneID from the inpector " + zoneID);
                continue;
            }

            Debug.Log(zone.zoneID);
            return zone;
        }

        Debug.LogError("Failed to get a TargetZone.");
        return null;
    }

    void OnDrawGizmos()
    {
        Gizmos.color = Color.green;
        Gizmos.DrawWireCube(transform.position, new Vector3(length, height, depth));
    }
}

}

Console window:

half of the issue described in above post has been corrected. My unit is, at least, trying to get to the proper continent. I cannot think of what is getting destroyed here to cause the MissingReferenceException. The triggering collider still remains undestroyed, and the scene loads as described previously.

I can show the inspector pics if you want. I am satisfied with what I am seeing there. Ahhh, hubris, such a heady tonic.

The problem remaining here is simple. The Player in the original scene has been destroyed (and we want it that way, as each scene should have it’s own copy of the Core which includes the Player).

After LoadSceneAsync() has completed, you’ll have to find the player again with GameObject.FindWithTag("Player");

You get a cookie.

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms