-1
\$\begingroup\$

I have a pretty "basic" combat class here. It has a Player and an Enemy. They are inherrited from a Character class and have values such as health, armor and ar (attack rating (damage)). I am trying to set the combat up in a way so that once you press a button (in this case, e), you attack the enemy and the enemy attacks you. I think this is most easily done by having a method in Update() which constantly checks whether or not my desired key is pushed, and if it is, to execute said method which has 2 attacks. I have rearranged the program so that the Player and Enemy are saved seperately in the methods "savePlayer" and "saveEnemy".

Relevant global objects:

 public Player Player; public EnemyCharacterCreator Enemy; public Player savedPlayer; public EnemyCharacterCreator savedEnemy; public Text[] savedArray; 

My constructor:

public Combat(Player Player, EnemyCharacterCreator Enemy, Text[] texxtArray) { savePlayer(Player); saveEnemy(Enemy); saveTextArray(texxtArray); textChanges(); // FightLoop(); } 

Aforementioned methods:

 public void savePlayer(Player Player) { savedPlayer = Player; } public void saveEnemy(EnemyCharacterCreator Enemy) { savedEnemy = Enemy; } 

Combat-related code:

public void attackButton() { if (Input.GetKeyDown("e") == true) { FightLoop(); } } public void FightLoop() { PlayerAttack(); EnemyAttack(); textChanges(); } public void PlayerAttack() { savedEnemy.health = savedEnemy.health - savedPlayer.ar; } public void EnemyAttack() { savedPlayer.health = savedPlayer.health - savedEnemy.ar; } 

Conceptually this should work fine, except it doesn't. When I "manually" execute FightLoop() in the constructor (commented out in the first code block) it works and the enemies health is reduced by 45 (which is my players' ar value), yet when I put attackButton() into the Update() function I get a NullReferenceException whenever I press e. 1 The error leads to line 74, which is the only line within the PlayerAttack() method:

savedEnemy.health = savedEnemy.health - savedPlayer.ar; 

I put a breakpoint there and the savedEnemy object was null. I have no idea why, it is a global object and it works perfectly when I do it "manually", without Update(). Help would be appreciated.

A Minimal Complete Verifiable Example as requested in the comments:

Object Setup:

Combat Script Slotting:

There are 4 Scripts.

Combat:

 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class Combat : MonoBehaviour { bool playerTurn = true; bool invActive = false; public Player Player; public Enemy Enemy; public Text[] textArray = new Text[6]; public Player savedPlayer; public Enemy savedEnemy; public Text[] savedArray; public Combat(Player Player, Enemy Enemy, Text[] textArrayPass) { savePlayer(Player); saveEnemy(Enemy); saveTextArray(textArrayPass); //textChanges(); // FightLoop(); textChanges(); } public void saveTextArray(Text[] textArray) { savedArray = new Text[6]; savedArray = textArray; //Debug.Log(savedArray[0].text); } public void savePlayer(Player Player) { savedPlayer = Player; } public void saveEnemy(Enemy Enemy) { savedEnemy = Enemy; } public void attackButton() { if (Input.GetKeyDown("e") == true) { Debug.Log(Player.health); FightLoop(); Debug.Log(Player.health); } } public void FightLoop() { PlayerAttack(); EnemyAttack(); textChanges(); } public void PlayerAttack() { savedEnemy.health = savedEnemy.health - savedPlayer.ar; } public void EnemyAttack() { savedPlayer.health = savedPlayer.health - savedEnemy.ar; } public void textChanges() { savedArray[0].text = savedPlayer.health.ToString(); savedArray[1].text = savedPlayer.armor.ToString(); savedArray[2].text = savedPlayer.ar.ToString(); savedArray[3].text = savedEnemy.health.ToString(); savedArray[4].text = savedEnemy.armor.ToString(); savedArray[5].text = savedEnemy.ar.ToString(); } // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { attackButton(); } private void Awake() { Combat fight = new Combat(Player, Enemy, textArray); } } 

Character:

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Inventory { public int[] inventory; public Inventory(int inventorySlots) { inventory = new int[inventorySlots]; Debug.Log("Your Inventory has " + inventorySlots + " slots."); } } public class Character : MonoBehaviour { public Inventory charInventory; public int health = 125; public int armor = 10; public int cash = 100; public int ar = 45; public bool weaponEquipped; public Character() { Inventory charInventory = new Inventory(20); } //HaheaghehAEH public void attack(Character enemy) { int oldHealth = enemy.health; enemy.health = enemy.health - ar; int damage = oldHealth - enemy.health; Debug.Log(damage + " damage!"); } public Character returnChar() { Character x = new Character(); return x; } } 

Player:

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Player : Character { public Player() { health = 150; ar = 45; } } 

Enemy:

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Enemy : Character { public Enemy() { health = 87; ar = 15; Inventory enemyInventory = new Inventory(5); } } 

Error Message

Enemy object gets saved in savedEnemy

Filled

\$\endgroup\$
7
  • \$\begingroup\$ The code is all in the same script? \$\endgroup\$ Commented Apr 9, 2022 at 16:10
  • \$\begingroup\$ Can you edit this question to present a Minimal Complete Verifiable Example? That is, the whole contents of every script we'd need to place in a new empty project to reproduce this behaviour. That will eliminate any ambiguity about what's going on in the parts of the script you haven't shown. Cut out anything that's not required to compile and cause the problem. \$\endgroup\$ Commented Apr 9, 2022 at 16:13
  • \$\begingroup\$ Hello, sorry for taking so long, I have added a "Minimal" Complete Verifiable Example. I made a new Project and "remade" it there in a simpler way (which is the aforementioned verifiable example that you can see in the new part of the original post), yet the same error still appears. \$\endgroup\$ Commented Apr 9, 2022 at 19:32
  • \$\begingroup\$ It's not OK to construct MonoBehaviour instances with new - Unity should be throwing warnings for you about that. Did you perhaps mean to use AddComponent to attach a new instance of a MonoBehaviour component, or GetComponent to get a reference to an existing one? \$\endgroup\$ Commented Apr 9, 2022 at 20:03
  • \$\begingroup\$ I saw the warnings but ignored them because to me it seemed that it was absolutely necessary to create a new Object of combat (i didnt know what else to do, but the answer to this post shows that not only was it not needed, it actually breaks the program). Was there anything I could have done with AddComponent or GetComponent to get a similar effect and have the program still work? \$\endgroup\$ Commented Apr 9, 2022 at 21:23

1 Answer 1

1
\$\begingroup\$

You declared a combat local variable here:

Combat fight = new Combat(Player, Enemy, textArray); 

This is not the same object you mounted on the gameobject. Maybe you don't need a constructor but an initializer function like:

public void CombatInit(Player Player, Enemy Enemy, Text[] textArrayPass) { savePlayer(Player); saveEnemy(Enemy); saveTextArray(textArrayPass); //textChanges(); // FightLoop(); textChanges(); } private void Awake() { CombatInit(Player, Enemy, textArray); } 
\$\endgroup\$
1
  • \$\begingroup\$ Thank you very much, this fixes all the issues I have in my program. Just for clarification, the way I did it doesn't work because I create a "second" Combat Object that has no use or something, right? I don't fully grasp why this fixes the issues. \$\endgroup\$ Commented Apr 9, 2022 at 21:19

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.