this is null in the inside method, how is this even possible? - c#

I have a class Star that inherits from Object. It has a GameObject field.
Here is code:
public class Star : Object {
public GameObject starObject;
// code omitted
}
It has a method that I use for selection.
public void selectStar () {
Debug.Log ("Inside star selection");
mapManager.selectionStarDidChange(this);
selected = true;
}
I have another script from which I call that method, it detects input.
public class StarObjectHandler : MonoBehaviour {
public Star starScript;
void OnMouseOver()
{
if (Input.GetMouseButtonDown(0)) {
Debug.Log ("Mouse registered star selection");
starScript.selectStar();
}
}
}
But when I call the first method, somehow this becomes null. How can I even call methods on null and they work?
What is wrong here? I checked the starScript in second class, it is not null.
In the log I get "Mouse registered.." and then "Inside star selection" as expected.
EDIT1:
Here is the method that is getting invoked from this:
public void selectionStarDidChange(Star newlySelected)
{
if (newlySelected) {
starSelector.transform.position = newlySelected.position;
} else {
starSelector.transform.position = new Vector2(-10, -10);
}
if (lastSelectedStar) {
lastSelectedStar.deselectStar();
}
if (lastSelectedShip) {
lastSelectedShip.deselectShip();
}
lastSelectedStar = newlySelected;
}
It always goes the else route, and logging here says that newlySelected is null.
EDIT2:
Here is the project if anyone feels interested to see what is wrong:
https://www.dropbox.com/s/napsqknlw3hjuku/Orion2.zip?dl=0
EDIT3:
I managed to localize the error to this code:
Star star = new Star();
Debug.Log ("Is star null? __" + (star == null) + "__");
Somehow this returns true, I will have to look up object instantiation.

Yes looks like you have declared variable without assigning it
Star star = new Star();
Will solve the issue

Apparently the problem was that this code returned null instead of an object. I have no idea how the rest of the program worked with this being null, and I can't understand why it returns null.
It is a subclass of Object class. I changed it to no superclass and it worked.
Star star = new Star();

Related

Understanding why Unity C# passing custom class object that return null pointer error

can someone help me understand what I'm missing?
My problem is, I have a script that manages the scene, and when it starts up it creates a bunch of buttons from a prefab.
void InitializeCharacters()
{
int index = 0;
foreach (CharacterSelectionItem character in CharacterSelectionData.CharacterList)
{
CharacterButton.GetComponent<CharacterButton>().Character = character;
CharacterButton.GetComponent<CharacterButton>().fullSource = character.FullCharacterSource;
GameObject newButton = Instantiate(CharacterButton) as GameObject;
newButton.transform.SetParent(CharacterPanel.transform, false);
}
}
CharacterButton is the prefab, assigned from editor to the script, with this script attached:
using static CharacterSelectionItem;
public class CharacterButton : MonoBehaviour
{
public CharacterSelectionItem Character;
public string fullSource;
void Start()
{
Debug.Log("Full" + fullSource);
Debug.Log("Character image source" + Character.FullCharacterSource);
}
}
public class CharacterSelectionItem
{
public string Description;
public string ImageSource;
public string FullCharacterSource;
public string CharacterHistory;
public CharacterSelectionItem(
string descr,
string img,
string fullSource,
string histroy,
Race race
)
{
Description = descr;
ImageSource = img;
FullCharacterSource = fullSource;
CharacterHistory = histroy;
Race = race;
}
};
Now, the first debug prints the string, while the second returns "Object reference not set to an instance of an object", and I can't really understand why. I tried to pass character in a SetCharacter(ref CharacterSelectionItem character) to have the reference but still don't work. The objective is not to pass every field on of CharacterSelectionItem but the whole object.
It's my first project in unity and I was loosely following a tutorial. and hope I could find an answer there but it didn't come.
I think I'm missing something pretty basic but don't know what, can someone help?
I had a similar situation lately and discovered that while Instantiate basically creates a deep clone of the prefab it only seems to take over custom fields that are serialized.
At least from the code you shared that wouldn't be the case for
public CharacterSelectionItem Character;
since the type CharacterSelectionItem isn't [Serializable]
Try to make it
[Serializable]
public class CharacterSelectionItem
{
...
}
If you then do not want it to appear in the Inspector you can still decorate the field additionally
[HideInInspector] public CharacterSelectionItem Character;
Alternatively instead of assigning the field on the prefab before Instantiate rather assign it on the instance in the first place:
var newButton = Instantiate(CharacterButton);
newButton.transform.SetParent(CharacterPanel.transform, false);
newButton.GetComponent<CharacterButton>().Character = character;
newButton.GetComponent<CharacterButton>().fullSource = character.FullCharacterSource;

