- Home /
Object reference not set to an instance of an object
I am trying to make a short pokemon-inspired battlesystem, but I am having a bit of trouble; I keep getting the error message; "NullReferenceException: Object reference not set to an instance of an object Abilities.AddMove (System.String dictionaryKeyID) (at Assets/Scripts/Battle Moves/Abilities.cs:26) TextBoxContent.Awake () (at Assets/Scripts/UI/TextBoxContent.cs:30)"
I have been trying to clear this up for a few days now, but with no luck. Any help would be much appreciated!
The Abilities class: public class Abilities : MonoBehaviour {
public List<Ability> abilities;
//Add move to Pokemon's list of moves.
public void AddMove(string dictionaryKeyID)
{
dictionaryKeyID = "WaterGun";
// Code that checks you don't already have 4 moves, etc, etc
Debug.Log ("Testing the dictionaryKeyID: " + dictionaryKeyID);
// Add move - using its unique string in the dictionary.
bool test = AbilityDatabaseScript.abilityDictionary [dictionaryKeyID];
Debug.Log("Found dictionaryKeyID in dictionary: " + test);
foreach (string key in AbilityDatabaseScript.abilityDictionary.Keys)
Debug.Log ("Ability: " + key);
//Debug.Log("list of abilities exists: " );
abilities.Add(AbilityDatabaseScript.abilityDictionary[dictionaryKeyID]);
}
}
The AbilityDatabaseScript class:
public static class AbilityDatabaseScript {
public static Dictionary<string, Ability> abilityDictionary = new Dictionary<string, Ability>() {
{"WaterGun", new Ability{_name = "Water Gun", _description = "It's a water gun.", _baseDamageAmount = 10
}},
{"Tackle", new Ability{_name = "Tackle", _description = "Tackle the opponent to the ground.", _baseDamageAmount = 10
}},
{"Ember", new Ability{_name = "Ember", _description = "Burn the motherfuckers.", _baseDamageAmount = 10
}}
};
}
And the TextBoxContent class;
public class TextBoxContent : MonoBehaviour {
public Text descriptionText;
public Text buttonText;
public Button myButton;
public GameObject Trainer; // get pokemonlist from this guy?
public Pokemon fightingPokemon;
public PokemonList pokeList;
public int moveNumber;
public string testString;
// Use this for initialization
void Awake () {
pokeList = gameObject.GetComponent<PokemonList>();
pokeList.AddPokemon ("Bulbasaur", 5, "1111"); //object ref
pokeList.AddPokemon ("Squirtle", 5, "2222");
fightingPokemon = pokeList.pokemonList [0];
testString = fightingPokemon.pokemonName; // gets the right name
fightingPokemon.AddMove ("Tackle"); // This is where the issue appears
}
public void UpdateTexts (bool hovering) {
if (hovering) {
buttonText.color = Color.red;
descriptionText.color = Color.red;
} else {
descriptionText.text = "";
descriptionText.color = Color.clear;
}
}
// Update is called once per frame
void Update () {
}
}
At this point I'm just trying to get the textbox on a button to display the proper move-name, and another textbox to display the move-description.
I often get the error: "You are trying to create a MonoBehaviour using the 'new' keyword. This is not allowed. MonoBehaviours can only be added using AddComponent(). " From other similar questions I take it that the issue is with my use of static classes.
Using Debug.Log I can see that it is capable of finding the ability from the static class AbilityDatabaseScript, but When I try to add the ability to the pokemon's list of abilities, I get an "Object reference not set to an instance of an object" error.
Just for reference, here are the pokemon scripts:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Pokemon : Abilities {
public string pokemonName;
public string regNumber;
public int[] statIncreasesPerLevel;
public float attack;
public float spAttack;
public float defense;
public float spDefense;
public float speed;
public float accuracy;
public int level;
}
PokemonList class:
using UnityEngine;
using System;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
public class PokemonList : MonoBehaviour {
public List<Pokemon> pokemonList;
public List<Pokemon> pokemonPcList;
private string regNumber1;
private string regNumber2;
public void AddPokemon (string dictionaryName, int level, string regNumber) {
Debug.Log("Pokemon found by keyName: " + PokemonDatabaseScript.pokemonDictionary [dictionaryName].pokemonName);
bool test = PokemonDatabaseScript.pokemonDictionary [dictionaryName];
Debug.Log("Found pokemon in dictionary: " + test);
Pokemon caughtPokemon = PokemonDatabaseScript.pokemonDictionary[dictionaryName];
caughtPokemon.level = level;
caughtPokemon.regNumber = regNumber;
// Code that checks you don't already have 6 pokemon, else send to bank, etc, etc
// Add pokemon - using its unique string name in the dictionary.
pokemonList.Add(caughtPokemon);
}
public Pokemon ReturnPokemon(string dictionaryName) {
return PokemonDatabaseScript.pokemonDictionary[dictionaryName];
}
public void SwitchPokemon () {
//switch pokemon matching regNumber1 with pokemon matching regNumber2 in Pokemon List
}
}
static PokemonDatabaseScript class (containing the static dictionary):
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public static class PokemonDatabaseScript {
public static Dictionary<string, Pokemon> pokemonDictionary = new Dictionary<string, Pokemon>() {
{"Bulbasaur", new Pokemon{pokemonName = "Bulbasaur", attack = 10, spAttack = 9, defense = 15, spDefense = 10, speed = 7, accuracy = 9
}},
{"Squirtle", new Pokemon{pokemonName = "Squirtle", attack = 9, spAttack = 11, defense = 16, spDefense = 7, speed = 10, accuracy = 9
}},
{"Charmander", new Pokemon{pokemonName = "Charmander", attack = 10, spAttack = 10, defense = 10, spDefense = 10, speed = 15, accuracy = 15
}}
};
}
Answer by Willyzekid · Aug 10, 2015 at 01:02 PM
You need to instantiate the 'abilities' variable somewhere in your Abilities class (a new List somewhere in a start function)
This field (by being public) is automatically instantiated for you by Unity, because the generic List is supported by Unity's serialization system. Re-instantiating it will cause loss of all the hand-injected abilities if there were any.
Update: pardon, what I said only holds true if the Ability is a serializable class. If not, I do not recommend declring a serialized field of a non-serializable class. In this case you indeed do need to manually instantiate the list and not rely on Unity.
Answer by Unturned · Aug 10, 2015 at 01:38 PM
Willyzekid: Adding a start function and instantiating the abilities variable didn't work, but simply changing it to:
public List<Ability> abilities = new List<Ability>();
in stead of
public List<Ability> abilities;
fixed the problem.
Thanks to stepan.stulov for telling me about the awake function, I think that explains some of the other issues I've had previously :)
I'm surprised this solved the problem. Because Generic lists are included into Unity serialization supported classes. Any serialized field of type of List is going to be instantiated by Unity automatically for you. You don't need to instantiate public or serialized private fields of this type yourself. In fact doing so can cause double instantiation and furthermore loss of hand-injected filling of the list if such happened in the inspector. What version of Unity are you using? And are you 100% positive just this one fix solved the problem?
Try it yourself:
public class Test : $$anonymous$$onoBehaviour
{
public List<float> myFloats;
public void Start()
{
Debug.Log(string.Format("$$anonymous$$y floats has {0} elements", myFloats.Count.ToString()));
}
}
Now either (1) fill the list with some floats in the inspector or (2) don't. In case of (1) the log will get you the count, in case of (2) the log will get you zero. But no null-pointer exception.
In fact now I suspect that one of these abilities that was injected into the list, got deleted, so the list's reference was pointing to a non-existing object (null/none). In this case re-instantiating the list only hid the problem, not solved it.
Update: pardon, what I said only holds true if the Ability is a serializable class. If not, I do not recommend declring a serialized field of a non-serializable class. In this case you indeed do need to manually instantiate the list and not rely on Unity.
I would try out your advice, but I'm not certain what you're trying to suggest? I haven't worked much with serializable fields, and I'm not sure what advantage this would give me.
Answer by stepan-stulov · Aug 10, 2015 at 01:25 PM
You are referencing a list of externally injected game objects from within the Awake() method. This is too early, as it's not guaranteed that those referenced objects are there yet.
General rule of thumb: mess with own internals in the Awake() and with externals in the Start(). Dependencies are injected for all the scene game objects somewhere in between all Awake()'s and all Start()'s.