public class UnitControllerGraves : UnitControllerCore
{
public void InitializeGraves(){
BuffBase.CreateNewBuff("ToughMechanism", "Tough Mechanism", sv, 3f, bf, -1f, null, ToughMechanism_OnStart, ToughMechanism_OnEnd, null, 1, true);
}
public void OnDashEnd(){
anim.SetTrigger("DashEnded");
dashing = false;
agent.speed = stats.moveSpeed;
agent.angularSpeed = 1000;
agent.radius = .25f;
agent.obstacleAvoidanceType = ObstacleAvoidanceType.HighQualityObstacleAvoidance;
rotateTowardsDash = false;
ResetAttackPeriod();
BuffBase.AddBuff(gameObject, gameObject, "ToughMechanism", 1);
}
public void ToughMechanism_OnStart(){
if (toughMechCor != null){
StopCoroutine(toughMechCor);
}
if (mainModel == null){
Debug.Log("?");
}
agent.speed = 20f;
//toughMechCor = StartCoroutine(ToughMechanism_Blend(true));
}
public void ToughMechanism_OnEnd(){
if (toughMechCor != null){
StopCoroutine(toughMechCor);
}
//toughMechCor = StartCoroutine(ToughMechanism_Blend(false));
}
}
public class UnitControllerCore : MonoBehaviour
{
// Modifyable information
private const int c_attackAnimIntRangeSize = 2;
public int[] attackAnimIntRange = new int[c_attackAnimIntRangeSize];
// A list of buffs
public List<BuffBase> buffContainer = new List<BuffBase>();
// I'm not sure this needs to be public, but whatever
public PhotonView PV;
public Animator anim;
public StatusModule stats;
public NavMeshAgent agent;
public GameObject weaponLaunchSource;
public GameObject weaponLaunchFX;
public GameObject weaponImpactFX;
public GameObject weaponMissileFX;
public GameObject[] abilFX;
public AudioClip[] abilSound;
public Material[] materialRefs;
public GameObject mainModel;
}
public class BuffBase : MonoBehaviour
{
// Public vars
// Buff name
public string buffName = "?un-namedBuff!";
// Buff id
public string buffId = "?buffId!";
// Buff flags
public BuffType buffFlags = BuffType.None;
// StatusVars that will be added at the beginning and subtracted at the end to the target
public StatusVars svars = new StatusVars();
// Source of the buff
public GameObject source;
// Target of the buff
public GameObject target;
// Length of buff
public float timer = 0f;
// Every periodicTick time passed, OnPeriodicTick is called
public float periodicTick = -1f;
// Delegate functions
public delegate void OnEventGenericFunction();
// Called every periodicTick loop
public OnEventGenericFunction onPeriodicTick;
// Called when buff starts
public OnEventGenericFunction onBuffStart;
// Called when buff ends, regardless of the timer fully reached or not
public OnEventGenericFunction onBuffEnd;
// Called when buff finishes, only when the timer is fully reached
public OnEventGenericFunction onBuffFinish;
// Private vars
// Tick left before periodic is called again
private float m_storedTick = 0f;
// Base duration of the buff, readonly
private float m_baseDuration = -1f;
public float baseDuration{
get {return m_baseDuration;}
}
// List of predefined buffs
protected static List<string> definedId = new List<string>();
protected static List<string> definedName = new List<string>();
protected static List<StatusVars> definedSvars = new List<StatusVars>();
protected static List<int> definedMaxStack = new List<int>();
protected static List<bool> definedRefreshStacks = new List<bool>();
protected static List<float> definedTimer = new List<float>();
protected static List<float> definedTick = new List<float>();
protected static List<BuffType> definedFlags = new List<BuffType>();
protected static List<OnEventGenericFunction> definedOPT = new List<OnEventGenericFunction>();
protected static List<OnEventGenericFunction> definedOBS = new List<OnEventGenericFunction>();
protected static List<OnEventGenericFunction> definedOBE = new List<OnEventGenericFunction>();
protected static List<OnEventGenericFunction> definedOBF = new List<OnEventGenericFunction>();
// When creating a new type of buff use this
public static int CreateNewBuff(string id, string n, StatusVars info, float baseDur, BuffType flags, float pt, OnEventGenericFunction f1, OnEventGenericFunction f2, OnEventGenericFunction f3, OnEventGenericFunction f4, int maxStacks, bool refresh){
OnEventGenericFunction tf;
// If string id already exists, return null;
if (definedId.Contains(id)){
Debug.Log(id + ", id has already been used to create a buff!");
return -1;
}
// Add all the new information
definedId.Add(id);
definedName.Add(n);
definedSvars.Add(info);
definedTimer.Add(baseDur);
definedFlags.Add(flags);
definedTick.Add(pt);
definedMaxStack.Add(maxStacks);
definedRefreshStacks.Add(refresh);
// Add delegates
definedOPT.Add(f1);
definedOBS.Add(f2);
definedOBE.Add(f3);
definedOBF.Add(f4);
// Return index
return definedId.IndexOf(id);
}
public static List<BuffBase> GetBuffsOfType(GameObject target, string id){
List<BuffBase> list = new List<BuffBase>(target.GetComponents<BuffBase>());
List<BuffBase> fList = new List<BuffBase>();
foreach (BuffBase b in list){
if (b.buffId == id){
fList.Add(b);
}
}
return fList;
}
// When you want to add a buff to a target use this
public static void AddBuff(GameObject source, GameObject target, string id, int amt){
if (!definedId.Contains(id)){
Debug.Log("AddBuff: Invalid id entered!");
return;
}
int i = definedId.IndexOf(id);
// Remove previous stacks if we are
int savedStacks;
List<BuffBase> curStacks = GetBuffsOfType(target, id);
savedStacks = curStacks.Count;
if (savedStacks + amt > definedMaxStack[i]){
foreach (BuffBase temp in curStacks){
temp.DestroyBuff(false);
savedStacks -= 1;
if (savedStacks + amt <= definedMaxStack[i]){
break;
}
}
}
if (definedRefreshStacks[i]){
foreach(BuffBase b in GetBuffsOfType(target, id)){
b.timer = b.m_baseDuration;
}
}
for (int lc = amt; lc > 0; lc--){
BuffBase newBuff = target.AddComponent(typeof(BuffBase)) as BuffBase;
newBuff.buffName = definedName[i];
newBuff.buffId = definedId[i];
newBuff.svars = definedSvars[i];
newBuff.buffFlags = definedFlags[i];
newBuff.timer = definedTimer[i];
newBuff.periodicTick = definedTick[i];
newBuff.onPeriodicTick = definedOPT[i];
newBuff.onBuffStart = definedOBS[i];
newBuff.onBuffEnd = definedOBE[i];
newBuff.onBuffFinish = definedOBF[i];
newBuff.source = source;
newBuff.target = target;
newBuff.m_baseDuration = newBuff.timer;
StatusModule tStats = target.GetComponent<StatusModule>();
tStats.AddStatusVars(newBuff.svars, 1f);
}
}
// Use this to destroy the buff
public void DestroyBuff(){
StatusModule tStats = target.GetComponent<StatusModule>();
tStats.AddStatusVars(this.svars, -1f);
if (this.onBuffEnd != null){
this.onBuffEnd();
}
UnityEngine.Object.Destroy(this);
}
// Does not call the end func depending on param
public void DestroyBuff(bool doEndFunc){
StatusModule tStats = target.GetComponent<StatusModule>();
tStats.AddStatusVars(this.svars, -1f);
if (this.onBuffEnd != null && doEndFunc){
this.onBuffEnd();
}
UnityEngine.Object.Destroy(this);
}
// Start is called before the first frame update
void Start()
{
if (this.onBuffStart != null){
this.onBuffStart();
//this.GetComponent<UnitControllerGraves>().ToughMechanism_OnStart();
}
}
// Update is called once per frame
void Update()
{
// Firstly check if we need to do any periodic functions
if (onPeriodicTick != null){
// Reset the tick if we need to and call functions as necessary
if (m_storedTick > 0f){
m_storedTick = periodicTick;
onPeriodicTick();
}
// Otherwise reset the tick
else {
m_storedTick -= Time.deltaTime;
}
}
// Decrease the timer if necessary
if (baseDuration != -1f){
timer -= Time.deltaTime;
// If buff has ended, call the onBuffEnd and destroy the buff
if (timer <= 0f){
this.DestroyBuff();
}
}
}
I have this weird issue with delegate functions in these particular scripts. I use delegates to store functions to call when a buff for a character has started/ended. But for some reason when I call them, I can not properly retrieve gameObject member of MonoBehaviour class and a bunch of other public variables that is originally class members. I think the delegate function is just not using proper class instance to call the function (kind of?), but while I think this some members like floats are being retrieved properly so I’m confused…
I know I’m wording this poorly, but basically the delegate functions they only half work, they work in the sense that they’re called but when I try to do anything with changing some class members ( they are public ), it throws errors and tells me the class instance has been destroyed or nulled even though it’s not.
This is strange because, in another pack of scripts this same technique works fine and works completely as intended. Also it is able to read some publicly declared variables like materials.
TLDR:
Look at line 19 - That adds a buff and it has a few delegate functions passed in that will be called when buff starts/ends.
Look at line 26 - That is one of the functions being called, that line specifically references a variable ‘mainModel’ in the parent class. Even though it’s set (I’ve checked many many times), it says the reference is a null reference. It also says the class instance of the UnitControllerGraves has been destroyed - even though it clearly isn’t during live tests. I’ve tried checking other stored variables to see if maybe the class instance is being stored properly, GameObject types specifically have this issue. I also can not even reference gameObject, one of the default members of monoBehaviour.
Perhaps delegates are not meant to be used in this manner and I’ve just gotten lucky in my other attempt. I’ve removed a lot of the scripts, but these are the important parts that will help you understand what it is they’re doing.
I’ve been banging my head at this for hours with no progress. I was hoping someone could shed light on this issue.
Also, I’m not a seasoned programmer with strong coding fundamentals so if you know of a better way to go about approaching this please let me know! I understand the code can seem kind of long, but it’s very simple if you take a glance at it.
Just about everything is working perfectly, except the delegate part T_T.
I’m using 2019.2 version