Why is a list of transforms within a class deleted when the class instance gets copied to a list?

I have this problem in Unity (or maybe C#) that's very weird to me. Here is a virtual class:
public abstract class ActionTaken : MonoBehaviour {
protected char type;
protected Transform minionTakingAction;
public abstract void activate();
}
And this virtual class is a parent to the one that interests me:
public class AbilityTaken : ActionTaken
{
public int index;
List<Transform> selectedFriendlyMinions;
List<Transform> selectedEnemyMinions;
List<Transform> selectedTiles;
public override void activate()
{
//The value here is 0 !!! And it should be 1...
Debug.Log(selectedEnemyMinions.Count);
if (selectedFriendlyMinions.Count == 0 && selectedEnemyMinions.Count == 0 && selectedTiles.Count == 0 )
{
minionTakingAction.GetComponentInParent<AbilitiesActivation>().activateAbility(index);
}
else
{
minionTakingAction.GetComponentInParent<AbilitiesActivation>().activateAbility(index, selectedFriendlyMinions, selectedEnemyMinions, selectedTiles);
}
}
public AbilityTaken(Transform _minionTakingAction, int abilityIndex, List<Transform> _selectedFriendlyMinions, List<Transform> _selectedEnemyMinions, List<Transform> _selectedTiles)
{
type = 'S';
minionTakingAction = _minionTakingAction;
index = abilityIndex;
selectedEnemyMinions = _selectedEnemyMinions;
selectedFriendlyMinions = _selectedFriendlyMinions;
selectedTiles = _selectedTiles;
//The value here is 1 !!!
Debug.Log(selectedEnemyMinions.Count);
}
public AbilityTaken(Transform _minionTakingAction, int abilityIndex)
{
type = 'S';
minionTakingAction = _minionTakingAction;
index = abilityIndex;
selectedFriendlyMinions = new List<Transform>();
selectedEnemyMinions = new List<Transform>();
selectedTiles = new List<Transform>();
}
}
As you can see in the comments, the value of selectedEnemyMinions list of Transforms changes from the constructor (count value: 1) to the "activate()" function (count value: 0), without me making any changes to it. All I do is:
1. I create a new instance of AbilityTaken giving to the constructor an enemyMinionSelection list with 1 element
2. I add abilityTaken to a list
3. I call activate() from LateUpdate()
AbilityTaken abilityTaken = new AbilityTaken(minionTakingAction, gameMaster.abilitySelected,
// value of enemyMinionSelected.Count here is 1
new List<Transform>(), gameMaster.enemyMinionsSelected, new List<Transform>());
List<ActionsTaken> actionsTaken = new List<ActionTaken>();
actionsTaken.Add(abilityTaken);
private void LateUpdate()
{
if (!actionInProgress &&
actionsTaken.Count>0)
{
ActionTaken currentAction = actionsTaken[0];
currentAction.activate();
}
}
If you can tell me why adding the class instance to List and accessing this instance would cause a member List of Transforms to change their value, that would be great. On the other hand the value of member variable "index" doesn't change (it's always 1). I tried changing Lists to public but that doesn't help as expected.
If you can tell me why adding the class instance to List and accessing this instance would cause a member List of Transforms to change their value, that would be great.
It's not. There's nothing wrong with doing that, it won't change the current values of AbilityTaken.
There is something else going on, that is, the problem is somewhere else. For example, Debug.Log(selectedEnemyMinions); is not printing the Count.
I thoroughly debugged my code and found that the problem here is that I am giving the
constructor of ActionTaken parameters (Namely List) that get changed after assignment in ActionTaken constructor. Then I execute some code that changes the first list of transforms, but due to C# not copying the value, but referencing the assigned value the List of Transforms changes when the firstly assigned value changes.
void function()
{
//Some code here
AbilityTaken abilityTaken = new AbilityTaken(minionTakingAction, gameMaster.abilitySelected, new List<Transform>(), gameMaster.enemyMinionsSelected, new List<Transform>())
//Here we go in the constructor and everything seems fine
// ! Some code here that changes gameMaster.enemyMinionsSelected. The class object doesn't copy the List of Transforms, but references it's address and when it changes, the reference in ActionTaken also changes.
}

