0

I'm making a Unity3D game. I want to implement a connection between the script Timer.cs and Collide.cs, by which they exchange the variable obji. And before you mark this question as a duplicate I want to mention that have already read this tutorial. As a result of the solution provided I get the error

A namespace cannot directly contain members such as fields or methods

Can you provide a solution for exchanging information between scripts that have no element in common. I want Timer.cs to get the variable obji from Collide.cs

Timer.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Timer : MonoBehaviour
{
    public ScoresManager ScoresManager;
    Text instruction;
    // Start is called before the first frame update
    void Start()
    {
        instruction = GetComponent<Text>();
        InvokeRepeating("time", 0, 1);

    }
    void time() {


        if (timeLeft <= 0){
        /*   if(move.obji() <= 0){
                instruction.text = "You win!";
            }else{
                instruction.text = "You lost!";
            }*/


} else {
            timeLeft = timeLeft - 1;
            instruction.text = (timeLeft).ToString();
        }
    }
    // Update is called once per frame
    int timeLeft = 30;

    void Update()
    {
    }
}

Collide.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
    public class Collide : MonoBehaviour
{
    public Text txt;
    public int obji = -1; //this is an example, I always try to initialize my variables.
    void Start()
    { //or Awake
        obji = GameObject.FindGameObjectsWithTag("Enemy").Length;
    }
    void OnCollisionEnter(Collision collision)
    {

        if (collision.collider.gameObject.tag == "Enemy")
        {

            transform.localScale -= new Vector3(0.03F, 0.03F, 0.03F);


            Destroy(collision.collider.gameObject);
            obji = obji - 1;
            Debug.Log(obji);

            if ((obji) > 0)
            {
                txt.text = (obji).ToString();
            }
            else {
                txt.text = "You win!";
            }
        }
    }
}

Editor view 1

Editor view 2

Dimitar
  • 4,402
  • 4
  • 31
  • 47
Atanas
  • 83
  • 1
  • 1
  • 11
  • 5
    Possible duplicate of [In Unity, how can I pass values from one script to another?](https://stackoverflow.com/questions/13891892/in-unity-how-can-i-pass-values-from-one-script-to-another) – AresCaelum Jan 31 '19 at 19:06

2 Answers2

3

Communication between scripts like this (sharing properties of one class with another class) is a very common task in Unity. The script that needs the value of a property of another class should get a reference to that other class.

In your example, since Timer needs to access the obji property from the Collide class, you need to add a reference to the Collide class to the Timer class:

public class Timer : MonoBehaviour
{
    public Collide _collide;

    // The rest of the script...
}

Then, in the Inspector in Unity, you need to drag a GameObject that has the Collide script attached to the _collide property of the GameObject with the Timer script attached.

Finally, you can access the obji property through your newly created reference:

if (_collide.obji > 0)

See this tutorial from Unity which covers this topic in depth.

Lews Therin
  • 3,707
  • 2
  • 27
  • 53
0

The error you've once received:

A namespace cannot directly contain members such as fields or methods,

tells you that in a namespace cannot be placed any methods or fields (i.e. variables) directly. A namespace can only contain

  • classes,
  • interfaces,
  • enums,
  • delegates,
  • structs
  • namespaces.

Generally speaking, a namespace is used to provide certain scope and organize entities.


There are many ways you can get access to another class's member fields. The cleanest and simplest way is through a so-called Getter method (also through get properties). You should avoid using and referencing public fields. For example, in your Collide class

// You don't have to always initialize your fields: they have default values. 
// Initialize only when you need to. 
private int obji;

...

public int GetObji() {
    return obji;
}

Now, to call that method you need a proper reference to it. For that you can simply add that as a parameter in your Timer class:

public Collide CollideRef;
...
// Get the field
CollideRef.GetObji();

And then just drag and drop the GameObject, having the Collide component onto it.

Dimitar
  • 4,402
  • 4
  • 31
  • 47
  • 1
    `obji` is a property already and has a [getter method](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/get). The only part OP was missing was adding a reference to the `Collide` script. – Lews Therin Jan 31 '19 at 19:11
  • 1
    @LewsTherin No, it doesn'T have a getter method. It is global and utilisation of global variables leads to poor code quality and harder maintenance. – Dimitar Jan 31 '19 at 19:15
  • [Global variables don't exist in C#](https://stackoverflow.com/a/14368428/4416750). My terminology was off. You are correct that it is not technically a property with a getter; But my point still stands that OP only needs a reference to `Collide` to be able to access the `obji` public field, as described in my answer. – Lews Therin Jan 31 '19 at 19:27
  • @LewsTherin By global I meant *global to a class*. In your link, they speak of **true** global variables, which I believe are not part of many, if any, modern day languages. About your point, while true, it is just a bad practice and can lead to various code smells. – Dimitar Jan 31 '19 at 19:31
  • But, much like I had to correct my terminology, you should correct yours. *Global variable* means something completely different than *public field*. While I agree that using public fields is a poor design choice from a purely C# perspective, it is an extremely common practice in Unity scripts due to public properties automatically being exposed to the inspector. Unity's own tutorials and documentation use public fields all the time. – Lews Therin Jan 31 '19 at 19:39
  • If it is recommended by the Unity developers, I'm sure there is a good reason (like the engine is probably optimized for handling public fields). We have no way of knowing because Unity is not open source. Beginners should stick to the method recommended by the engine or framework they are working in. By correcting your terminology I was referring to your incorrect use of the term *global variables*. I didn't downvote your answer. I come to this site for discussion and don't have enough rep to be downvoting everyone I don't agree with. – Lews Therin Jan 31 '19 at 19:54
  • Firstly, I don't see how can this be "optimization" of any sorts. More so, the problem is clearly of code style, not engine intricacies. Secondly, Unity have an OS reference at GitHub and there are many unofficial decompilations, too. So there is way. About the downvote, okay then I apologise for my previous comment. A fly-by downvote is irritating. – Dimitar Jan 31 '19 at 20:10
  • 1
    Well you taught me something! I didn't know that they [open-sourced the engine](https://blogs.unity3d.com/2018/03/26/releasing-the-unity-c-source-code/) ([GitHub link](https://github.com/Unity-Technologies/UnityCsReference)). I don't have time to dig through it now, but I still think that there must be some reason the Unity devs recommend public variables so often (they must know about Encapsulation and that Microsoft recommends properties instead of public fields in C#). Fly-by downvoting and leaving no reason is one of the most annoying things on this site! Thanks for keeping this civil :) – Lews Therin Jan 31 '19 at 20:31