I am trying to get data from an int variable in Unity using C# code.
Below is the C# code I am using to get the int.
using UnityEngine;
using System.Collections;
public class endGameMessage : MonoBehaviour {
public static int score2;
void Start () {
GameObject thePlayer = GameObject.FindWithTag("Player");
gameScript game = thePlayer.GetComponent<gameScript>();
score2 = game.score;
}
// Update is called once per frame
void Update () {
Debug.Log (score2);
}
}
Below is the code from the other script I am trying to pull the data from.
using UnityEngine;
using System.Collections;
public class gameScript : MonoBehaviour {
//score
public int score = 0;
void OnTriggerEnter(Collider other) {
if(other.gameObject.tag =="hammer"){
GameObject.FindGameObjectWithTag("pickUpMessage").guiText.text = ("Picked Up A Hammer");
Destroy(other.gameObject);
Debug.Log("collision detected hammer");
audio.PlayOneShot(gotHit);
score = score+10;
}
}
}
I can get the the int value to come across to the other script but its always 0 even if the int was meant to be 10.
My question is how would i keep the value across the scripts? Any help is appreciated.
try this
public static int score2
{
get
{
return GameObject.FindWithTag("Player").GetComponent<gameScript>().score;
}
}
You have a lot of possibilities.
The first one is to set your Score as a static argument for you gameScript.
So you can access it anywhere just like that :
int myScore = gameScript.Score ;
And the declaration should be :
public static int score;
The second possibilities is far better if you want to save a lot of differents values from differents script.
In this case, you need to define a gameContext singleton.
If you don't know what is this, you should take a look at singleton in C# :
[https://msdn.microsoft.com/en-us/library/ff650316.aspx]
Singleton will allow you to have a single instance of your gameContext.
In your case, your singleton will have a Score attribute.
And you will be able to get the value from any scene and any scripts.
This is the best way so far.
score2 is read once at start and then never again. int is an integral type in C# and thus passed by value i.e. it receives a copy. There several ways to solve this problem.
The easiest solution is to access the gameScript.score directly - it provides read/write access to everyone anyway. To encapsulate it you may choose to define a property.
A better way could be to define a new class GameStatus which holds all relevant things. This can be implemented as singleton for example.
Related
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!";
}
}
}
}
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.
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.
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class VictoryCountdown : MonoBehaviour
{
public float victoryCountdown = 300.0f;
public Text startText;
void Update()
{
victoryCountdown -= Time.deltaTime;
startText.text = "Survive until dawn!: " + (victoryCountdown).ToString("0");
}
}
/////////// 2nd Script below ///////////////////
using UnityEngine;
public class VictoryManager : MonoBehaviour
{
public VictoryCountdown victoryCountdown;
Animator anim;
void Awake()
{
anim = GetComponent<Animator>();
}
void Update()
{
if (victoryCountdown <= 0)
{
anim.SetTrigger("Victory");
}
}
}
Hello, I'm a beginning student so this may be an obvious error. I need to create a countdown timer to end a game. I believe that I have everything working, but ran into this issue at the final hour.
The first script creates a timer and then counts down to 0. The second script triggers the victory screen animation. Unity is returning the error:
Operator <=' cannot be applied to operands of typeVictoryCountdown'
and `int
I made some progress, but hit this hurdle and was hoping someone more experienced could tell me what I have done wrong.
You have a problematic naming convention. Also, you are trying to compare the
VictoryCountDown object with an int and that's not possible.
you can easily fix it like this. But it'll look like a mess...
if (victoryCountdown.victoryCountdown <= 0)
{
anim.SetTrigger("Victory");
}
It looks like you are defining an object of type VictoryCountdown, and you are naming that object victoryCountdown. Your comparison isn't accessing the class variable victoryCountdown, but the object. To get the variable, you have to do something like victoryCountdown.victoryCountdown, where you are accessing the variable through an instance of the class (though I would recommend changing the name of the class instance to avoid this confusion).
I would also note that the variable you want is a float and 0 is an int, so that comparison might not work as well (I'm not as familiar with how C# handles this kind of type disparity)
According to this tutorial demonstrated on youtube
It is working to make a static variable to hold the score and change it in other scripts when the player should be awarded, but it also advised that using static variable in C# script of Unity might not be recommended.
Flowing is what I do to construct the scoring system:
in ScoreManger which was bonded to an UI Text component to show the score:
public class ScoreManager : MonoBehaviour {
public static int score;
Text text;
private void Awake()
{
text = GetComponent<Text>();
}
private void FixedUpdate()
{
text.text = "Score: " + score;
}
}
And the procedure to add the score:
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Player"))
{
ScoreManager.score = ScoreManager.score + score;
Destroy(gameObject);
}
}
So what's the best way to do this without a static variable, and if someone could explain why using a static variable is not recommended will be more grateful.
EDIT
Have tried the event handle way as #rogalski demonstrated in the answer, but the IDE shows that there is an error for type conversion on the lambda function of ExecuteEvents.Execute<T>
You have to remember that Unity is a component based engine meaning that components have to do some specific work independently. This can be well combined with event driven programming methodology which mean that application ( or each of it's components ) are dependant on some event input.
Unity introduced really well designed Event ( or Messaging ) system which you should use instead of making static fields, classes etc.
To point you out in the right way of implementing this I'll use the most trivial example.
( EDIT -> Added definition for EventData which is required for Unity's messaging system )
First register your event :
public interface ICoinPickedHandler: IEventSystemHandler
{
void OnCoinPickedUp(CoinPickedEventData eventData);
}
Then implement this EventTarget into your script :
public class ScoreManager
: MonoBehaviour, ICoinPickedHandler
{
private int m_Score;
public void OnCoinPickedUp(CoinPickedEventData eventData)
{
this.m_Score += eventData.Score;
eventData.Use();
}
// code to display score or whatever ...
}
Now to use this you have to just fire the event :
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Player"))
{
// assuming your collision.gameObject
// has the ICoinPicker interface implemented
ExecuteEvents.Execute<ICoinPickedHandler>(collision.gameObject, new CoinPickedEventData(score), (a,b)=>a.OnCoinPickedUp(b));
}
}
Now you have to create EventData :
public class CoinPickedEventData
: BaseEventData
{
public readonly int Score;
public CoinPickedEventData(int score)
: base(EventSystem.current)
{
Score = score;
}
}
Of course this example requires from you to know the target to which you want to send that event. But if you do not know the target, you can use approach to ExecuteHierarchy.
More on that topic on docs.unity3d.com.
First of all , it would be better to wrap the static variable in a method: addToScore , so in case behavior changes you only have to implement the changes once.
For the static variable: I think it would be better to make the score manager a singleton instance instead of a static variable. Because then if you decide you need more score managers, reset the score manager, save more data in the score manager or whatever it will be a lot easier.
I'm making a game where i want to change which objects my player can collide with by changing the layer mask, but every time I try to change the variable in a different script it throws this error
Error CS0120: An object reference is required to access non-static
member `RaycastController.jumpableCollisionMask'
The code for where i create the variable:
using UnityEngine;
using System.Collections;
[RequireComponent (typeof (BoxCollider2D))]
public class RaycastController : MonoBehaviour {
public LayerMask collisionMask;
public LayerMask jumpableCollisionMask;
The code for where i set the variable
using UnityEngine;
using System.Collections;
public class PlayerChanger : MonoBehaviour {
public float numberOfPlayersPerLevel;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown (KeyCode.E)){
RaycastController.jumpableCollisionMask = 11;
}
}
}
I have tried using a setter but i couldn't get it to work. Thanks in advance and have a nice day =).
jumpableCollisionMask = 11
not
RaycastController.jumpableCollisionMask = 11
Note that you likely have another problem:
You set layer masks (which are just int) like this:
int layerMaskDogs = 1 << LayerMask.NameToLayer("Dogs");
int layerMaskFruits = 1 << LayerMask.NameToLayer("Fruits");
ok?
Never use "= 11" or any number or other value. In Unity it is now only possible to use the form 1<<LayerMask.NameToLayer("Blah")
Finally note you are using public to declare the LayerMask. That can be a bit confusing -- you only do that if you want to set it in the editor. If that's what you want to do, fine. But if you don't need to do that, just use private.
Finally note that you have this is TWO DIFFERENT SCRIPTS!!! This is the most basic problem in Unity. Fortunately it is easily solved:
-- add a public variable to your SECOND script
public class PlayerChanger : MonoBehaviour {
public RaycastController myRC;
-- IN THE INSPECTOR take your "RaycastController" and drag it to that public variable
-- Now use it like this
public class PlayerChanger : MonoBehaviour {
public RaycastController myRC;
...
...
...
//whenever you need to use it...
myRC.jumpableCollisionMask = 11;
Please read the literally 1000s of QA on this! Example, How to access a variable from another script in another gameobject through GetComponent?
I have to access variable ammoMagazine from this class
public class Pistol : MonoBehaviour {
public int ammoMagazine = 7;
}
then i tried this code :
public class AmmoCounter : MonoBehaviour {
public int ammo;
private Pistol _pistol;
void Start () {
_pistol = GetComponentInChildren<Pistol>();
}
void Update () {
ammo = _pistol.ammoMagazine;
guiText.text = "Pistol: " + ammo + "/7";
}
}
Why there's NullReferenceException: Object reference not set to an instance of an object ?
Thanks, im new in C#
There is probably no value assigned to the _pistol field, so either you didn't call Start before you called Update, or GetComponentInChildren<Pistol>() returned null.
public class AmmoCounter : MonoBehaviour {
public int ammo;
private Pistol _pistol = GetComponentInChildren<Pistol>();
void Update () {
ammo = _pistol.ammoMagazine;
guiText.text = "Pistol: " + ammo + "/7";
}
}
Initialize the _pistol-variable before calling the update-function (Check if the GetComponentInChildren()-function returns null)
Note that it's NullReferenceException.
I think for some reason Update() is being called before Start().
I know this has already been answered..but I think there will be some roadblocks ahead for you if you code everything with private variables like the accepted answer. Usually to get a reference in Unity you need to have an object in the scene with the desired attached script. So first you would re-write the script:
public class AmmoCounter : MonoBehaviour {
public int ammo;
public GameObject _pistol;
void Start( ){
ammo = _pistol.GetComponent< Pistol >( );
}
void Update( ) {
guiText.text = "Pistol: " + ammo + "/7";
}
}
In the scene you want an object with the Pistol script attached, then in the editor ( inspector ) you should drag that object into the _Pistol slot. There are several reasons for doing this but two important ones are 1) You're shortening the code and referencing the script once ( the current answer is duplicating the reference ) and 2) Using GetComponentInChildren is inefficient; Unity searches through every game object underneath a parent to get your component. Thus, a direct object reference is better. Also using this type of object reference will save you several headaches in the future, most notably easy reference and making it easier to follow your own code in the future ( you can see the reference in the editor rather than looking through code -- trust me magical references can be a nightmare ).
If you don't want to reference through objects read up on the static type in C#, this allows you to reference items without using GameObjects. However, static types are finicky creatures, so be careful and know what you're doing before using them
edit: grammar