Using Variables between scripts

Hi, I have two scripts. Userdata is used to access my database(phpmyadmin and xampp), I import the data from a website, now my code is woolly as I haven’t been able to test individual parts. in the script userdata, I use a while loop to search through all of the records in the table and then when a result is found it attempts to store the relevant data from my 2d array of database results into 3 variables and break out of the while loop. It throws an error when storing the 2d array items into string variables. Then afterwards I want to use the results in another script however I don’t know how to do this. Also yes I know I am using the old GUI stuff and should switch to the newer version :slight_smile:
This is the script where I want to access the result variables.

public class ILogin : MonoBehaviour
{
    public IStudent IStudent;
    public GameObject StudentScreen;
    public string UsernameInput = string.Empty;
    public string PasswordInput = string.Empty;
    public Boolean Student = false;
    public int row;
    private Rect windowRect = new Rect(0, 0, Screen.width, Screen.height);
    private string AccountType;
    //private object userArray;
    public void OnGUI()
    {
        GUI.Window(0, windowRect, WindowFunction, "Login");
    }
    public void WindowFunction(int windowID)
    {
        //user input log in
        UsernameInput = GUI.TextField(new Rect(Screen.width / 3, 2 * Screen.height / 5, Screen.width / 3, Screen.height / 10), UsernameInput, 10);
        PasswordInput = GUI.PasswordField(new Rect(Screen.width / 3, 2 * Screen.height / 3, Screen.width / 3, Screen.height / 10), PasswordInput, "X"[0], 10);
        GUI.Label(new Rect(Screen.width / 3, 35 * Screen.height / 100, Screen.width / 5, Screen.height / 8), "Username");
        GUI.Label(new Rect(Screen.width / 3, 62 * Screen.height / 100, Screen.width / 8, Screen.height / 8), "Password");
        if (GUI.Button(new Rect(Screen.width / 3, 4 * Screen.height / 5, Screen.width / 8, Screen.height / 8), "Log-in"))
        {
            GetComponent<Userdata>().CallDetail(UsernameInput, PasswordInput);

            if (DBUsername == UsernameInput && DBPassword == PasswordInput)
            {
                //Check account type
                if (AccountType == "STAFF")
                {
                    //call Staff interface
                    Debug.Log("STAFF");
                    SceneManager.LoadScene("Staff");
                }
                else
                {
                    //Call Student Interface
                    Debug.Log("STUDENT");
                    SceneManager.LoadScene("Student");
                }
            }
            else
            {
                GUI.Label(new Rect(Screen.width / 2, Screen.height / 2, Screen.width / 12, Screen.height / 12), "Incorrect Login Details");
            }
        }
    }
}

Then the Userdata Script that accesses the table.

public class Userdata : MonoBehaviour
{
    string url = "http://localhost/Wellbeing/userdata.php";
    private string Temp;
    public string[] users;
    public string[] userArray;
    private int row = 0;
    private bool Found = false;
    public string DBUsername;
    public string DBPassword;
    public string AccountType;
    public IEnumerator CallDetail(string UsernameInput, string PasswordInput)
    {
        Debug.Log("hi");
        WWW userData = new WWW(url);
        yield return userData;
        string userDataString = userData.text;
        users = userDataString.Split(';');
        Debug.Log(users);
        for (var i = 0; i < users.Length; i++)
        {
            Temp = users[i];
            userArray = Temp.Split('|');
            Debug.Log(userArray);
        }
        while (Found != true)
        {
            if (userArray[row] == UsernameInput && userArray[row] == PasswordInput)
            {
                DBUsername = userArray[row][2];
                DBPassword = userArray[row][1];
                AccountType = userArray[row][6];
                Found = true;
            }
            else
            {
                row++;
            }
        }
        yield return DBUsername;
        yield return DBPassword;
        yield return AccountType;
    }
}

Many Thanks

I’m not at my pc at the moment so I can’t give you code, but I can tell you how I’d do it. I’d keep it on the same thread (so get rid of the Ienumerator) as getting info from a website will be quick if you have any decent internet connection. Then I’d have it return a custom Class with the fields you need inside the custom class. Then just use these fields where you need to

variables from other scripts can be accessed if they are static variables,

for example
public static int number;

Then in another script —> OriginalScriptName.number = 4;

2 Likes

So if I got rid of the IEnumerator, what type of function would I use? would it be ‘public string’?? Sorry this is my first proper attempt at object orientated:)

So would I declare the variable in the script I want to use it in and then in the script that contains the IEnumerator function, where I want to give it a value, should I give it a value using the static variable method?

public (custom class). So let’s say you make a class called UrlData to hold the data you receive from the URL. It could have a couple of strings in it or whatever. Then you create a new instance of your custom class, assign the values and return it. That’s only though if you’re trying to get more than one type of data from the method. If you want just a couple of strings then return a string array (String) and make it a public string function. All of this though is me assuming you want to return multiple pieces of data. If it’s just a string you want to return then return a string and make it a public string function

As soon as I changed the IEnumerator to string[ ], it gives me this “The body of cannot be an iterator block because ‘string[ ]’ is not an iterator interface type”

Where is the error happening? Do you still have the yeild returns in there? Because they could be causing the issue.

If I remove the yield and just have it as a return it says “cannot return from an iterator…”
The error occurs on the CallDetail part of the function definition, and when I look at the potential fixes it suggests that I keep as an IEnumerable and but change it to IEnumerable.
Even if these are fixed it still gives me an error saying “cannot implicitly convert type ‘char’ to ‘string’” on the each of the userArray definitions

