Passing values between objects from different scenes

Hi there. I’ve been having some problems with passing values from objects that originate from different scenes.

To specify accurately,

I’ve got a login scene with an object called ‘Player’ with a script on it that handles information (login, getScore, saveScore etc.) via functions that call certain stored procedures on an SQL server.

This object is then passed through scenes (main menu, level 1, level 2 etc.) using DontDestroyOnLoad(this);

On scene “level_1” I’ve got a trigger object (start / finish) which keeps track of time. When a player passes through finish there is a variable called finishTime which holds the time it took to complete level.

I’d like to pass this value to the Player object so I can call a function to save the score. Normally, it wouldn’t be a problem to pass a variable value if both objects were placed to the scene by me. Then I could manually target the object I want to drag values from. But this is not the case.

I’ll post code relevant to this situation.

Player.cs (created on login scene)

using UnityEngine;
using System;
using System.Collections;

public class Player : MonoBehaviour 
{
  public string username;

  public IEnumerator SaveScore(string scr)
	{
		WWWForm form = new WWWForm();
		form.AddField("username", username);
		form.AddField("score", scr);
		
		WWW download = new WWW("http://www.colorsoft.hr/Portals/ColorsoftHr/files/SaveScore.aspx", form);
		
		yield return download;
		
		if (download.error != null)
		{
			outputText = download.error;
		}
	}
}

Trigger.cs

public class CircuitTimer : MonoBehaviour 
{
	public double levelTime;
	public double difference;
	public double actualTime;
	public double finishTime;
	
	public bool started = false;
	public bool finished = false;
	
	// Use this for initialization
	void Start () 
        {
		actualTime = 0;
	}
	
	// Update is called once per frame
	void Update () 
	{
		levelTime = Time.time;
		if(started)
			actualTime = levelTime - difference;		
	}
	
	//
	void OnTriggerEnter (Collider other) {
		if(started)
		{
			finishTime = actualTime;
			finished = true;
		}
		difference = Time.time;
		started = true;
	}
}

So, what I want is: when finished is true then pass the value of finishTime to object Player and use that value in function SaveScore().

My scripts work fine, there are no compilation errors yet for some reason, the values wont pass. I’m sure I’m probably missing a tiny bit of knowledge to connect all this.

Any help would be greatly appreciated. Maybe I’m using a completely wrong method(ology). Really can’t tell. I’m counting on your will to help us n00bs.

Cheers!

Hey pavlito,

There are a couple ways I can think of that you can do this. One way would be to keep a reference to the Player object in your Trigger script. Since, as you said, you don’t have a reference to it at compile-time (before running the game), you’ll need to find the object at run-time. You can do this in many different ways but I think the easiest would be to set the Tag of your Player object to “Player” and then use the GameObject.FindGameObjectsWithTag() function to find the correct object. Here’s a link to the scripting reference for that function:

Once you find the right GameObject, you’ll need to get a reference to its Script, which is Player.cs. You can do this with the following snippet of code:

//Assuming you set playerObj to be the reference to the correct game object.
Player playerScript = playerObj.GetComponent(typeof(Player)) as Player;

After this, you should be able to call the SaveScore function on playerScript and it should hopefully all work.

This seems to me to be a perfect case for an event structure, since you want the CircuitTimer class to call a method but it won’t necessarily know what that is or where that method is.

So instead of having the CircuitTimer find the Player, you can set up an event in the CircuitTimer class that fires when the race is finished. Then the Player class can subscribe to that event with the SaveScore method and it will execute whenever the event is called in the CircuitTimer class.

Here’s just a quick example of how to set this up (totally untested, though!):

public class CircuitTimer : MonoBehaviour
{
	public delegate void TriggerHandler(string time);
	public event TriggerHandler RaceTime;
	
	public void OnTriggerEnter()
	{
		...
		RaceTime(finishTime.ToString());
	}
	
	...
}

public class Player : MonoBehaviour
{
	public void OnLevelWasLoaded(1)
	{
		CircuitTimer finishLine = (CircuitTimer)GameObject.Find("StartFinish").GetComponent(typeof(CircuitTimer));
		finishLine.RaceTime += new CircuitTimer.TriggerHandler(SaveScore);
	}
	
	public void SaveScore(string scr)
	{
		...
	}
	
	...
}

You might also be able to simplify it even more by making the RaceTime event static (so your Player class wouldn’t even need the reference to the CircuitTimer instance), but I’m not sure if that is best practice or not.

Thanks guys, I’ll give it a try.

What got me confused is that another function actually CAN be called from the trigger.cs script without any fuss. Maybe it’s because of the type of the function.

This is the one:

public string Convert(double sec)
	{
		TimeSpan t = TimeSpan.FromSeconds(sec);
		string result = string.Format("{1:smile:2}:{2:smile:2}:{3:smile:3}", 
                        t.Minutes, 
                        t.Seconds, 
                        t.Milliseconds);
		
		return result;
	}

in trigger.cs I make:

// to get Convert() from
	public Player player;

and then later i just used

player.Convert(someDoubleVariable);

I can’t see why this function works, but SaveScore doesn’t.

Again, I point out that I’m moderately skilled in programming. It’s been some time :slight_smile:

Hi again!

I used both methods. Made the player object subscribe to the event and in trigger object i got some data using find object by tag.

Since this project is for learning purposes, I’m proud they both (methods) worked. :slight_smile:

There were some changes that needed to be done like

public void OnLevelWasLoaded(int level)
   	{
    	switch(level)
      	{
      		case 1: StartCoroutine(GetScore());
      				break;
      		case 2: CircuitTimer finishLine = (CircuitTimer)GameObject.Find("StartFinish").GetComponent(typeof(CircuitTimer));
      				finishLine.RaceTime += new CircuitTimer.TriggerHandler(SaveScore);
      				break;
     	}
     }

and also changing the SaveScore function to void because it didn’t work as set up before (used IEnumerator).

Why did I use both methods, apart from being educational? I wanted to check whether the finishTime was better or worse from previous efforts. If yes, display save button which triggered the event, and if not, don’t display save button at all.

So, thanks again. I now know much more than 2 days ago :slight_smile:

Cheers!