- Home /
Is there any way to receive callbacks from the Unity Engine without creating a MonoBehaviour?
Is there any way to write a class (not inheriting from MonoBehaviour) that somehow can itself subscribe to an Unpdate or other Events of the Unity Engine?
I'd like to write a Utility that does some custom GL drawing - but the only way so far I see is by creating a GameObject that will have an attached monoBehaviour to receive OnRenderObject events. Is there no way around this?
Answer by whydoidoit · Oct 18, 2013 at 11:19 PM
No there is no way around this, and why would there be :)
You can make a game object and attach a script to call your OnRenderObject in your custom class though. And you can do that at run time rather than in the Editor and if you like you can hide it from the Editor view too:
Support.cs
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class Support : MonoBehaviour
{
public static Support instance;
void Awake()
{
instance = this;
}
public OtherClass owner;
void Update()
{
if(owner != null)
owner.Update();
}
}
Example.cs
using UnityEngine; using System.Collections;
public class TestAddComponent : MonoBehaviour {
public OtherClass someClass;
}
[System.Serializable]
public class OtherClass
{
public string something;
public virtual void Update()
{
Debug.Log("Updating");
}
public OtherClass()
{
if(!Support.instance)
{
var go = new GameObject();
go.hideFlags = HideFlags.HideAndDontSave;
var support = go.AddComponent<Support>();
support.owner = this;
}
else
{
Support.instance.owner = this;
}
}
}
thank you for the idea of HideAndDontSave. I'm a bit concerned though as I don't fully understand the description of HideFlags.Hide: "The object will not be saved to the scene. It will not be destroyed when a new scene is loaded. It is your responsibility to cleanup the object manually using DestroyImmediate, otherwise it will leak." Does this only count for new scenes loaded at runtime, or may the Object even still stick around in the editor if I stop playmode? How do I watertightly clean up this object? Also: How can I avoid that 'Support' is added by other scripts that are not under my control?
You should make Someclass IDisposable and destroy the GO when it's done with (or any other dispose method you prefer). At the present time the go would be permanent even if other scenes were loaded (which is normally what you want with a support class like this).
currently I have two problems with this approach. 1st I cannot add the Support component, I get a "Can't add script behaviour . The scripts file name does not match the name of the class defined in the script! UnityEngine.GameObject:AddComponent()". Second, each time I hit play, a new support game object is created that remains in the scene. I need my script to work with [ExecuteInEdit$$anonymous$$ode], but there should be never more than one Support GO in the scene. I don't see currently how and when to properly create and destroy the GO to ensure this.
Ouch really. It use to work for non permanent gos to have a non specific component. Haven't checked it recently will take a look
Answer by user012345 · Jan 28, 2021 at 03:02 PM
Since you posted a comment above, let me post a similar comment here. You get a -1 because:
You bumped a 8 years old question. A lot things have changed since then.
Your answer does not apply to the Unity engine because the delegate you linked is an editor only callback which can not be used at runtime.
You haven't read the question that was asked because it was about custom GL drawing which can not be done from Update. You need to hook into the game loop at the rendering stage and therefore you need a callback like OnRenderObject or OnGUI
Answer by Huacanacha · Oct 18, 2013 at 10:43 PM
You can easily roll this yourself if you already have a manager/controller style game object (or even something constant like the camera etc). Just add a callback mechanism in it's main script that allows non-MonoBehavior scripts to register to be called during Update(). I'm not sure about JS but in C# you can create an Interface that the non-MonoBehavior scripts can inherit from, and you can use the same Update() call signature to keep things clear. I use this technique to add Listeners to some key game events I want to track, so I can use thin utility scripts to respond rather than full fat MonoBehavior classes.
yes this is clear, but if possible i'd like to come up with some solution that does not depend upon other scripts or a certain scene setup and does not add gameObjects to the scene
Answer by wolfgraphicsLLC · Jan 28, 2021 at 07:17 PM
Hello @temptest123 I think that the best answer is being overlooked altogether, ```Is there any way to write a class (not inheriting from MonoBehaviour) that somehow can itself subscribe to an Update or other Events of the Unity Engine?
I'd like to write a Utility that does some custom GL drawing - but the only way so far I see is by creating a Game Object that will have an attached monoBehaviour to receive OnRenderObject events. Is there no way around this?
Add comment``` from you statement and question here has no one suggested an abstract class to you or have you not ran across it in the api because that somehow can itself subscribe to an Unpdate or other Events of the Unity Engine?
this right here is pritty much aht it boes as an exsample here is an enemy ai class i wrote for a platformer for a client. ```using System.Collections; using System.Collections.Generic; using UnityEngine;
// doing it this way sets an abstract class so all of the enemy AI will feed off this one script as a base for there movement animations and such with there special actions for that enemy set in there own. public abstract class TS_Guardian_Enemy : MonoBehaviour { // serializefiels on the variables so the base stats can be set in the inspector on each enemy.
// this one is for the enemys healt.
[SerializeField]
protected int _health;
// the speed at which the enemy moves from point to point.
[SerializeField]
protected int _speed;
// the amount of in game currency the enemy can and will drop.
[SerializeField]
protected int _emeraldLeaf;
// the transition points in which the enemy will patrol.
[SerializeField]
protected Transform pointA, pointB;
// a base line attack method that all enemies will be able to preform without having to write it in there script.
public virtual void _attack()
{
Debug.Log("BaseAttack Called!");
}
// the base absrtact update method for the enemy AI in the set.
public abstract void Update();
} here is the main base enemy AI abstract class script and then it controles this named enemy subclass some what like a helper script.
using System.Collections; using System.Collections.Generic; using UnityEngine;
using TS_Guardian;
public class TS_Guardian_Enemy_AI_plagueSquirrel : TS_Guardian_Enemy {
private bool _switching = false;
// in the void start set the ability to call the base attack function set in the main enemy script.
private void Start()
{
_attack();
}
// inorder to function correctly the void updat has to be an override to over ride the base enemy script actions to add custom movement update and attacks spicific to the enemy its on.
public override void Update()
{
if (transform.position == pointA.position)
{
Debug.Log("Point A");
_switching = false;
}
else if (transform.position == pointB.position)
{
Debug.Log("Point B");
_switching = true;
}
if (_switching == false)
{
// move right
transform.position = Vector3.MoveTowards(transform.position, pointB.position, _speed * Time.deltaTime);
}
else if (_switching == true)
{
// move left
transform.position = Vector3.MoveTowards(transform.position, pointA.position, _speed * Time.deltaTime);
}
}
public override void _attack()
{
Debug.Log("Plague Squirrel put the smack down on ya!");
}
}```