Class instance null after instantiation - c#

Update: As mentioned in the comments below, Targeting target_manager; (in MakeAbility) is null. This seems to be the root of the issue, but why is it null?
===========
In the code below, I create an instance of the class PartAttack and then assign it the reference for my target manager.
The first debug log returns PartAttack. The second returns null.
public Targeting target_manager (in the first script) is assigned in the inspector to a gameobject with the Targeting script on it.
What am I doing wrong? Why is this not assigning the target manager correctly?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class MakeAbility : MonoBehaviour
{
public BlockScriptableObject block;
public IDictionary<string, IAbility> abilities_parts = new Dictionary<string, IAbility>();
public Targeting target_manager;
public PartAttack part_attack = new PartAttack();
private void Start()
{
part_attack.block_attack_damage = block.attack_damage;
part_attack.target_manager = target_manager;
Debug.Log(part_attack);
Debug.Log(part_attack.target_manager);
abilities_parts.Add("part_attack", part_attack);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class PartAttack: IAbility
{
public Targeting target_manager;
public void Use()
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Targeting : MonoBehaviour
{
public int max_targets = 3;
public string current_target = null;
public List<Combatant> list_of_targetable = new List <Combatant>();
public BlockScriptableObject block; // The abilityblock
public GameObject target_clickfield_char1;
public Combatant character_slot_1;
}

TargetManager does not appear to be instantiated anywhere.

You need to set TargetManager to a memory allocation. This is done with instantiation.
You can either create a constructor for the aggregate class that will allocate this memory by instantiating the variable, or you can set target_manager to some new memory allocation in your main function.
public Targeting target_manager = new Targeting();
C# is fast. And we can't expect to see an instantiated object so soon after the pointers are declared. I understand that there is a game object elsewhere in the system that will eventually assign it, but for your debugging script, that hasn't happened yet.
You need to assign it to an empty object inside of a constructor if you intend to always have something there. Especially if you write a line of code that is going to access it immediately after declaring its existence. Don't rely on other code that exists in a different object. To the computer that code is very far away.
This wont hurt your later assignment, as reassignment isn't going to break anything, it just allocates more memory somewhere else.
If you're worried about memory leaks, dispose the object before redeclaring it.

Related

c# script class in unity cannot be found despite no compile errors and names matching

I am attempting to amend a c# sharp script to an empty object but I am constantly given the "script class cannot be found" error, not allowing me to add the script to the object. every fix I have found for this issue includes either there being a compile error which is found in the console (my console is entirely empty) or that the names do not match (which they 100% do).
Here is my code below
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class MenuController : MonoBehaviour
{
[Header("Levels to load")]
public string _newGameLevel;
private string levelToLoad;
[SerializeField] private GameObject noSavedGameDialogue = null;
public void newgameYes()
{
SceneManager.LoadScene(_newGameLevel);
}
public void loadgameYes()
{
if (PlayerPrefs.HasKey("Savedlevel"))
{
levelToLoad = PlayerPrefs.GetString("SavedLevel");
SceneManager.LoadScene(levelToLoad);
}
else
{
noSavedGameDialogue.SetActive(true);
}
}
public void QuitButton()
{
Application.Quit();
}
}
Possible reason: the name of the script is different from the class.
Sometimes unity can (in my experience) fail finding the script if the only difference is lower/upper case. When that happens I think I usually need to delete and add a new script.
Try adding a script with different name.
Also try edit->preferences->externaltools-> regenerate project files

Scriptable Object is null when dragged to field inspector?

I'm a novice in programming / unity and trying to use scriptable objects, but having a hard time.
I'm trying to use SO as a way to store the base attribute data of players, monsters, etc.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "New StatData", menuName = "Scriptables/StatData", order = 0)]
public class StatData : ScriptableObject
{
[SerializeField] public float[] _stats;
public StatData()
{
_stats = new float[(int)StatType.Num];
}
}
The class is simple. Only has a float array(StatType is an enum).
So I made the SO in the project window under the Resources folder,
assigned all the values via the inspector,
and dragged it into the field in my StatContainer class, which deals with all attribute changes.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public enum StatType
{
MaxHP,
MaxMP,
Strength,
Defense,
Magic,
Resistance,
MoveSpeed,
Num,
}
public class StatContainer : MonoBehaviour
{
[SerializeField] protected Stat[] _stats;
[SerializeField] protected StatData _statData;
void Awake()
{
Init();
}
void Init()
{
_stats = new Stat[(int)StatType.Num];
for(int i = 0; i < _stats.Length; i++)
{
_stats[i] = new Stat((StatType)i, 0);
_stats[i]._currentValue = _stats[i]._baseValue = _statData._stats[i];
}
_currHP = _stats[(int)StatType.MaxHP]._baseValue;
_currMP = _stats[(int)StatType.MaxMP]._baseValue;
}
}
I tried debugging, and in line 35 of the StatContainer class, the _statData itself is null.
I can see that it's referenced in the field in the inspector, but it's still null in debug.
I found out that if I use Resources.Load, no problem occurs.
But if I drag - drop the SO into the inspector, it's always null.
Is this not the way to use SO? or am I doing something wrong?
If I make an instance of the SO by ScriptableObject.CreateInstance,
it means that I have to assign all the values by script, and that's not what I want,
because I thought that the convenience of assigning the values in the inspector and drag-dropping or loading the SOs were a big part of using them.
It hasn't been long since I started programming and used unity, so I would much appreciate any enlightenment.
Thanks in advance!
I would answer this as a comment, but I don't have the reputation yet, as I am also pretty new. Could it be the that object you are referencing the script on is a prefab? Sometimes if you don't hit apply on the prefab, it just doesn't accept that you've referenced it in the Inspector field. That could possibly be the issue, but I have no experience with Scriptable Objects, so I could be absolutely wrong.

Creating a list of <T> class on an object

This is a common question and I assure you I have done my research first. I simply cannot get a list of all of the instances of a script type on the game object.
I have tried making an array of the types and looping the contents into a list. This gives me conversion errors.
I have tried directly adding the array to the list with .AddRange. Conversion errors.
I have tried the different formats of GetComponents, and casting the output of the array into every applicable type I can think of, with no success.
I have also tried initising the list first and then running GetComponent in start.
I have tried using CharEquipGenre as both monobehaviour and non-monobehaviour.
What am I doing wrong?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class CharEquipment : MonoBehaviour
{
public List<CharEquipGenre> equipment_genres = GetComponents <CharEquipGenre>(); // I am trying to do something like this
public CharEquipGenre attack;
public CharEquipGenre defend;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharEquipGenre
{
public List<BlockScriptableObject> equipped = new List<BlockScriptableObject>();
}
// Additional code via request:
public class CharEquipment : MonoBehaviour
{
void Start()
{
equipment_genres = GetComponents<CharEquipGenre>();
}
public List<CharEquipGenre> equipment_genres = new System.Collections.Generic.List<CharEquipGenre>();
public CharEquipGenre attack;
public CharEquipGenre defend;
Well, CharEquipGenre is not a MonoBehaviour, so there are no "components" of this class.
You can find all components of a specific type with GetComponents (read more here) but it needs to be a MonoBehaviour that's attached to the GameObject
I don't know unity3d but it seems as though you are trying to initialize equipment_genres when defining it and for that the compiler would need access to something that is available at compile time.
If GetComponents is a method on the class then this will not work as the method is not static. You could use the instance by perhaps going with a method or expression body:
public List<CharEquipGenre> equipment_genres => new List<CharEquipGenre>(GetComponents<CharEquipGenre>());
Although a slightly "better" design would be exposing IEnumerable:
public IEnumerable<CharEquipGenre> EquipmentGenres => ...
// or
public IEnumerable<CharEquipGenre> GetEquipmentGenres() => ...
First of all make your class [Serializable] so you can see it in the inspector and save it.
[Serializable]
public class CharEquipGenre
{
public List<BlockScriptableObject> equipped = new List<BlockScriptableObject>();
}
And then simply instantiate a new list like
public List<CharEquipGenre> equipment_genres = new List<CharEquipGenre>();
Note that both of those will be overruled by whatever you do to them in the Inspector later. Once you added elements any change to the initialization lines is useless (unless you hit Reset in the MonoBehaviour's context menu)

C# / new keyword / class member reference

So i have 2 classes (Sound class, AudioManager class)
I dont understand why we can access Sound's field without creating instance of Sound class. ()
Like what are the differences between
public Sound test1;
AND
Sound test2 = new Sound();
Its weird to me cause when i was learning c# tutorials / u could only access variables from another class only by creating instance of that class but in Unity u cas access it just by typing public Sound test1;
Sound Class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
[System.Serializable]
public class Sound
{
public AudioClip clip;
public string name;
public float volume;
public float pitch;
public AudioSource soundclass_source;
}
AudioManager class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
public class AudioManager : MonoBehaviour
{
public Sound test1;
Sound test2 = new Sound();
test1.name = "test1"; // without new
test2.name = "test2"; // with new
}
Since it is [Serializable] and the Unity Inspector of MonoBehaviour and ScriptableObject (etc) classes automatically initializes serialized fields with a default instance there is no difference between
public Sound test1;
and
public Sound test1 = new Sound();
(public fields are serialized by default if the type is serializable)
Also note that once you created an instance in Unity and the values are set via the Inspector those serialized values will always overrule the ones you hardcode here. If you later e.g. decide to pre-initliaze some values and change it to
public Sound test1 = new Sound() { name = "test1" };
it will have no effect since the value configured in the Inspector is taken instead. This coded value only is used for the very forst time the instance is created or if you hit Reset in the components context menu.
If you want later that the coded value is used instead you have to assign the new value in a method instead (see below).
No accesibility definition for a field (/class/method/property/etc) like public, protected or private in c# always means private.
If a field is private like in your case you have to initialize it "manually" otherwise the default value for classes is always null (== no instance reference).
You can do this like you did staticly in the class
private Sound test2 = new Sound();
this means the instance is already created as soon as an instance of your class is created or can do it laso later in a method like
private Sound test2;
private void Awake()
{
Sound test2 = new Sound();
}
Finally you can/should make as private as possible for capsulation but can still make them serialized => displayed in the Inspector and stored using the [SerializeField] attribute:
[SerilaizeField] private Sound test2;
You can read more about Unity's serialization here

CS0120 Cant set layer mask from different script

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?

Categories