Heritage, child variable not working - c#

This problem is on Unity, but I think I am just doing the c# side wrong.
EDIT
It looks like by doing the code and storing a child Class B of parent Class A, and by saying is a Class A type, by modifing my variable of type Class A containing Class B I modify some sort of hybrid Class A/B that doesn't represent my real Class B Script
What I do is, having multiple script on different prefabs. Each of those script represent an item and all have has parent Usable which is a class that I actually use like an interface, but that will in the future get some stuff.
The full WeaponLaser and Usable script is below
When the player go over a drop, I instantiate the gameObject containing the script like this (using prefab)
GameObject Item = Instantiate(droppedItem, transform.position, Quaternion.identity);
Item.transform.parent = transform;
usableItem = droppedItem.GetComponent<Usable>();
usableItem.OnUsed += ReleaseItem;
and Use the item like this
if (usableItem != null)
usableItem.Use(firePoint.position);
The thing is, it looks like the script I call when I do Use() is another version.
I mean, If I set int fireCurrentShoot = 10; on top of the script WeaponLaser and then trought code in the Start for exemple I do fireCurrentShoot = 2;
It will work on the inside the script WeaponLaser, but when I call it using the above code
if (usableItem != null)
usableItem.Use(firePoint.position);
It will show fireCurrentShoot = 10 so without the modification
END EDIT
Hello,
I have a problem with heritage I don't understand, I cleaned all my class, and still I can't find why.
I have a class A :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Usable : MonoBehaviour
{
protected virtual void Start()
{
}
protected virtual void Update()
{
}
public virtual void Use(Vector3 pos)
{
}
protected virtual void Used()
{
}
}
and a class B
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WeaponLaser : Usable
{
const int SHOOT_AVAILABLE = 5;
const float FIRE_COOLDOWN = 1;
float fireCurrentCooldown = 0.0f;
int fireCurrentShoot = 0;
protected override void Start()
{
base.Start();
Debug.Log("start");
fireCurrentShoot = SHOOT_AVAILABLE;
Debug.Log("fireCurrentShoot" + fireCurrentShoot);
}
protected override void Update()
{
Debug.Log(fireCurrentShoot); // value is = 5
base.Update();
}
public override void Use(Vector3 shootPosition)
{
Debug.Log(fireCurrentShoot);// value is = 0
base.Use(shootPosition);
base.Used();
}
void FireCooldown()
{
}
}
when I call Use, my Debug.Log of booth value give 0... but I am expecting to have fireCurrentShoot = 5
I call it like this *:
usableItem = droppedItem.GetComponent<Usable>();
usableItem.Use(firePoint.position);
why he is equal to 0?

Your inheritance seems fine, so the problem might be in the way you use these objects in Unity. This makes it a bit more difficult to be sure what the solution is, but here is what you could try:
Be sure that Start is called: I think it is the case for you since
you say you see a debug print, but remember that Start is not called
on objects that are deactivated in the Editor, so some values that
you set in start will not be initialized.
Name your objects in unity editor with unique names and add the name in the debug
Debug.Log(fireCurrentShoot + ", " + name);
This should help you sure you see the values for the object that interests you
You can also replace
int fireCurrentShoot = 0;
by a property:
int _fireCurrentShoot = 0;
private int fireCurrentShoot
{
get{ return _fireCurrentShoot;}
set{ _fireCurrentShoot = value; Debug.log("fireCurrentShot set to " + _fireCurrentShoot);}
}
this will allow you to see a message when the value is modified. You
can also set a debug point on the setter to see the callstack
EDIT:
I think I got it: you don't register to the instanciated object, but to your prefab
replace
usableItem = droppedItem.GetComponent<Usable>();
by
usableItem = Item.GetComponent<Usable>();

Related

Why do my variables keep going back to 0 and why do my variables look different on the screen then in reality?

