Why i need to declare a variable that have the same name of class and script? - unity3d

public class CanvasManager : MonoBehaviour
{
public static CanvasManager Instance; // = lobby
[SerializeField]
private LobbyFunction _lobbyFunction;
public LobbyFunction LobbyFunction
{
get { return _lobbyFunction; }
}
...
below is one of the reference
private void Start()
{
GameObject lobbyCanvasGO = CanvasManager.Instance.LobbyFunction.gameObject;
...
I am confused that is it necessary to have the same name of canvasmanager that it is declared , and why there is no error when I sayCanvasManager.Instance.LobbyFunction ,it made me confused since LobbyFunction is belonged to CanvasManager, not Instance.
Finally , sometimes ,
private LobbyFunction _lobbyFunction;
private LobbyFunction LobbyFunction
{
get { return _lobbyFunction; }
}
Sometimes,
private LobbyFunction _lobbyFunction;
public LobbyFunction LobbyFunction
{
get { return _lobbyFunction; }
}
Thanks for your patience reading this, and your help would be greatly appreciated, thanks!

Your class is named CanvasManager, but you cannot statically access it right away.
You created a static member variable in CanvasManager, which holds a reference to a CanvasManager. This is called the singleton pattern.
You can only access static members without a class instance. But in the case of singletons, you create a single instance of the class (usually assigned in Start() or in getInstance() (lazy) after checking if it exists) which you can then access statically through "Instance".
Now, Instance is a static variable holding a reference to a single instance of CanvasManager. So, you can then access non-static members and functions of CanvasManager, if you access "Instance".
Think about it like this:
CanvasManager local_instance = new CanvasManager();
local_instance.non_static_member = value; // this works
CanvasManager.static_member = value; // this works
CanvasManager.non_static_member = value; // won't work.
And now one step further, you access the instance via CanvasManager.Instance.*
CanvasManager.Instance.non_static_member = value; // works!
Explanation of static vs non-static:
normal variables:
Variables needs memory. So usually you create 5 instances of CanvasManager and each instance can have different values. Because each instance reserves memory for each Variable. But if you want to change one, you need to explicitly talk to that instance. You could manage them in a List or by having multiple variables in Code like manager1, manager2...
Think of it as books, where each copy can be modified (write notes into it)
static variables
If you create a static variable, the memory is reserved once for the Class. You can then directly get/set this static variable from anywhere in Code without the need of a Reference to an instance.
Think of it as an online blog, where changes are applied for everyone, being accessible from everywhere. The text exists once in the blog database.
Singletons:
If you only want a single CanvasManager and not 5, you could attach it to any GameObject and access it. But every other script needs a reference, like public CanvasManager my_manager which you need to assign in inspector. As an alterantive, you could use
GameObject.Find("CanvasManagerObject").getComponent<CanvasManager>()
in each script... If only there was a better way to access this CanvasManager from everywhere...
The singleton pattern allows you to get a reference to a single, nonstatic instance of the CanvasManager, while it doesn't even need a GameObject it can attach to.
Naming
You are talking about "it has to have the same name" - this is not true. You can name the instance whatever you like. CanvasManager.MyCustomlyNamedInstance would work too. But the MyCustomlyNamedInstance must be a static variable in the CanvasManager class, or any other class. You could have a GameManager that manages your instances, so GameManager.MyCanvasManagerInstance would work too.

Related

Extenject - NullReferenceException when second time inject

I'm new at Zenject(Extenject).
My dev environment: Win10, Unity2020, Extenject 9.2.0
Here is my question:
In installer bind the class
Container.Bind<AccountInfo>().AsCached();
Inject it at classA
private AccountInfo accountInfo;
[Inject]
private void Init(GameSetup _gameSetup, AccountInfo _accountInfo)
{
this.gameSetup = _gameSetup;
this.accountInfo = _accountInfo;
}
accountInfo.address = "xxx'; // works fine
Then inject AccountInfo to classB
private AccountInfo accountInfo;
[Inject]
private void Init(AccountInfo _accountInfo)
{
this.accountInfo = _accountInfo;
}
accountInfo.address = "xxx'; //NullReferenceException: Object reference not set to an instance of an object
Why accountInfo changed to null? AsCached() dosen't work? Or something worng else?
Help please~~ Thank you!
Here is my code:
Installer
"ClassA" inject GameSetup, and create instance, works fine
"ClassB" inject GameSetup, Error: null object
"ClassB" Creator, I'm trying use container.Instantiate() to create it
---update---
gameSetup still Null Object
There are two cases, when injection will not work properly in your code.
The code, that uses injected object is executed before Init. For example if this code is placed in the construcor.
You create your GameObject/Component in runtime whithout using IInstantiator. While you use Znject you always should use IInstantiator to create objects. To do it you should inject IInstantiator to the object, that creates another objects. IItstantiator is always binded in the container by default, so you don't have to bind it manually. For example:
public class Creator : MonoBehaviour {
[SerializeField]
private GameObject _somePrefab;
private IInstantiator _instantiator;
[Inject]
public void Initialize(IInstantiator instantiator) {
_instantiator = instantiator;
}
private void Start() {
// example of creating components
var gameObj = new GameObject(); // new empty gameobjects can be created without IInstantiator
_instantiator.InstantiateComponent<YourComponentClass>(gameObj);
// example of instantiating prefab
var prefabInstance = _instantiator.InstantiatePrefab(_somePrefab);
}
}
Not an expert but I think that passing IInstantiator or the container around is not a good practice. If you need to create injected instances at runtime, then you need a Factory.
From the documentation
1.- Best practice with DI is to only reference the container in the composition root "layer"
Note that factories are part of this layer and the container can be referenced there (which is necessary to create objects at runtime).
2.- "When instantiating objects directly, you can either use DiContainer or you can use IInstantiator, which DiContainer inherits from. However, note that injecting the DiContainer is usually a sign of bad practice, since there is almost always a better way to design your code such that you don't need to reference DiContainer directly".
3.- "Once again, best practice with dependency injection is to only reference the DiContainer in the "composition root layer""

Argument exception when I try to set a variable with a custom set in unity

I tried to use the Generate() function only if a variable has changed without having to check it every frame. I used the following tutorial to achieve this. but for some reason, whenever i try to set the variable, I get this error:
ArgumentException: GetComponent requires that the requested component 'List`1' derives from MonoBehaviour or Component or is an interface.
the script:
public GameObject CEMM;
private int ListLength;
public static int ListLengthProperty
{
get
{
return JLSV.instance.ListLength;
}
set
{
JLSV.instance.ListLength = value;
JLSV.instance.Generate();
}
}
private void Awake()
{
instance = this;
}
I tried to set the value like this: JLScrollView.ListLengthProperty = JLScrollView.instance.CEMM.GetComponent<List<JLClass>>().Count;
The generic type parameter that you use when calling GetComponent must be a class that derives from Component (or an interface type). List is a plain old class object, which is why you are getting the exception from this:
GetComponent<List<JLClass>>()
I'm not really sure what value you are trying to assign to the property. If you are trying to get the number of components of a certain type on the GameObject you can use GetComponents.
JLScrollView.ListLengthProperty = JLScrollView.instance.GetComponents<JLClass>().Length;

Zenject: MonoBehaviour injection

I'm new to Zenject and this is my first project using this asset. I'm having injection problems! Maybe someone knows what I am doing wrong or where the error might be. In the code below, _spawnArea is not initialized.
public class BootstrapIniter : MonoInstaller
{
[SerializeField] private Camera _mainCamera;
[Space(10)]
[SerializeField] private Spawner _spawner;
public override void InstallBindings()
{
BindMain();
BindBallHandle();
}
private void BindMain()
{
Container.Bind<Camera>().FromInstance(_mainCamera).AsSingle();
}
private void BindBallHandle()
{
Container.Bind<Spawner>().FromInstance(_spawner).AsSingle();
}
}
[RequireComponent(typeof(SpawnArea))]
public class Spawner : MonoBehaviour
{
private SpawnArea _spawnArea;
private void Awake()
{
_spawnArea = GetComponent<SpawnArea>();
}
[Inject]
public void Construct(Camera camera)
{
Rect cameraRect = camera.pixelRect;
_spawnArea.Init(cameraRect);
}
}
Thanks in advance for the answer or direction in which to look for a solution
I think that you did not inject your instance.
From the documentaiton "FromInstance - Adds a given instance to the container. Note that the given instance will not be injected in this case. If you also want your instance to be injected at startup, see QueueForInject" (QueueForInject will queue the given instance for injection once the initial object graph is constructed). Basically you need to inject your instance for the injected methods to execute.
On the other hand I dont see the point of binding a monobehaviour from instance, as you have to generate the instance bind it to the container and then inject it. You have binding methods that do this all at once for you, check the section "Construction Methods".
Check for example: FromComponentInNewPrefabResource - Instantiate the given prefab (found at the given resource path) as a new game object, inject any MonoBehaviour's on it, and then search the result for type ResultType in a similar way that GetComponentInChildren works (in that it will return the first matching value found).
Note that for the injection to take place succesfully you have to previously wire up the dependency in the container with the Container.Bind statement so that the container knows what needs to be injected and how.
I suggest to read carefully the documentation which is very good and follow the examples along.

class access control: What if there is readonly access controller?

While I was using class, I found that some attributes, especially the ones which are boolean, are often read by other instances. For example,
class Node{
private:
int item;
bool visited;
public:
bool isVisited(){return visited;}
void bar(){
...
visited=true;
...
}
};
class Graph{
private:
vector<Node> nodes;
public:
void bar(int idx){
if(nodes[idx].isVidited()){
...
nodes[idx].foo();
...
}
}
}
In that case if visited is only changed by the methods of the class Node, then the access controller of the attribute visited shouldn't always be private in perspective of reading. What if there is an access controller 'readonly' that is opened to reading, closed to writing? I think that is useful when defining state attributes. Will there be any side effects?
Have you tried marking the Graph class as friend inside the Node class?
This facilitates accessing the private members of the Node class by the Graph class.
In some languages, there is getter/setter which works as an api of a private value.
It seems like a public value, but internally the methods control the private member variable, not the code with '=' operator itself.
//TYPESCRIPT
class Foo{
private _name:string
constructor(n:string){this._name=n}
get name(){return this._name}
//set name(n:string){this._name=n}
}
const foo=new Foo('Jack jack')
console.log(foo.name) //[LOG]: "Jack jack"
foo.name='hudson' //[ERR]: Cannot set property name of #<Foo> which has only a getter
The code above shows how a readonly property is set. There is only a getter which delivers exactly the same value of the private member variable '_name', but since setter is not defined, only the class itself can change the value and is not able to edit from outside of the class.

what is the use of encapsulation here

What is the use of constructor here ?
This is script A :
[SerializeField]
private LobbyFunction _lobbyFunction;
public LobbyFunction LobbyFunction
{
get { return _lobbyFunction; }
}
This is script B:
private void Start()
{
GameObject lobbyCanvasGO = CanvasManager.Instance.LobbyFunction.gameObject;
if (lobbyCanvasGO == null) return;
}
what if I choose not to use the encapsulation ? no error , I guess .Any help would be greatly appreciated ,thanks!
edit: I guess using encapsulation here make the var read- only , only get... and therefore increase the security , people from outside can't change the value ,is it the ans?
This is no constructor but a Property
A property is a member that provides a flexible mechanism to read, write, or compute the value of a private field. Properties can be used as if they are public data members, but they are actually special methods called accessors. This enables data to be accessed easily and still helps promote the safety and flexibility of methods.
In your case it is for granting Read-Only access to the private backing field _lobbyFunction so no other class can change its value since only the class "A" containing _lobbyFunction itself is allowed to assign it.
Btw the way you have it it is equivalent to simply write
public LobbyFunction LobbyFunction { get; private set; }
without the need for a backing field. Then still only the containing class "A" itself is allowed to assign a value while everyone else can read it.