Variable not being set in class using inheritance - c#

Edit: It turns out it was how I was getting the ProjectileWeapon component. I was getting the one that was on the non-instantiated prefab instead of getting the one on weapon gameobject. I changed it so that the code instantiates the game object first (or gets the existing one if we've already picked it up), and then get the component from that. So the rest of the code works fine. Now I can move on and improve it!
I have an issue with the class below called ProjectileWeapon. It is based on an abstract class called Weapon, and that class inherits MonoBehaviour.
Weapon has two abstract functions called BeginCycle and EndCycle which are implemented in the ProjectileWeapon class. Those functions set a variable called "firing".
The problem is, "firing" doesn't ever seem to be set despite the functions being called correctly. I know the functions are called because I can see the prints in the console.
Also, when I use that variable in the update function, it doesn't do anything because the variable never changes.
The OnGUI function is working and is displaying text on screen, however the "firing" variable is never updated.
Am I misunderstanding how to use inheritance?
This class is on the weapon prefab which is then instantiated during the equipping function in the game
public class ProjectileWeapon : Weapon
{
private bool firing;
private float firingTimer;
void Start()
{
print("ProjectileWeapon start");
}
void OnGUI()
{
GUI.Label(new Rect(0,100,100,100), "ProjectileWeapon firing: " + firing);
}
void Update()
{
// this function is called but "firing" is not updated
}
public override void BeginCycle()
{
print("projectile begin cycle");
firing = true;
}
public override void EndCycle()
{
print("projectile end cycle");
firing = false;
}
}
Here's the base class:
public abstract class Weapon : MonoBehaviour
{
public abstract void BeginCycle();
public abstract void EndCycle();
}
EDIT: Here is the code that calls the above
This component is added to the player game object
public class WeaponHandler : MonoBehaviour
{
public bool FireInput { get; set; } // set to true when user holds the mouse button down, and false when let go
public Weapon WeaponBehaviour; // this is the script that does the weapon functionalility. Any subclass of Weapon can be put here e.g. ProjectileWeapon, MeleeWeapon
private bool isFiring = false;
void OnGUI()
{
GUI.Label(new Rect(0,0,100,100), "fire input:" + FireInput + ", isFiring:" + isFiring);
}
void Update()
{
// fire weapon
if (WeaponBehaviour && FireInput && !isFiring)
{
ActivateWeapon();
}
else if (!FireInput && isFiring)
{
DeActivateWeapon();
}
}
private void ActivateWeapon()
{
print("activate weapon");
isFiring = true;
WeaponBehaviour.BeginCycle();
}
private void DeActivateWeapon()
{
print("deactivate weapon");
isFiring = false;
WeaponBehaviour.EndCycle();
}
}

Based on the code you've provided, there are three possible scenarios:
You're not calling the functions. However, if the print functions are called, then you must be calling them.
You're calling both functions, which sets the variable to true, and then to false.
You're overriding the variable with a local variable with the same name. Visual Studio will warn you if that's the case.
It's hard to tell without knowing where the variables are supposed to be called. If you upload the rest of the code, I'm sure the answer will be clear.

It turns out it was how I was getting the ProjectileWeapon component. I was getting the one that was on the non-instantiated prefab instead of getting the one on weapon gameobject. I changed it so that the code instantiates the game object first (or gets the existing one if we've already picked it up), and then get the component from that. So the rest of the code works fine. Now I can move on and improve it!

Related

Property appears to be self initializing when running in unity editor

I've got a strange issue, that makes no sense. It appears that one of my variables that store a custom class is self initializing when it should be staying null. I've deleted 99% of the code in the two class files involved and simplified the remaining to its minimum while debugging, yet it still occurs.
The code is in question is designed to [ExecuteInEditMode] in the background in Unity while the designer does their thing and not in play.
On the first tick, ownedData evaluates as expected (null).
On the second tick, ownedData is evaluating as if an object was assigned even though I ensure that canPrepare is never set to true. (Its internal properties are still null though)
[Serializable]
public class MyData {
public byte[] id { get; private set; } = null;
public MyData(byte[] id) {
this.id = id;
}
}
#if (UNITY_EDITOR)
[ExecuteInEditMode]
#endif
public class Owner : MonoBehaviour {
private bool canPrepare = false; //Made private to ensure nothing external can set this to true
private MyData ownedData = null;
#if (UNITY_EDITOR)
private float _editorTime;
public void Awake() {
EditorApplication.update += EditorDataEval;
}
private void OnDestroy() {
EditorApplication.update -= EditorDataEval;
}
private void EditorDataEval() {
if (Application.isPlaying) { return; }
if (Time.realtimeSinceStartup - _editorTime >= 1f) {//Tick once per #f seconds
_editorTime = Time.realtimeSinceStartup;
if (ownedData == null) {
if (canPrepare) {
ownedData = new MyData(System.Guid.NewGuid().ToByteArray());
}
return;
} else {
//Should never be called as canPrepare is not set
Debug.Log("Data id: "+ ownedData.id);
}
}
}
#endif
//TOOD: Add class methods back in once bug resolved.
}
I'm starting to theorize that Unity is in the background auto initializing the variable in order to show it in the editor...
By default this should and does not happen since your field is not public and not [SerializeField].
I guess you once entered the Debug mode in the Inspector:
In this case the Unity Inspector will automatically instantiate the field because its type is [Serializable]! Later it stays instantiated until the scene is re-loaded even if you already left the Debug mode.
As you can see not even Reset does help - I can only guess but I think Unity only resets the serialized Inspector fields here.
A really quick fix would be to simply enter and exit PlayMode once. The scene and thereby your component is reloaded and the field reset to null.
Another option would be to implement the Reset method and actually do it there
private void Reset()
{
ownedData = null;
}
Now if you try it again and hit Reset in the contextMenu you can see that the output is stopped since the field is reset to null.

Get variable from OnTriggerEnter class to another class

I have a really simple question, where I can't understand what I am missing.
I am getting error:
NullReferenceException: Object reference not set to an instance of an object
test.Update () (at Assets/Scripts/test.cs:13)
So, I have one class "DissableInput", where there is a simple trigger that changes whenever it triggered by another object. It returns true/false, whenever it changes.
Another class has is named "test", which basically should print out when it's true/false depending on trigger input.
//Test Class
public bool touchedCollision;
// Update is called once per frame
void Update()
{
if (this.GetComponent<DissableInput>().touchedCollision)
{
Debug.Log("IN COLL");
}
if(!this.GetComponent<DissableInput>().touchedCollision)
{
Debug.Log("NOT IN COLL");
}
}
// DisableInput Class
public bool touchedCollision;
void OnTriggerEnter (Collider other)
{
Debug.Log("In Triger");
touchedCollision = true;
}
public void OnTriggerExit(Collider other)
{
Debug.Log("Out Triger");
touchedCollision = false;
}
I am expecting that true/false will go into the test class, but instead it gives NullReferenceException error.
This part this.GetComponent<DisableInput>().touchedCollision will try to get a component of the specified type, in this case your "DisableInput" class, in the same gameObject that its attached. If you wish to have the DisableInputClass script in another gameObject you need to reference in some other way.
Using a public variable would look like that
//TestClass
public DisableInput disableInput;
// Update is called once per frame
void Update()
{
if (disableInput.touchedCollision)
{
Debug.Log("IN COLL");
}
else //you don`t need to specify the condition again, you can do it with and else
{
Debug.Log("NOT IN COLL");
}
}
you can look how GetComponent works here
An alternative is also to use FindObjectOfType<DisableInput>()
//TestClass
private DisableInput disableInput;
void Awake(){
disableInput = FindObjectOfType<DisableInput>();
// you only need to get the
//class refence one time, no need for it to be in the update,
//it gains a lot of performance
}
// Update is called once per frame
void Update()
{
if (disableInput.touchedCollision)
{
Debug.Log("IN COLL");
}
else //you don`t need to specify the condition again, you can do it with and else
{
Debug.Log("NOT IN COLL");
}
}
More about FindObjectOfType here, but to use this you must understand that
it will return the first object that he finds that has DisableInput attached to it. If you have multiple gameObjects with DisableInput you can`t be sure which one it will get.
Your scripts are on different objects, therefor this.GetComponent won't work
You want to know when ObjA touches ObjB, so you have one script on ObjA that wants to know and a script on ObjB that is run when the two touch. However, this.GetComponent can only see scripts attached to the same object. So you can't run this code from ObjA (because it doesn't, and can't, know about ObjB!)
I'm going to make two small changes to your scripts:
//Test Class
public bool touchedCollision; //you already had this, but weren't using it
// Update is called once per frame
void Update()
{
if (touchedCollision)
{
Debug.Log("IN COLL");
}
if(!touchedCollision) //can be changed to just `else`
{
Debug.Log("NOT IN COLL");
}
}
// DisableInput Class
//removed touchedCollision from here
void OnTriggerEnter (Collider other)
{
Debug.Log("In Triger");
other.GetComponent<TestClass>().touchedCollision = true;
}
public void OnTriggerExit(Collider other)
{
Debug.Log("Out Triger");
other.GetComponent<TestClass>().touchedCollision = false;
}
This works because it is assumes that:
You have more than one DisableInput instance for each volume that you wish to disable input
You may have more than one TestClass (or whatever you're going to call it) instance (for example, multiplayer or AI controlled objects).
The DisableInput class finds the TestClass of the object that caused the physics event and changes its value.
Addendum:
You should check for nulls. This is the simplest way (glory be to C# 6!)
other.GetComponent<TestClass>()?.touchedCollision = ...
Using a null conditional operator.

How to refactor my code to avoid raising events from outside the classes?

I am developing a game which contains some managers for different tasks such as collision detection, etc. Once a collision is detected, each gameobject affected by this collision should raise the OnCollided event, so I can easily play sounds, make the object disappear, open a door or whatever, but I cannot get this behavior without raising the event from the CollisionSystem, instead of letting each GameObject raise their own event.
Here is a simplified example, hope it makes sense:
Class CollisionSystem
{
// It is called when a collision has been detected between two objects (code not included)
public void HandleCollision(GameObject gameObject1, GameObject gameObject2)
{
//Do whatever
//How can trigger each corresponding game object event from here?
}
}
Class GameObject
{
protected event GameEventHandler Collided;
protected void OnCollided(GameEventArgs e)
{
if (Collided != null)
Collided(this, e);
}
}
I’ve tested things like making the OnCollided method public, but the event should be raised from inside the class... And, on the other hand, the game object itself cannot determine when it collides, cause, well, this is what the CollisionSystem does.
Thanks in advance.
Alright this is what I could come up with that should embody what you're trying to do:
public class CollisionSystem
{
Public void HandleCollision(IGameObject[] objects)
{
foreach (IGameObject obj in objects)
{
//call the function that tells the object it collided
obj.CollisionDetected()
}
}
}
You'll want your gameobjects to implement an interface
public interface IGameObject
{
public void CollisionDetected()
}
Which means your gameobject-class looks like this;
public class GameObject : IGameObject
{
//constructors, params, whatever
public void CollisionDetected()
{
//play some sound, remove object, whatever
}
}
The thing is; I don't know what raises your collision-event.
Edit: so I reread your question and saw you mentioned it's begin called from the physics engine. Wherever/however that may be, if you have your CollisionSystem declared inside the physics-engine class, you can call the HandleCollision()-method from there and you'd be full circle.

Cannot access class variable in member function

In Unity C# script I have a singleton game controller object in which the game variables are stored but I am getting odd behaviour when accessing them in a member function. It prints the initialized value, not the current one. In the update function however it prints the correct value each frame. I summarized the class below. The controller class has a static reference to itself. If you need to know additional details you can ask. I am new to C# and Unity so I might be lacking some obvious answer.
Thanks
public class controller : MonoBehaviour {
public int[] star = new int[64];
void Start(){ /* calls another function to set 0 for each star index */ }
void Update(){ // during gameplay star[0] gets a value of 1
print(star[0]); // prints correct value which is 1
}
public void checkValue(){
print(star[0]); // prints 0 incorrectly which should be 1
}
}
I've set up a little example. I created a button and an empty gameobject GameController. I added this code to the GameController:
using UnityEngine;
using System.Collections;
public class GameController : MonoBehaviour {
public static GameController instance;
[System.NonSerialized] public int[] star = new int[64];
private void Awake()
{
if(instance == null)
instance = this;
else if(instance != this)
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
}
private void Start()
{
StartCoroutine(SetAllTo(1));
}
// for simulating some changes during the game
private IEnumerator SetAllTo(int value)
{
yield return new WaitForSeconds(2.0f);
for(int i = 0; i < star.Length; i++)
star[i] = value;
Debug.Log("Setting done");
}
public void PrintFirst()
{
Debug.Log(star[0]);
}
}
Now I added an OnClick event to the button, dragged the GameController gameobject into the slot and picked PrintFirst.
I start the game and click the button once before the Coroutine log and once after and the console gives the following:
0
Setting Done
1
Edit:
The gameobject for the OnClick event must be in the scene, it can't be a prefab in the assets folder.
It seems that's the call to the start function may also occurs in your external calls before the call for ckeckValue
Try debugging by putting breakpoint in each of these functions and you can understand what is going on.
The start function runs once in the beginning of the game and not again.
For that you have pattern - Singleton
The second thing why to call external function to initialize class member? do it in your default contractor.
- C# default constractor already initialize 0 in int[].
if you still what to run Start from other scope in your code make it public (c# makes variable/methods as private by default)

Why am I geting two outputs for each Debug.Log in a State Manager script I am writing for Unity?

I am writing a State Manager script for Unity in C#. Everything appears to be correct but when I test it out inside Unity, all the Debug.Log lines output twice. I'm following along with a book called Learning C# by Developing Games with Unity 3D Beginner's Guide. I have studied and studied the reference and I do not see what I am doing wrong. The script is far from finished, I think, but according to the text there should only be one output per Log.
This is my ISBase for the interface.
namespace Assets.Code.Interfaces
{
public interface IStateBase
{
void StateUpdate();
void ShowIt();
void StateFixedUpdate();
}
}
This my the BeginState, there is also a PlayState, WonState, and LostState. They're all pretty much identical except for the class name, the constructor name, the Debug.Log output, and the the new SwitchState.
using UnityEngine;
using Assets.Code.Interfaces;
namespace Assets.Code.States
{
public class BeginState : IStateBase
{
private StateManager manager;
public BeginState (StateManager managerRef) //Constructor
{
manager = managerRef;
Debug.Log ("Constructing BeginState");
}
public void StateUpdate()
{
if (Input.GetKeyUp (KeyCode.Space))
manager.SwitchState (new PlayState (manager));
}
public void ShowIt()
{
}
public void StateFixedUpdate()
{
}
}
}
And here is the actual StateManager.
using UnityEngine;
using Assets.Code.States;
using Assets.Code.Interfaces;
public class StateManager : MonoBehaviour
{
private IStateBase activeState;
void Start ()
{
activeState = new BeginState (this);
}
void Update ()
{
if (activeState != null)
activeState.StateUpdate ();
}
public void SwitchState(IStateBase newState)
{
activeState = newState;
}
}
IF it prints twice, it means that the call is done twice.
As obvious as it may sound, it is a matter to track it down, and I believe Unity will point you to the line of code called, if you double click on the console entry in the editor. This should open your IDE, or if it is open, just point the IDE to the line called.
Based on that, you may figure out that you have the same script on multiple objects; or you may have the same debug.log call in 2 different places of the script, but that are executed close to each other, ending up with you seeing the same statement.
Not much that can be done, without put a breakpoint and follow step by step
have you checked whether you have two same scripts attached to the same game object accidentally?
usually the duplicate log occurs for me when i got same script attached twice without me noticing it.
on the other hand, i would suggest guarding input from happening when switching state. anything to do with user input will need some guard from rapid repetition.
e.g.
if (!switchingStateCoolDown) {
Input.GetKeyUp (KeyCode.Space) { ... };
}
// hope you get the idea.

Categories

Resources