Access variable in C# - c#

I have a problem that I want to solve. I have two classes
public class Class1
{
[ReadOnly] public int Selector
private void Start()
{
Selector = Random.Range(0, 4);
Debug.Log("Selectorul " + selector);
}
}
public class Class2
{
private Class1 sp;
private void Start()
{
Debug.log(sp.Selector);
}
}
I'm a beginner and I want to understand why when I start Unity the second class the selector is always 0?
maybe I do not get right!!!
Help!!!

It seems like you are using Unity.
In Unity, the Start method of a script is only called if the class derives from MonoBehaviour, which yours does not at the moment.
Fix this by simply replacing
class Class1
with
class Class1 : MonoBehaviour
Now, if you drag the script onto a GameObject, the Start method of Class1 should get called.

You never initialize the seed for the random number generator so every attempt will generate the same number (in your case 0).
Use Random.initState (int seed) before Selector = Random.Range(0, 4);
But again this might not even be your problem. Since both Debug.log() methods are in Start() how do you make sure which one is ran first? Also the Classes are not derived from MonoBehaviour so do you call Start() yourself? or?
For instance Class2 Start() might run before Class1 sets the value of selector.

Related

Calling an Object from another Script

This might be a super simple question, but for some reason I can't get it to work:
I have two scripts, both attached to the same GameObject.
One script has a dictionary:
public class RPG_Implementierung : MonoBehaviour
{
public Dictionary<string, string> StoryText = new Dictionary<string, string>();
void Start()
{
StoryText.Add("1", "This is the first Entry");
}
}
The other script wants to call that Dictionary. The method SendMessageToChat` is defined in this script and works well as long as it's not referencing the other script.
The first thing I tried didn't work, I get the Error:
CS0120 An object reference is required for the non-static field, method, or property
public class GameManager : MonoBehaviour
{
void Update()
{
if (Input.GetKeyDown(KeyCode.Y))
{
SendMessageToChat(RPG_Implementierung.StoryText["1"]);
}
}
}
I
this also doesn't work, it gives me the Error
CS0119 'RPG_Implementierung' is a type, which is not valid in the given context
public class GameManager : MonoBehaviour
{
void Update()
{
if (Input.GetKeyDown(KeyCode.Y))
{
SendMessageToChat(GetComponent(RPG_Implementierung).StoryText["1"]);
}
}
}
Can someone please tell me what I did wrong? In standard C# all I would have to do is to set the other class to public and then I can reference it and access it's objects, why doesn't this work in Unity?
To reference another component on a GameObject, you will need to grab that reference either by serializing the field in the inspector (Making it public or using the attribute [SerializeField].
I am not sure how many places you want to eventually call the method you are trying to invoke, but if it is from a bunch of different places, you might want to consider the Singleton pattern.
To quickly fix your current issue, on your GameManager.cs, do one of these two things:
public class GameManager : MonoBehaviour
{
[SerializeField] private RPG_Implementierung rpgImplement = null;
// OR
public RPG_Implementierung rpgImplement;
void Update()
{
if (Input.GetKeyDown(KeyCode.Y))
{
SendMessageToChat(rpgImplement.StoryText["1"]);
}
}
}
Edit: If you want to use the GetComponent in the Update here is how you would call it. I would advise against this as calling a GetComponent in an Update can be quite costly for performance if called frequently. It is better to store the reference to later use.
public class GameManager : MonoBehaviour
{
void Update()
{
if (Input.GetKeyDown(KeyCode.Y))
{
SendMessageToChat(GetComponent<RPG_Implementierung>().StoryText["1"]);
}
}
}

How can i re-use a code in monobehaviour class?

First i'd like to give a short version of my question:
How can i access another code pieces attached to another game object, or how can i initiazlie a class without have an game object attched.
When i making a small game in Unity, i made an unit designer where you give some value such as how many weapon does it carry, and the status of that unit (attack, range, speed, etc.) will be calculated by ComputeValues() and saved when you click confirm. But all those values were adjusted by clicking a button instead of direct input. (I.e. Click a button and add/reduce 1 weapon)
However, when i try to add some template unit at start up it won't work. So i made a CreateDesignWithValue() function. Which takes input for all the related data, and use the ComputeValues() above to compute the value for that object.
The problem is i'm trying to do it in player class. But i can't create new ShipDesigner, and neither can i set it to static. How can i get access to it?
Without knowing you exact usecase and what the methods do you are talking about we can only give a very general answer:
Not all classes have to be of type MonoBehaviour it really depends on your needs.
Extension Methods
If you have a certain calculation for a certain type you can use Extension Methods like
public static class Vector3Extensions
{
public static Vector3 DevideBy(this Vector3 a, Vector3 b)
{
return new Vector(a.x / b.x, a.y / b.y, a.z / b.z);
}
}
which you can use like e.g.
var newVector = transform.position.DevideBy(new Vector(1, 2, 3));
in all other classes.
public static class
In general you can use a public static class to implement methods and store values that shall be executable from everywhere e.g.
public static class Settings
{
private static int _currentInt = 7;
public static void SaySomething(string something)
{
Debug.Log(something);
}
public static void DoubleCurrentInt()
{
_currentInt *= 2;
}
public static int GetSquareOfCurrentInt()
{
return _currentInt * _currentInt;
}
}
which you can call now from everywhere like
Settings.DoubleCurrentInt();
Settings.SaySomething(Settings.GetSquareOfCurrentInt.Tostring);
Instances
Ofcourse sometimes you do not want that something is accessible from everywhere so you can also simply have a normal instanced class for your calculation like
public class Settings
{
private int _currentInt = 7;
public Settings(int initialInt = 0)
{
_currentInt = initialInt;
}
public void SaySomething(string something)
{
Debug.Log(something);
}
public void DoubleCurrentInt()
{
CurrentInt *= 2;
}
public int GetSquareOfCurrentInt()
{
return CurrentInt * CurrentInt;
}
}
So you can use
private Settings settings;
private void Start()
{
new Settings(3);
}
in one MonoBehaviour and
private Settings settings;
private void Start()
{
new Settings(26);
}
in another MonoBehaviour, both have different instances but can use all the implemention in it for calculating and doing stuff individually.
public static void
you can also only "share" one method among all instances of a certain type (static) and also allow other types to access it (public)
public class A : MonoBehaviour
{
// A prefab only this specific component has access to
[SerializeField] private GameObject prefab;
// example for a kind of singleton pattern
private static GameObject prefabSingleton;
private void Start()
{
prefabSingleton = prefab;
}
public static void Spawn(int someIntToAssign, string someTextToAssign)
{
var obj = Instantiate(prefabSingleton)
;
componentReference = obj.GetComponent();
componentReference.someIntField = someIntToAssign;
componentReference.Getcomponent<Text>().text = someTextToAssign;
}
}
this you can call from other types as well like
A.Setup(someExampleReference, "Yeay!");
(in this example you could consider to rather implement it in SomeExampleType, though ^^)
ScriptableObjects
What you described also sounded like ScriptableObjects (Tutorial) might be interesting for you.
ScriptableObjects are kind of assets similar to prefabs but can store values and also methods. You than can reference them in fields of MonoBehaviour components to change their behaviour according to the values or in order to share it as kind of container between multiple instances and different types.
Instance with public method
Last but not least the most "usual" of doing it would be to have a
public class A : MonoBehaviour
{
[SerializeField] private Transform someObject;
public Vector3 GetObjectPosition()
{
return someObject.position;
}
}
and access it via one of the many GetComponent or/and FindObjectOfType variants or simply by referencing the according component like
public class B : MonoBehaviour
{
// drag in via the Inspector
public A AReference;
private void Start()
{
// or get it on runtime e.g.
AReference = GameObject.Find("ObjectWithA").GetComponent<A>();
// or if there is only one e.g.
AReference = FindObjectOfType<A>();
Debug.Log(AReference.GetObjectPosition());
}
}
Answer of short versions:
How can i access another code pieces attached to another game object:
Declare a public field for the script you want to reach e.g. public ExampleScript exampleScript; and assign the gameobject which has ExampleScript to your field in the inspector.
how can i initiazlie a class without have an game object attched: You can't create an instance of a script derived from MonoBehaviour just like new ExampleScript();. But instead you can add that script to your existing gameobject with gameObject.AddComponent<ExampleScript>(); and you can reach this script from another script which is attached the very same gameObject like: gameObject.GetComponent<ExampleScript>();

How to send integer value to another script?

I am making a game where the player first has to choose the type of control to use before playing. The three options being: Keyboard, Controller, Touch
The player must click the button corresponding to his choice. Each button runs this script when clicked on:
public class KeyboardButton : MonoBehaviour {
public static int controller;
public void buttonClick () {
controller = 1;
}
}
In reality, each button as its own script, where the value of controller is different depending on the script ran. The idea is that the value of this integer would be sent over to the script responsible of controlling the player so it will make use of the demanded input type. ie: if the keyboard button is selected, it will run the corresponding script, setting the integer value to 1. After the PlayerController script receives this value, it will know to only accept input from the keyboard.
I have consulted a lot of documentation, but a lot of it contains context-specific C# things that I don't understand and are irrelevant to what I want to do.
Also, I would not like an answer around the lines of: "You don't have to make the player choose a control type, here's how you can make your game accept all types of control at once." I already know all this stuff and there is a reason I want the player to make a choice. Furthermore, I would still like to know a way to transfer integers to be able to be more organized, rather than having a single script that does 90% of the things in the game.
There are three way you can pass value to another script.
GetComponent
You can use GetComponent method to get another script.
public class KeyboardButton : MonoBehaviour {
public int controller;
//this is anotherScript instance
public AnotherScript anotherScript;
Start()
{
anotherScript = GameObject.Find("Name of Object").GetComponent<AnotherScript>();
}
public void buttonClick () {
controller = 1;
anotherScript.sendValue(controller); //send your value to another script
}
}
Singleton
Let AnotherScript be a static Singleton,You can get the instance on other side.
public class AnotherScript : MonoBehaviour
{
//need to be static
public static AnotherScript Current;
Start()
{
if(Current == null)
{
Current = new AnotherScript();
}
}
public void sendValue(int val)
{
//todo
}
}
public class KeyboardButton : MonoBehaviour
{
public int controller;
public void buttonClick () {
controller = 1;
AnotherScript.Current.sendValue(controller);//send your value to another script
}
}
SendMessage
If you want to send a value to otherscript,SendMessage is a simple way you can choose.
ps:SendMessage method can just send a parameter.
public class KeyboardButton : MonoBehaviour
{
public void buttonClick ()
{
controller = 1;
GameObject.Find("name of object").SendMessage("sendValue",controller);
}
}
As pointed out in one of the comments, you already exposed that value, you can refer to is via
Debug.Log(KeyboardButton.controller);
without providing an instance. There's multiple other ways of doing it, as this way is only good to a certain level of complexity, after which it starts to get more muddy, but depending on what you need right know it might get you through. It is one of the valid ways, and probably the simplest one.
You may also want to know when the value has changed, for example you could use UntiyEvent and trigger it when value is changed
public class KeyboardButton : MonoBehaviour {
public UnityEvent OnValueChanged;
public static int controller;
public void buttonClick () {
controller = 1;
OnValueChanged.Invoke();
}
}
this is if you like to wire events in the editor. You could also do:
public class KeyboardButton : MonoBehaviour {
public static UnityEvent OnValueChanged;
public static int controller;
public void buttonClick () {
controller = 1;
OnValueChanged.Invoke();
}
}
the downside is that the event won't show up in the editor,but the upside is that you can set up a trigger without having to have a reference to the KeyboardButton instance that just got clicked.
public class ControllerChangeReactor : MonoBehaviour {
void Start()
{
KeyboardButton.OnValueChanged.AddListener(React); // add event listener
}
void React() // will get called when keyboard is clicked
{
Debug.Log(KeyboardButton.controller);
}
}
This approach can become limiting after you've written a dozen or so of those scripts, but a step up involves tailoring a custom system which is probably not worth it on your level (just yet). You can finish a simple game using the above approach, even if its not the most elegant.
You could also parametrize your script (expose 'WHAT DOES IT CHANGE' in editor), to avoid unnecessary multiplication of code

Inspector value cannot access from another class in Unity3d

I have two classes. One called GameManager and another one Enemies.
I have two variables in GameManager which I have changed from inspector currentLevel=1 and totalEnemy=10.
// GameManager.cs
private static GameManager instance = new GameManager();
public static GameManager get(){ return instance; }
public int currentLevel;
public int curLevel { get; set; }
public int totalEnemy;
public int totLevel { get; set; }
void Start () {
curLevel = currentLevel;
totLevel = totalEnemy;
}
I'm trying to access these two variable from Eneimes class like this; but everytime it gives me curLevel = 0, but I'm expecting to get curLevel = 1. What I'm doing wrong?
// Enemies.cs
void Start () {
Debug.Log (GameManager.get().curLevel); // always output = 0
}
The line private static GameManager instance = new GameManager(); is the issue.
When a script is attached to a GameObject, an instance of the type of the script is referenced as this inside the script. In other words, there can be multiple instances of same type if the same script is attached to multiple GameObjects.
Therefore, the specific instance that have curLevel = 1 as you set in the Inspector is an instance of the type attached to the specific GameObject. This means the one should be referred to as this inside the script.
If you declare a new instance of GameManager as in your code, you are basically ignoring all values in the Inspector because the static GameManager instance is pointing to a different instance than the instance you set values for in the Inspector.
In order to use the specific instance that you declared using the Inspector, you should do the following.
using System.Collections.Generic;
using System.Collections;
using UnityEngine;
public class GameManager : MonoBehaviour
{
private static GameManager instance;
public static GameManager get() { return instance; }
public int currentLevel;
public int curLevel { get; set; }
public int totalEnemy;
public int totLevel { get; set; }
void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Debug.LogError(string.Format("GameManager.Awake(): More than one instances of this type {0} is being initialised but it's meant to be Singleton and should not be initialised twice. It is currently being initialised under the GameObject {1}.", this.GetType(), this.gameObject.name));
Destroy(gameObject);
}
curLevel = currentLevel;
totLevel = totalEnemy;
}
}
Note that I changed Start() to Awake(). This is because you are referring to values initiliased in this method from other scripts, and you cannot guarantee which Start() is called first between different MonoBehaviours in the runtime. However, Unity guarantees that Awake() is always called earlier than Start(). Further, it is Unity's best practice to initialise self-initialisable variables in Awake(), and initialise variables dependent on other scripts in Start() because of this execution order.
Lastly, there will be problems when there are multiple GameObject that has GameManager as its component in your scene. Consider a case where you have two such objects. when the scene is loaded, each of the script will call Awake(), and both of them will set private static GameManager instance; to each of the two this. The result would be one is overriden by another.
You could say that you will be careful to use this script and make sure only one GameObject has this script as its component. However, you should always write your code as if someone who do not know about your code can use it without thinking, and stupid mistakes of other people new to the project could be easily detected.
EDIT:
To respond to the OP's comment, I added code to handle when this type is initialised more than once in the project. In addition to #Kardux's suggestion, I added Debug.LogError() because I do not want the project to silently solve things. If a problem happens, I want to get notified of it.
If you are using Singletons frequently in your project, you might want to have a parent abstract class Singleton that handles this instance checking process for all child Singletons, and have GameManager inherit from Singleton.
However, use Singleton with care as it is considered a bad design pattern if misused. (And I don't know how to use it properly so I avoid using it.)

Access variable from another non MonoBehaviour class

Hi I would like to get a variable from another class in Unity
public class CameraMove : MonoBehaviour {
public GameObject lookTarget;
public GameObject MainCamera;
public GameObject nextMovePoint;
private int currentPoint = 0;
[System.Serializable]
public class Paths
{
public float time;
public Transform[] movePoints;
}
public Paths[] Path;
void Update()
{
Paths paths = gameObject.GetComponent<Paths>();
Debug.Log (paths.movePoints [currentPoint]);
if (nextMovePoint.transform.position != paths.movePoints [currentPoint].position)
{
currentPoint += 1;
nextMovePoint.transform.position = paths.movePoints [currentPoint].position;
}
iTween.MoveTo(nextMovePoint,iTween.Hash("time",paths.time));
}
I want to get the movePoints from class Paths, but GetComponent gives me an error? How do I get that variable?
Still struggling with this, anyone got an idea?
You do not need to use GetComponent to get access to your paths.
First, I'm going to clean up your naming a little bit so that what we're talking about a little clearer:
public class CameraMovement : MonoBehaviour {
public GameObject LookTarget;
public GameObject MainCamera;
public GameObject NextMovePoint;
public Path[] Paths;
private int _currentPoint = 0;
}
[System.Serializable]
public class Path {
public float Time;
public Transform[] MovePoints;
}
As I understand it, you want to use your Paths in your Update event. Because Paths is just a regular variable on the component, like LookTarget or MainCamera, you can just refer to it directly. Thus:
void Update() {
Path path = Paths[/*some index*/];
Debug.Log(path.MovePoints[_currentPoint]);
// etc.
}
GetComponent is used to grab a different MonoBehaviour which is attached to the game object. When it's part of the serialized information, it's already on your class and you can just use it directly.
From you code I see that your Path class is inner(local class) of your CameraMove class. So I think you want to move it to separate class(read like file) and attach that as component to what ever game object you need. So then you can use getComponent<Path>() on that object. However if you want Path class to be inner class so you need to initiate (in Start())it like
Path[0]=new Path(); //etc
Then you can use it inside your CameraMove class as
Path[0].movePoints[currentPoint];
But if you explain more what you want to achieve then I can provide more accurate solution.
The primary objective of GetComponent is to get a component attached to the game object. This means that GetComponent can only get objects of classes deriving from UnityEngine.Component. Note that MonoBehaviour is a subclass of Component, so any and all MonoBehaviour subclasses are subclasses of Component. Also note that only components, i.e. classes deriving Component show up in the inspector of a game object.
The class Paths does not inherit the class Component, either directly or indirectly. Thus it can't be added to an object or retrieved from an object using GetComponent.
As you specify in the comments, an object of type CameraMove.Paths is in another component of type CameraMove. If the other CameraMove component is on another game object, things become incredibly easy. You won't even need GetComponent for this. Just create a field public CameraMove otherCameraMove (I prefer a private field with the SerializeField attribute, but it doesn't matter here since you're obviously new to Unity) and select the other CameraMove object in the inspector. You'll be able to access the paths variable as otherCameraMove.Path.
If the other CameraMove component is on the same object however, things get more tricky. I, for one, suggest that game objects should never, ever have more than one instance of the same component. But if you do, you'll have to use GetComponents<CameraMove>() to retrieve all components of type CameraMove on the game object and iterate through the array looking for the CameraMove component which you want. This is just one of the reasons why in my opinion same components shouldn't be added to the same object more than once.

Categories

Resources