Game Text Inputfield and Button not responding

I am trying to build a word guessing game. My input field is supposed to accept a string and go to the next page if right or return back to the menu page if not right. Currently, my scene is going to the next scene for both the right and wrong answer.
public class anser : MonoBehaviour {
private string secretWord = "eje";
private string guessWord;
public void GetInput(string guessWord) {
//Invoke ("CompareGuesses", 1f);
if (secretWord == guessWord) {
SceneManager.LoadScene ("Front Page");
} else if (secretWord != guessWord) {
SceneManager.LoadScene ("Question2");
}
}
}
I'm not sure what the guessWord field is for, all you need is the guessWord parameter for your GetInput() method.
Also, you need to make sure the scene names are correct (check for capitals and spaces), and make sure that some other script is actually calling the GetInput() method (you didn't post the code for anything that calls this method).
Here is a cleaned up version of your code that should do what you want:
// The class name "anser" was misspelled. Also you typically use PascalCase for class names.
public class Answer : MonoBehaviour
{
private string secretWord = "eje";
public void GetInput(string guessWord)
{
if (secretWord == guessWord.ToLower())
{
SceneManager.LoadScene("Question2");
return; // eliminates the need for an else clause
}
SceneManager.LoadScene("Front Page");
}
}
Edit: Per Scriven's suggestions in the comments, I changed the LoadScene() arguments to reflect the OP's desired behavior. Also added a little bit of input validation for the guessWord parameter.

Method within a class won't take away value from integer argument

I've currently only been learning C# for a week so apologies for any stupid errors but I'm trying to call a method within a switch statement to take away an integer value that is declared within a list from the argument given to the method, the argument in this case is the health of the currEnemy object, but when the currEnemy.health value is printed out to the console it's value is unchanged and I can't figure out why.
The list that stores the integer value that the currEnemy.health is taken away by a long with the health variable which is set to an integer value in the Enemy class:
public List<Weapon> myWepList = new List<Weapon>(){
new Weapon {name = "Dagger", dmg = 10, stamDrain = 5},
new Weapon {name = "Sword", dmg = 20, stamDrain = 20},
new Weapon {name = "Halberd", dmg = 40, stamDrain = 20}
};
public int health{ get; set; }
The method within the player class that should take away the 'dmg' value from the enemy.health value and set enemy.health to a new value:
public void charAttack(int enemyHealth)
{
enemyHealth -= equippedWep[0].dmg;
}
Then the code that calls the above method to display the currEnemy.health's new value:
enum PlayerInput
{
Attack,
Block,
}
while (gameStart == true) //this part onwards is stored in the main method
{
string playerInput = Console.ReadLine().ToUpper();
PlayerInput inputChoice;
Console.WriteLine("Type 'attack' to attack the enemy");
if (Enum.TryParse<PlayerInput>(playerInput, true, out inputChoice))
{
switch (inputChoice)
{
case PlayerInput.Attack:
currPlayer.charAttack(currEnemy.health);
Console.WriteLine("Enemy Health is {0}", currEnemy.health);
break;
default:
break;
}
}
}
I'd also appreciate if any general advice is just given to me about my code considering I'm completely new to C# so any constructive advice would be great, thank you.
This method:
public void charAttack(int enemyHealth)
{
enemyHealth -= equippedWep[0].dmg;
}
... is basically pointless. The statement enemyHealth -= ... only affects the parameter called enemyHealth. It won't change currEnemy.Health at all - because the method argument is passed by value. In other words, the process is:
currEnemy.health is evaluated
The value is used as the initial value for enemyHealth
The method executes
The final value of enemyHealth isn't used at all
See my article on parameter passing for more details.
There are various ways you could tackle this - for example, you might want:
currPlayer.Attack(currEnemy);
where the Attack method would look something like:
public void Attack(Enemy enemy)
{
Weapon weapon = equippedWep[0];
enemy.Health -= weapon.Damage;
}
Note that the last line is very different to your previous code, because it would set the value of enemy.Health afterwards... you wouldn't just be changing a parameter, but the state of an object.
You can't change a value that is passing to a function. It is the same in all langages, not only C#.
int a = 0;
function changeA(int a)
{
a = 1;
}
print a; // show 0, not 1
You can change a value that is contained into an object:
public class Obj
{
public int health{ get; set; }
}
var o = new Obj();
function changeHealth(Obj o)
{
o.health = 1;
}
print o.health; // show 1
C# or whatever, that is the first thing you need to understand.
For your actual problem, here is a proposal: pass the actual object representing the enemy, not just his health. Then deduce the health of the enemy object.
public void charAttack(Player enemy)
{
enemy.health -= equippedWep[0].dmg;
}
while (gameStart == true) //this part onwards is stored in the main method
{
string playerInput = Console.ReadLine().ToUpper();
PlayerInput inputChoice;
Console.WriteLine("Type 'attack' to attack the enemy");
if (Enum.TryParse<PlayerInput>(playerInput, true, out inputChoice))
{
switch (inputChoice)
{
case PlayerInput.Attack:
currPlayer.charAttack(currEnemy);
Console.WriteLine("Enemy Health is {0}", currEnemy.health);
break;
default: break;
}
}
}
C# passes ints by value which means that you only get a copy of the value in the method. To solve it you can parse the enemy object instead like this:
public void charAttack(Enemy enemy)
{
enemy.health -= equippedWep[0].dmg;
}
This would affect the health on the enemy object.
The problem lies in your charAttack method declaration. The enemyHealth parameter is not being passed by reference, so you are actually modifying a copy of the variable not the original (this is call passing by value).
You can either pass the parameter by reference, which will mean that you modify the original:
public void charAttack(ref int enemyHealth)
{
enemyHealth -= equippedWep[0].dmg;
}
Or you could return the modified result and set your original value to it:
public int charAttack()
{
return equippedWep[0].dmg;
}
i = charAttack();
You need to pass your enemyHealth value by reference. C# defaults to passing by value which means the method gets it own copy of the value. To pass the actual int object pass by reference.
public void charAttack(int enemyHealth)
{
enemyHealth -= equippedWep[0].dmg;
}
Based on the above method, the behavior you're experiencing is expected. You are not actually stating "change the calling currEnemy.health" when calling this method, but change enemyHealth. The problem is currEnemy.health is being passed by value (as in the literal value is passed, not the "object" that represents that value) not by reference.
There are two (or more) ways to fix this:
one would be use a ref to the enemy health:
public void charAttack(ref int enemyHealth)
{
enemyHealth -= equippedWep[0].dmg;
}
and call your method like:
currPlayer.charAttack(ref currEnemy.health);
or return a new enemy health from your method:
public int charAttack(int enemyHealth)
{
return enemyHealth - equippedWep[0].dmg;
}
and call like:
currEnemy.heatlh = currPlayer.charAttack(currEnemy.health);

Unity modify component property after collision

I'm trying to modify a property of a class component after collision,
but the property doesn't seem to be set.
void OnCollisionExit2D (Collision2D myCollision) {
Debug.Log ("OnCollisionExit2D in Player:" + myCollision);
CompoMyClass compo = myCollision.gameObject.GetComponent<CompoMyClass>();
if (compo.collideOnce == true)
return;
compo.collideOnce = true;
// it always goes here :(
}
Do you know why?
public class CompoMyClass : MonoBehaviour {
public bool collideOnce = false;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
Make sure that you're looking at the same instance of the Component per collision. If this component were attached to multiple objects, each would have their own collideOnce variable to worry about.
A good way to do this, which will unambiguously tag each object, is to assign an ID such as
Guid ID = Guid.NewGuid();
Also make sure that the object you're colliding with has the right component, to prevent a NullReferenceException, and that you're exiting the collision properly. You may want to switch to OnCollisionEnter.
finally, make sure you're not setting that value false anywhere else.

Categories