So for some reason when I try to access variables from another script the text is showing what I put but when I change things in the editor the accrual values don't change. even if in the code I put playerPoints to 9 when I run it its 0 during a collision. I have all the scripts and objects connected up, and when I try to flip where the variables are, make them in the destroy script, for some reason, it doesn't work. it wont let me use public BallsText bt; it will return an error even though it works fine for the other. I'm sorry if all of these are really basic questions but I've looked as far as I can on the internet and I cant find anything, so any help would be appreciated.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class BallsText : MonoBehaviour
{
public Destroy destroy;
// public int playerPoints = 0;
//public int enemyPoints = 0;
//int playerPoints = 0;
//int enemyPoints = 0;
public Text playerPointsText;
public Text enemyPointsText;
void Update()
{
playerPointsText.text = destroy.playerPoints.ToString();
enemyPointsText.text = destroy.enemyPoints.ToString();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Destroy : MonoBehaviour
{
public int playerPoints = 9;
public int enemyPoints = 0;
void OnCollisionEnter2D(Collision2D col)
{
if (this.name == "Destroy.cs")
{
Debug.Log("i have a doughnut");
}
if (col.gameObject.name == "Player")
{
Debug.Log(playerPoints);
playerPoints++;
Debug.Log(playerPoints);
Debug.Log("at least im a dounut");
Destroy(this.gameObject);
}
else if (col.gameObject.name == "Enemy Zone")
{
enemyPoints++;
Destroy(this.gameObject);
}
}
}
You can hide them from the inspector's view with HideInInspector while still being public.
[HideInInspector] public int playerPoints = 9;
You can also use the reset script to return the setting of public numbers to the first state.
Like Everts said, you had the value at zero and created an instance of the script. Now when you change the script Unity will serialize (save) all the public/serialized fields, then reload the script, then deserialize (load) all the public/serialized fields it previously saved. This means your values are locked into the instance that you previously used. If you make a new instance, you get the current values, and if you reset the script you'll get the current values.
This has also bitten me enough times that I don't set default values when variables are declared anymore. If you have a value that you want to use, set those values in Start() or Awake().
The advantage here is that whatever is in the script will get overwritten when play mode starts. The disadvantage is that you can't customize those values on a per-instance basis anymore, because all instances of the script will all load the same default values when play mode starts. If this matters to you, and you want to be able to customize those values, then unfortunately you'll need to go to each script and change those values manually.
If you use a property with an automatic backing field then you won't be able to see it in the editor
public class Destroy : MonoBehaviour
{
public int playerPoints{get; set;} = 9; // Can't see this in the editor
public int enemyPoints{get; set;} = 0; // Can't see this in the editor
If you use a property with an explicit backing field then you can expose the backing field to the editor with the [SerializeField] tag, but then you've got the same problem you've got now - the editor will serialize that field and subsequent changes to the script won't affect instances:
public class Destroy : MonoBehaviour
{
[SerializeField]
private int playerPoints_ = 9; // Instances will "lock in" values and later changes to the script here won't take effect
[SerializeField]
private int enemyPoints_ = 0; // Instances will "lock in" values and later changes to the script here won't take effect
public int playerPoints
{
get=>playerPoints_;
set{playerPoints_ = value;}
}
public int enemyPoints
{
get=>enemyPoints_;
set{enemyPoints_ = value;}
}
If you keep the fields public (and thus exposed to the editor) but set the values at runtime in Awake() or Start() then you can see the values in the editor but the editor values for all instances will be overridden when play mode starts:
public class Destroy : MonoBehaviour
{
public int playerPoints; // Doesn't matter what the value is here on instantiation becuase you'll override it on Awake()
public int enemyPoints; // Doesn't matter what the value is here on instantiation becuase you'll override it on Awake()
public void Awake()
{
playerPoints = 9; // Will override *every* instance's values with this.
enemyPoints = 0; // Will override *every* instance's values with this.
}
:EDIT:
I'll add too that repeatedly polling is wasteful. If you use events then you can subscribe and get notifications when there's something to see. Consider instead:
public class Destroy : MonoBehaviour
{
private int playerPoints = 9;
private int enemyPoints = 0;
public System.EventHandler<int> OnPlayerPointsChanged;
public System.EventHandler<int> OnEnemyPointsChanged;
void OnCollisionEnter2D(Collision2D col)
{
if (this.name == "Destroy.cs")
{
Debug.Log("i have a doughnut");
}
if (col.gameObject.name == "Player")
{
Debug.Log(playerPoints);
playerPoints++;
OnPlayerPointsChanged?.Invoke(this, playerPoints);
Debug.Log(playerPoints);
Debug.Log("at least im a dounut");
Destroy(this.gameObject);
}
else if (col.gameObject.name == "Enemy Zone")
{
enemyPoints++;
OnEnemyPointsChanged?.Invoke(this, enemyPoints);
Destroy(this.gameObject);
}
}
}
Now Destroy has two public events that fire when the public or enemy points change. Anyone subscribing to those events will get notified when the points change, and part of the event notification is the current point value.
Then your other script subscribes to the events and does whatever they need to when they receive that event. Here they'll convert the points .ToString() and update the Text values:
public class BallsText : MonoBehaviour
{
public Destroy destroy;
public Text playerPointsText;
public Text enemyPointsText;
private void Start()
{
destroy.OnPlayerPointsChanged += PlayerPointsChanged;
destroy.OnEnemyPointsChanged += EnemyPointsChanged;
}
public void PlayerPointsChanged(object sender, int points)
{
playerPointsText.text = points.ToString;
}
public void EnemyPointsChanged(object sender, int points)
{
enemyPointsText.text = points.ToString();
}
}
Last note here is that your Destroy script increments enemyPoints but then also immediately destroys the gameObject, so I don't see the point in incrementing enemyPoints unless there's something else accumulating enemy points. With the subscription model that's totally doable - you could have something else subscribing to the Destroy script models and they'll get notifications before the script self-destructs.

Unity communication between scripts

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.

Check the previous loaded scene

I'm making a game in Unity3D with C# for mobile devices and can't figure out how to check which scene was loaded before the current scene. I need to check this to change the spawn point from the player gameobject. First I added a simple script to my buttons (loadnextscene and loadprevscene)
public class SwitchScene : MonoBehaviour {
public int sceneNumber;
public void LoadScene(int sceneNumber) {
Application.LoadLevel(sceneNumber);
}
}
A second scripts handles the touch input from the user and changes the movement of the player object.
So, for example: If the player clicks on the "load previous scene" button in the second Level to switch to the first level again, I want to set the spawn point of the player object on the right half on the screen and not on the left side like when the game was started the first time.
I tried it with Singleton and PlayerPrefs, but it did not work out.
You need to save the scene number to some variable before LoadScene, then check it after the scene loaded.
The only problem is that this variable will be destroyed after the new scene is loaded. So, to prevent it, you can use DontDestroyOnLoad. Here is what you do:
First, create a new empty game object, and attach the following script to it:
using UnityEngine;
using System.Collections;
public class Indestructable : MonoBehaviour {
public static Indestructable instance = null;
// For sake of example, assume -1 indicates first scene
public int prevScene = -1;
void Awake() {
// If we don't have an instance set - set it now
if(!instance )
instance = this;
// Otherwise, its a double, we dont need it - destroy
else {
Destroy(this.gameObject) ;
return;
}
DontDestroyOnLoad(this.gameObject) ;
}
}
And now, before you load, save the scene number in the Indestructable object:
public class SwitchScene : MonoBehaviour {
public int sceneNumber;
public void LoadScene(int sceneNumber) {
Indestructable.instance.prevScene = Application.loadedLevel;
Application.LoadLevel(sceneNumber);
}
}
And last, in your scene Start() check Indestructable.instance.prevScene and do your magic accordingly.
More info here:
http://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html
*I did not compile the code, so there may be some errors, but this is the general idea.
Why did the PlayerPrefs approach did not work?
I think its the easiest way to solve your problem.
public class FirstLevel : MonoBehaviour {
public void Start() {
PlayerPrefs.SetString("SceneNumber", SceneManager.GetActiveScene().name);
}
}
And then in the second scene simply read the saved PlayerPrefs
public class SecondLevel : MonoBehaviour {
string PrevScene;
public void Start() {
PrevScene = PlayerPrefs.GetString("SceneNumber");
// if there will be a third scene, etc.
PlayerPrefs.SetString("SceneNumber", SceneManager.GetActiveScene().name);
}
public void GoToPrevScene() {
SceneManager.LoadScene(PrevScene);
}
}
You can solve this problem with a single static member variable in the SwitchScene class. No need for the singleton pattern or DontDestroyOnLoad.
public class SwitchScene : MonoBehaviour
{
public int sceneNumber;
private static int previousScene;
private int oldPreviousScene;
void Start()
{
oldPreviousScene = previousScene;
previousScene = sceneNumber;
}
public void HandleLoadPrevButtonClick()
{
SceneManager.LoadScene(oldPreviousScene);
}
}

How to access another variable C#

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

Unity C# Null Reference Exception

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.

Categories