- Home /
C# Creating my own custom Unity Messages
Unity has a bunch of "Messages" that are functions you can write implementation for - In example Awake, OnBecameVisible, Update, and LateUpdate.
Say I had a class Character that inherits from Monobehaviour
and three classes that inherit from Character such as NPC, Enemy and Hero
I'd like to have the Character Class have a message it sends called "OnTakeDamage" I'd like to implement it in the Enemy and Hero class but not the NPC class. (Just like you don't have to use all messages from Monobehaviour)
Is it possible to write my own class that has messages sent for classes to inherit from (like a OnTakeDamage)? Also how would I do it?
(Note: I am aware of the SendMessage() function of Monobehaviour. SendMessage however does "Calls the method named methodName on every MonoBehaviour in this game object.")
Thanks
Just add the function to the Character
class, then override it in the Enemy
and Hero
classes. You can then call the function on any "character", with it only doing something on Enemy
and Hero
instances.
Perhaps this is off topic, but since we know the NPC class doesn't need the OnTakeDamage function, is it not bad design to force the class to implement it? Does this not call for another subclass of Character that the Enemy and Hero can inherit from with the method defined, and one that the npc can inherit from without it?
"is it not bad design to force the class to implement it?" I never stated that the method should throw an exception if it isn't implemented. Something along the lines of:
class Character extends $$anonymous$$onoBehaviour {
function OnTakeDamage() {
}
}
class NPC extends Character {
}
Works just fine and reduces the complexity of the code. If there are an arbitrary amount of functions that exist between the classes NPC
, Enemy
and Hero
, that would mean that you would need not only the baseclass Character
but also something like NPCEnemy
, NPCHero
, EnemyHero
, which makes it way more difficult to maintain that just 4 classes.
Ok, it was just when you said "override". I thought that would mean exposing that function using virtual and therefore it would be inherited by the NPC class. I wasn't doubting you, just trying to clear it up for myself.
@T27$$anonymous$$ Fair enough. I should have probably avoided the word "override", I'm just too used to working in other languages ;)
Answer by nesis · Mar 30, 2014 at 01:07 AM
There's a few ways, but if you want to follow object oriented programming logic, then I'd suggest making base class from which all damagable objects extend. Eg:
In DamageSource.cs (the class you'd use to give damage to )
using UnityEngine;
using System.Collections;
public class DamageSource : MonoBehaviour {
protected float damageAmount = 10f;
//not 100% necessary, but handy for an example of how to
//handle damage based on the attacker (which is
//relevant for info sent in the OnTakeDamage() method
protected ElementType elementType = ElementType.Normal;
//we use a function for getting the damage this
//DamageSource can do because it lets us overwrite it.
//Eg, if the enemy is weakened, you can factor that
//in and return a lesser amount of damage.
public float GetDamageAmount() {
return damageAmount;
}
public ElementType GetElementType() {
return elementType;
}
}
//kinds of elements available for damage / resistance calculations
public enum ElementType {
Normal,
Fire,
Ice,
Lightning
}
In DamageableObject.cs (the base class from which all damageable objects inherit):
using UnityEngine;
using System.Collections;
public class DamageableObject : MonoBehaviour {
protected bool wasRecentlyHit;
protected float health;
protected float maxHealth;
public void Awake() {
health = maxHealth;
}
//Creating a virtual void method lets you choose whether
//or not you want to set it in a derived class.
//Here, we track the amount of damage and the source
//the damage came from. This can sometimes be handy for
//context-sensitive reactions to being damaged. Eg, play
//a particular sound in damaging the player, when
//successfully damaged by a particular attack.
//Note that this base implementation does nothing - you
//override it in an inheriting class, very similar to using Update() etc.
protected virtual void OnTakeDamage(float damageAmount, DamageSource damageSource) {}
//An example of how you'd check whether damage is incoming.
//You can alternatively just call
//someDamageableObject.TryDoDamage() from another script.
public void OnTriggerEnter(Collider other) {
DamageSource damageGiver = other.GetComponent<DamageSource>();
if (damageGiver) {
TryDoDamage(damageGiver.GetDamageAmount(),damageGiver.gameObject);
}
}
public void TryDoDamage(float damageAmount, GameObject damageGiver) {
//early out, this DamageableObject was damaged a very
//short time ago and shouldn't be damaged again so soon
if (wasRecentlyHit) return;
//optionally perform any damage calculations here based
//on the damageGiver, eg more damage from the player
//being weakened somehow, or less damage from type
//resistances... etc.
damageAmount = CalculateDamage(damageAmount,damageGiver);
//if after our damage calculations we still have an
//amount of damage greater than 0, we do the damage and
//send the OnTakeDamage() message.
if (damageAmount>0f) {
health -= damageAmount;
//optional handling of dying (uncomment this and the OnDeath() function to enable)
//if (health<0f) {
// OnDeath(damageAmount,damageGiver);
//}
//else {
OnTakeDamage(damageAmount,damageGiver);
//}
}
}
//Uncomment this and the (healtn<0f) if statement above
//if you want to handle dying as well as being damaged
//protected virtual void OnDeath(float damageAmount, DamageSource damageSource);
//Default implementation for calculating damage,
//given some amount of damage, and some source of damage.
//Override this in an inheriting class if you want to do
//different damage, eg based on the damage source (2x
//damage from fire attacks, 0.5x damage from ice
//attacks... etc) or based on the DamageableObject's
//current state (eg, player is weakened, so takes 1.5x damage)
protected float CalculateDamage(float damageAmount, DamageSource damageSource) {
return damageAmount;
}
}
In PlayerDamageReceiver.cs:
using UnityEngine;
using System.Collections;
public class PlayerDamageReceiver : DamageableObject {
//override the OnTakeDamage() method to make a
//different implementation of it for this class
protected override void OnTakeDamage(float damageAmount, DamageSource damageSource) {
Debug.Log("Ouch, the player was damaged!");
}
//Uncomment this to override the OnDeath() function
//in DamageableObject (if you've uncommented that, that is)
//protected override void OnDeath(float damageAmount, DamageSource damageSource) {
// Debug.Log("Uhoh... The player died. :(");
//}
//override the CalculateDamage() function to
//determine how damage applies to the player
protected override float CalculateDamage(float damageAmount, DamageSource damageSource) {
//Example: give the player a 2x weakness to fire damage, and immunity to ice damage
switch (damageSource.GetElementType()) {
case (ElementType.Fire):
damageAmount *= 2f;
break;
case (ElementType.Ice):
damageAmount = 0f;
break;
}
return damageAmount;
}
}
Answer by Bunny83 · Mar 30, 2014 at 01:16 AM
SendMessage is the exact counter part to the built-in messages. They are also called on every MonoBehaviour on the gameobject. For example if you have two scripts attached to the same gameobject which both implement OnCollisionEnter, then OnCollisionEnter will be called on both scripts when a collision occurs.
Keep in mind that SendMessage has an additional SendMessageOptions paramete. Using SendMessageOptions.DontRequireReceiver doesn't force that the message has to be implemented by any script.
No, i didn't say Unity uses Send$$anonymous$$essage to invoke the built-in messages. I said that it works the same (in the sense of what is called on which objects and if it's forced to be implemented or not).
from what we know, Unity uses some kind of hybrid solution for the built-in messages. First of all Unity uses reflection to call the messages. However Unity might cache each types $$anonymous$$ethodInfos to speed up it's invocation. Unity also seems to optimise the calling depending on some meta data if a certain class actually implements a certain callback or not. $$anonymous$$ost of those things are done in native code in the engine's core.
Send$$anonymous$$essage on the other hand also uses reflection to invoke the method but of course in a more dynamic way. I wouldn't call it "horribly inefficient". Yes, reflection has a quite bad execution time compared to direct invocation, however it offers more flexibility.
If you want to use direct invocation you shouldn't use base-class inheritance. It restricts your class to that one parent class. It's better to use interfaces. However when using interfaces or base-classes you still need to do some kind of GetComponent which is probably the real bottleneck.
NGUI for example does use Send$$anonymous$$essage extensively for almost all aspects because it simplifies the usage and goes hand in hand with the built-in message philosophy from the users point of view.
Your answer

Follow this Question
Related Questions
Get List of Signatures of Messages to Monobehaviour 1 Answer
Is There a List of MonoBehaviour Messages That Are Still Called When Disabled? 1 Answer
Which Monobehaviour messages get called in the inspector (scene not playing)? 0 Answers
Sync Event with delegate and event 0 Answers
Message Boxes and running .exe files 3 Answers