Pretty common question but for the life of me can’t figure out where I’m going wrong.
Brand new to Unity and trying to follow along with a simple turn-based RPG tutorial. Fine up to this point but returning this error when I press the ‘melee’ attack button. If anyone could point me in the right direction, that would be great.
Probably over-kill but added all the code I have been using so far.
Full Error Message:
NullReferenceException: Object reference not set to an instance of an object
AttackScript.Attack (UnityEngine.GameObject victim) (at Assets/Scripts/AttackScript.cs:45)
FighterAction.SelectAttack (System.String btn) (at Assets/Scripts/FighterAction.cs:38)
MakeButton.AttachCallback (System.String btn) (at Assets/Scripts/MakeButton.cs:23)
MakeButton+<>c__DisplayClass2_0.b__0 () (at Assets/Scripts/MakeButton.cs:16)
UnityEngine.Events.InvokableCall.Invoke () (at C:/buildslave/unity/build/Runtime/Export/UnityEvent/UnityEvent.cs:166)
UnityEngine.Events.UnityEvent.Invoke () (at C:/buildslave/unity/build/Runtime/Export/UnityEvent/UnityEvent/UnityEvent_0.cs:58)
UnityEngine.UI.Button.Press () (at C:/Program Files/Unity/Hub/Editor/2019.2.17f1/Editor/Data/Resources/PackageManager/BuiltInPackages/com.unity.ugui/Runtime/UI/Core/Button.cs:68)
UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at C:/Program Files/Unity/Hub/Editor/2019.2.17f1/Editor/Data/Resources/PackageManager/BuiltInPackages/com.unity.ugui/Runtime/UI/Core/Button.cs:110)
UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at C:/Program Files/Unity/Hub/Editor/2019.2.17f1/Editor/Data/Resources/PackageManager/BuiltInPackages/com.unity.ugui/Runtime/EventSystem/ExecuteEvents.cs:50)
UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) (at C:/Program Files/Unity/Hub/Editor/2019.2.17f1/Editor/Data/Resources/PackageManager/BuiltInPackages/com.unity.ugui/Runtime/EventSystem/ExecuteEvents.cs:261)
UnityEngine.EventSystems.EventSystem:Update() (at C:/Program Files/Unity/Hub/Editor/2019.2.17f1/Editor/Data/Resources/PackageManager/BuiltInPackages/com.unity.ugui/Runtime/EventSystem/EventSystem.cs:377)
Make Button Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class MakeButton : MonoBehaviour
{
[SerializeField]
private bool physical;
private GameObject hero;
void Start()
{
string temp = gameObject.name;
gameObject.GetComponent<Button>().onClick.AddListener(() => AttachCallback(temp));
hero = GameObject.FindGameObjectWithTag("Hero");
}
private void AttachCallback(string btn)
{
if (btn.CompareTo("MeleeBtn") == 0)
{
hero.GetComponent<FighterAction>().SelectAttack("melee");
} else if (btn.CompareTo("RangeBtn") == 0)
{
hero.GetComponent<FighterAction>().SelectAttack("range");
} else
{
hero.GetComponent<FighterAction>().SelectAttack("run");
}
}
}
Fighter Action Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class FighterAction : MonoBehaviour
{
private GameObject hero;
private GameObject enemy;
[SerializeField]
private GameObject meleePrefab;
[SerializeField]
private GameObject rangePrefab;
[SerializeField]
private Sprite faceIcon;
private GameObject currentAttack;
private void start()
{
hero = GameObject.FindGameObjectWithTag("Hero");
enemy = GameObject.FindGameObjectWithTag("Enemy");
}
public void SelectAttack(string btn)
{
GameObject victim = hero;
if(tag == "Hero")
{
victim = enemy;
}
if(btn.CompareTo("melee") == 0)
{
meleePrefab.GetComponent<AttackScript>().Attack(victim);
} else if(btn.CompareTo("range") == 0)
{
//rangePrefab.GetComponent<AttackScript>().Attack(victim);
Debug.Log("Range Attack");
} else
{
Debug.Log("Run");
}
}
}
Attack Script Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class AttackScript : MonoBehaviour
{
public GameObject owner;
[SerializeField]
private string animationName;
[SerializeField]
private bool magicAttack;
[SerializeField]
private float magicCost;
[SerializeField]
private float minAttackMultiplier;
[SerializeField]
private float maxAttackMultiplier;
[SerializeField]
private float minDefenceMultiplier;
[SerializeField]
private float maxDefenceMultiplier;
private FighterStats attackerStats;
private FighterStats targetStats;
private float damage = 0.0f;
private float xMagicNewScale;
private Vector2 magicScale;
private void Start()
{
magicScale = GameObject.Find("HeroMagicFill").GetComponent<RectTransform>().localScale;
}
public void Attack(GameObject victim)
{
attackerStats = owner.GetComponent<FighterStats>();
targetStats = victim.GetComponent<FighterStats>();
if(attackerStats.magic >= magicCost)
{
float multiplier = Random.Range(minAttackMultiplier,maxAttackMultiplier);
attackerStats.updateMagicFill(magicCost);
damage = multiplier * attackerStats.melee;
if(magicAttack)
{
damage = multiplier * attackerStats.magicRange;
attackerStats.magic -= magicCost;
}
float defenceMultiplier = Random.Range(minDefenceMultiplier, maxDefenceMultiplier);
damage = Mathf.Max(0, damage - (defenceMultiplier * targetStats.defence));
owner.GetComponent<Animator>().Play(animationName);
targetStats.ReceiveDamage(damage);
}
}
}
Fighter Stats Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
public class FighterStats : MonoBehaviour, IComparable
{
[SerializeField]
private Animator animator;
[SerializeField]
private GameObject healthFill;
[SerializeField]
private GameObject magicFill;
[Header("Stats")]
public float health;
public float magic;
public float melee;
public float magicRange;
public float defence;
public float speed;
public float experience;
private float startHealth;
private float startMagic;
[HideInInspector]
public int nextActTurn;
private bool dead = false;
//Resize Health and Magic Bar
private Transform healthTransform;
private Transform magicTransform;
private Vector2 healthScale;
private Vector2 magicScale;
private float xNewHealthScale;
private float xNewMagicScale;
private void Start()
{
healthTransform = healthFill.GetComponent<RectTransform>();
healthScale = healthFill.transform.localScale;
magicTransform = magicFill.GetComponent<RectTransform>();
magicScale = magicFill.transform.localScale;
startHealth = health;
startMagic= magic;
}
public void ReceiveDamage(float damage)
{
health -= damage;
animator.Play("Damage");
if(health <= 0)
{
dead = true;
gameObject.tag = "Dead";
Destroy(healthFill);
Destroy(gameObject);
} else
{
xNewHealthScale = healthScale.x * (health / startHealth);
healthFill.transform.localScale = new Vector2(xNewHealthScale, healthScale.y);
}
}
public void updateMagicFill(float magicCost)
{
magic -= magicCost;
xNewMagicScale = magicScale.x * (magic / startMagic);
magicFill.transform.localScale = new Vector2(xNewMagicScale, magicScale.y);
}
public int CompareTo(object otherStats)
{
int nex = nextActTurn.CompareTo(((FighterStats)otherStats).nextActTurn);
return nex;
}
}