while (Found != true)
        {
            if (userArray[row] == UsernameInput && userArray[row] == PasswordInput)
            {
                DBUsername = userArray[row][2];
                DBPassword = userArray[row][1];
                AccountType = userArray[row][6];
                Found = true;
            }
            else
            {
                row++;
            }
        }

A few things (I’m at home now so I can properly examine your code), you’re using Boolean in the first class (ILogin), in c# it’s bool. Second is that with userArray[row] you’re grabbing a string and by adding another [ ] afterwards you’re getting the character in that string (as strings are a list of chars). I don’t know the format of the string you’re using so I can’t tell you how to fix it, just why it is getting that error. I you’re willing to show me the format then maybe I can help.

Couple of things:

You try to access the members in that first script without any type and instance and they clearly seem to be declared in your UserData.That is, when you call GetComponent(), you need to store the return value into a variable to access it’s members (methods, fields etc). I’m referring to the identifiers DBUsername and such.

However, it seems the relations of the members are a bit off anyway. Reading about a login class, I’d assume that’s the entity which pulls off any login requests rather than UserData.

Also, you don’t actually start a coroutine, but execute it as if it was a method. This is usually not desired, as the response may take a while so the coroutine needs to wait.

And here’s another great thing: Try to build a JSON-formatted response. Instead of splitting, parsing and checking the response manually, you could use a JSON utitlity (such as the one shipped with Unity) to take the response and throw it into a type-safe object. Any errors that occur could simply be caught and handled.
That’s alot less error-prone, but may also take some effort to read everything up especially when you’re new to it.

Other than that, your while-loop (which will no longer be necesary if you follow the previous advice) is a candidate to freeze your application. You only allow it to stop when the value you’re looking for is found, otherwise it’ll continue looping infinitely.
That seems not important in a self-contained environment, but the response comes from an actual external source (even though you’re still likely the one who’s got full control over it in the first place) so it could actually be malformed and such. A simple typo, tampering or anything else would hang your application there.

Don’t yield strings. Strings are basically designed as an array of characters, so you’d yield X times where X is the number of characters in that string. I think that’s not what you’re trying to achieve there.

Other recommandations
Get rid of the legacy UI, it’s alot of boiler-plate code. If you replace it by using the newer system, it’s way less code and you can actually focus on the actual logic. It’s also benefitial when you need to reach out to other people on the forums, as there will be way less code to look into.

It also appears that the Login class is supposed to be more of a view component.

Conventions & Code-style

  • In some programming languages such as C#, types starting with a prefixed I (such as your ILogin class) are (for many people at least) considered to be interfaces rather than actual implementations.
  • AccountType could as well be an enumerated value
  • Declare literals (hard coded strings and non-intuitive primitive values) as constants to have a single point of declaration. This helps alot to prevent errors in the future.

I probably missed a few things, but if you follow the advices from top to bottom, you might get rid of many issues already.

Correct, declare it as a public static (variable type) (variable name) in the original script and then access it in any other script using – original_script_name.variable_name

I strongly recommend not to do that.
This usually ends up becoming a bad habit and is not a proper way to communicate between instances.

The string that I’m pulling from the Webpage is:
2|password|Student|AA|11|STUDENT;
which is in the form
ID|Password|Username|register Group|Year|AccountType

Thanks

Thanks this is a great piece, I am doing this for a school project and am already a bit out of my depth with OOP and C# so I think it is best to avoid JSON for the moment, but if there is time left at the end I will look into it.
You said I am not starting the coroutine at the correct time because it needs time to respond, so where and how should I be doing this?
To break out of the while loop, I will implement an else if statement that will break out of the loop when it finds a null value. This should be alright as only I can edit the data source, and I don’t intend on allowing anyone else to change it for the moment at least.
Then in general, calling a function that returns a value, how would I go about doing this? Would it be:

Var1 = GetComponent<Script>().Function()
[\CODE]
And then for multiple values
[CODE]
Var1, Var2, Var3 = GetComponent<Script>().Function()
[\CODE]

CallDetail needs to be started like (perhaps typos - sorry for that)

StartCoroutine(GetComponent<Userdata>().CallDetail(UsernameInput, PasswordInput));

That’s ugly though, and I still recommend to relocate that method to the login script, or at least split it into several lines or link the UserData component via inspector.

Also note, you wouldn’t get any results immediately, because that’s what yielding a www instance is all about. It yields until there’s any kind of response/result, let it be the desired response from the website or some kind of error. It’s pretty much unpredictable in which frame it’s going to be ready.

When it continues, you’d first check for errors using the error property of the WWW instance. If there’s no error, you can continue processing the response.

Since you don’t want to replace the split/parsing/evaluation mechanism by some JSON deserialization, I’d recommend anything but a while-loop.
If you always expect a fixed number of values in a particular order, for instance the format you’ve provided

you could split first and check the length of the returned array. If it does not have the desired length (a.k.a number of values), it’s malformed and there’s no need to do further processing unless you wanna handle it anyway.
Of course that’s optional, you can skip that and you could just start to read the values one by one, I’d still use a for-loop though and validate the seperate entries in that array.

Additionally, splitting a string like that, there shouldn’t be any null-values in the returned array. If you don’t specify the StringSplitOptions, it’ll contain empty strings instead if there’s nothing between two seperators.

I’m aware that’s a test setup and you’re controlling what’s being sent. A defensive programming strategy is important though. But ye, I assume you’re under pressure due to deadlines for the project… Anyway, good luck. :slight_smile:

No…

I have no idea why but this doesn’t work