Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
2
Question by Diet-Chugg · Mar 29, 2014 at 11:52 PM · monobehaviourmessages

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

Comment
Add comment · Show 6
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Benproductions1 · Mar 30, 2014 at 12:27 AM 0
Share

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.

avatar image T27M · Mar 30, 2014 at 12:40 AM 0
Share

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?

avatar image Benproductions1 · Mar 30, 2014 at 12:48 AM 0
Share

"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.

avatar image T27M · Mar 30, 2014 at 01:10 AM 0
Share

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.

avatar image Benproductions1 · Mar 30, 2014 at 02:08 AM 0
Share

@T27$$anonymous$$ Fair enough. I should have probably avoided the word "override", I'm just too used to working in other languages ;)

Show more comments

2 Replies

· Add your reply
  • Sort: 
avatar image
1
Best Answer

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;
     }
 }
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
1

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.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Bunny83 · Mar 30, 2014 at 03:46 AM 1
Share

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

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

24 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

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


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges