Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 14 Next capture
2021 2022 2023
2 captures
12 Jun 22 - 14 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 /
  • Help Room /
avatar image
0
Question by Cirrocumulus · Feb 03, 2018 at 07:52 PM · interfaces

From interface to gameobject

I'm still trying to get my mind around interfaces and their uses. I have an EventManager which handles events such as this, sent from a UFOController script when the UFO is hit by a player weapon:

 EventManager.Instance.UFOKilledByPlayer(this);

This is picked up by EventManager, which has this among other things:

 public Action<UFOController> OnUFOKilledByPlayer;
 public void UFOKilledByPlayer(UFOController ufo) { if (OnUFOKilledByPlayer != null) OnUFOKilledByPlayer(ufo); }

I have a UIManager component which subscribes to that event and displays a small score value where the UFO was hit, like this:

 public void ShowPointsAtScreenPosition(UFOController ufo)
 {
     Text t = Instantiate(textRoaming);
     t.transform.SetParent(canvas.transform, false);
     t.transform.position = Camera.main.WorldToScreenPoint(ufo.gameObject.transform.position);
     t.text = ufo.pointValue.ToString();
     Destroy(t, 1.0f);
 }

This works fine. However, it only works for the UFOController class. So I tried to generalize so I could apply to other objects which are not UFOs, by defining a basic "IKillable" interface and adding it to the UFOController class (in the class declaration line, next to MonoBehaviour):

 public interface IKillable {

     void Kill();

 }

If I now redefine the event in EventManager:

 public Action<IKillable> OnSomethingKilledByPlayer;

And do the same everywhere along the chain of events, I get to this:

 public void ShowPointsAtWorldPosition(IKillable k)
 {
     // blah
 }

But I can't find any way of finding the object that the variable k came from. I read somewhere that I should cast it but it doesn't seem to work, even if I cast it to MonoBehaviour.

As a side note, I can put the "ShowPoints" method as a separate, dedicated component on the gameobjects I'd like to use it, so that they display their point value themselves before being destroyed by Unity, or (maybe better) on the explosion gameobject itself that is instantiated when destroyed (a particle effect and an audio clip). This would seem to conform to the Unity principle of "one component per behaviour". But then I'm totally coupling the UI to the game logic, am I not?

Thanks for your help.

Comment
Add comment
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

2 Replies

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

Answer by ElijahShadbolt · Feb 04, 2018 at 04:00 AM

Option 1: Interface

 public interface IKillable {
     GameObject gameObject { get; }
     int pointValue { get; }
     void Kill();
 }

 public class UFOController : MonoBehaviour, IKillable {
     // property `gameObject` inherited from MonoBehaviour.
     public int pointValue { get { return 42; } }
     public void Kill() {
         EventManager.Instance.SomethingKilledByPlayer(this);
     }
 }

 public class OtherController : MonoBehaviour, IKillable {
     public int pointValue { get { return 88; } }
     public void Kill() {
         EventManager.Instance.SomethingKilledByPlayer(this);
     }
 }

Option 2: Inheritance

 // base class
 public class Killable : MonoBehaviour {
     public virtual int pointValue { get { return 88; } }
     public void Kill() {
         EventManager.Instance.SomethingKilledByPlayer(this);
     }
 }

 // daughter class
 public class UFOController : Killable {
     public override int pointValue { get { return 42; } }
 }

 // daughter class
 public class OtherController : Killable {
     // pointValue defaults to 88
 }

Also, I would change your EventManager from using just a single Action to using an 'event' with Action listeners.

     // `event` keyword means multiple delegates can be added as listeners, not just one.
     public event Action<IKillable> OnSomethingKilledByPlayer;

     public void AddListener(Action<IKillable> handler) {
         OnSomethingKilledByPlayer += handler;
     }
     public void RemoveListener(Action<IKillable> handler) {
         OnSomethingKilledByPlayer -= handler;
     }

     // this function calls the event
     public void SomethingKilledByPlayer(IKillable killable) {
         if (OnSomethingKilledByPlayer != null) // if at least one listener
             OnSomethingKilledByPlayer(killable); // call event (let listeners handle the event)
     }

     // in your UIManager initialization
     EventManager.Instance.AddListener(ShowPointsAtScreenPosition);
Comment
Add comment · Show 4 · 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 ElijahShadbolt · Feb 04, 2018 at 04:15 AM 0
Share

Option 3: Type Casting

     // in your UI$$anonymous$$anager
     public void ShowPointsAtScreenPosition(I$$anonymous$$illable killable)
     {
         Text t = Instantiate(textRoa$$anonymous$$g);
         t.transform.SetParent(canvas.transform, false);

         UFOController ufo = killable as UFOController;
         if (ufo != null) {
             t.transform.position = Camera.main.WorldToScreenPoint(ufo.gameObject.transform.position);
             t.text = ufo.pointValue.ToString();
         }
         else {
             OtherController other = killable as OtherController;
             if (other != null) {
                 t.transform.position = Camera.main.WorldToScreenPoint(other.gameObject.transform.position);
                 t.text = "OtherController (may not have pointValue property)";
             }
             else {
                 $$anonymous$$onoBehaviour behaviour = killable as $$anonymous$$onoBehaviour;
                 if (behaviour != null) {
                     t.transform.position = Camera.main.WorldToScreenPoint(behaviour.gameObject.transform.position);
                     t.text = behaviour.name;
                 }
                 else {
                     t.transform.position = Vector3.zero;
                     t.text = "Cannot recognize $$anonymous$$illable type.";
                 }
             }
         }

         Destroy(t, 1.0f);
     }
avatar image Cirrocumulus · Feb 04, 2018 at 05:46 AM 1
Share

Thank you for clearing this up with example code. I obviously have a design issue here because my I$$anonymous$$illable objects already inherit from an Entity class which has a pointValue variable. I should shuffle stuff around and probably have two interfaces ins$$anonymous$$d of one, because some objects can be "killed" but not all of them are worth points. Something like "IScorePoints". But I get the idea. Thanks again! Like I wrote, I also thought about making the "ShowPointsAtWorldPosition" a component ins$$anonymous$$d, which I could then drag onto any prefab that needs it. But this has proved frustrating because of canvas referencing. I posted a question about it if anyone's interested. As to the Event$$anonymous$$anager, I don't understand. The way I did it does allow multiple subscribers. For example, the UI$$anonymous$$anager simply does this inside OnEnable:

 Event$$anonymous$$anager.Instance.OnUFO$$anonymous$$illedByPlayer += ShowPointsAtScreenPosition;

BTW, subscribing (either with your method or $$anonymous$$e) has swelled into another issue because I can't find a way of subscribing only after the Event$$anonymous$$anager has been instantiated (again, in case you're interested). Anyway, thanks a lot for your help. I dig interfaces a bit more each day but the road is long :)

avatar image ElijahShadbolt Cirrocumulus · Feb 04, 2018 at 08:14 PM 0
Share

Well I learned something new. Actions and delegates can be multidelegate (have multiple functions assigned to them), just like events. One major difference between delegates and events is that a normal delegate/Action/Func field can be assigned a new value, which replaces all the previous delegates, whereas an event cannot be assigned a new value and must be used with the += operator. It's just syntactical sugar.

 public event Action myEvent;
 myEvent += () => Console.WriteLine("my delegate");
 myEvent = () => Console.WriteLine("second delegate"); // this throws an error

See 1, 2.

avatar image Cirrocumulus ElijahShadbolt · Feb 04, 2018 at 08:30 PM 1
Share

Yes, I know. Since I learned that last week I've replaced all my delegates+events declaration with Action, all is one line and functionality is exactly the same. No idea about performance difference, though, not an issue for me right now (as you've probably figured out :)

avatar image
0

Answer by MacDx · Feb 03, 2018 at 11:06 PM

But I can't find any way of finding the object that the variable k came from.

That's the whole point of an interface, you use them so you don't have to worry about the actual object behind the interface, you should just care about what the interface provides (properties, methods, events and indexers). You could for instance include a method that returns a MonoBehaviour object and when you implement your interface on a script you just do return this; inside that method, however like I said, that would defeat the whole purpose of an interface since you would be tying it to a specific class and then bypassing it. Interfaces are supposed to be used with any object since they only add functionality.

Here's how I would solve it. The ShowPointsAtScreenPosition uses the ufo variable for only 2 things. Obtaining a Vector3 and an integer value.

 t.transform.position = Camera.main.WorldToScreenPoint(ufo.gameObject.transform.position);
 t.text = ufo.pointValue.ToString();

So why don't you include those in your interface like this:

 public interface IKillable {
     public int pointValue;
     public Vector3 objPosition;
     private void Kill();
 }

Then you can do this in your ShowPointsAtScreenPosition method:

 public void ShowPointsAtScreenPosition(IKillable k){
     Text t = Instantiate(textRoaming);
     t.transform.SetParent(canvas.transform, false);
     t.transform.position = Camera.main.WorldToScreenPoint(k.objPosition);
     t.text = k.pointValue.ToString();
     Destroy(t, 1.0f);
 }

You would just have to make sure to properly implement those properties in whatever class uses the IKillable interface.

Hope this helps, If you have any questions feel free to ask!

Comment
Add comment · Show 7 · 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 meat5000 ♦ · Feb 03, 2018 at 11:14 PM 0
Share

"you should just care about what the interface provides (properties, methods, events and indexers)"


This is what I dont get about interfaces. All I see them providing is placeholders that get repeated anyway. I havent hit upon the actual case in which it'll all make sense, I suppose. So I do other things ins$$anonymous$$d.

avatar image MacDx meat5000 ♦ · Feb 03, 2018 at 11:25 PM 0
Share

I havent hit upon the actual case in which it'll all make sense

Neither have I, but I trust someday I will. $$anonymous$$aybe I haven't worked on a project that's big enough to make interfaces worthwhile.

avatar image Glurth meat5000 ♦ · Feb 03, 2018 at 11:37 PM 0
Share

You get compiler errors if you don't fill in ALL the placeholders, which can be useful. OOP wise: I look at interfaces as a way to allow derived classes to "inherit" from more "types" than just their base class. Sure, the interfaces are completely and purely abstract, but being derived from them (and thus implicitly CASTable into them), can still be useful.
Edit/addition - Unity Wise: GetComponents only takes class types, but imagine how powerful getting any component "derived from"/ implementing a given Interface could be.

avatar image Cirrocumulus meat5000 ♦ · Feb 04, 2018 at 05:25 AM 0
Share

I was thinking the same thing. What's the point. Until I read you can do a GetComponent for the, say, I$$anonymous$$illable interface and it will return the first script on the gameobject that implements that interface. Later if you swap one I$$anonymous$$illable component for another everything still works.

avatar image Glurth Cirrocumulus · Feb 04, 2018 at 03:43 PM 0
Share

It's not built into unity, but here is my own not-so-efficient version:

 public static T GetInterface<T>(GameObject inObj) where T : class
         {
             if (!typeof(T).IsInterface)
             {
                 return null;
             }
             inObj.GetComponents<Component>(allComponents);
             foreach (Component component in allComponents)
             {
                 if (typeof(T).IsAssignableFrom(component.GetType()))
                 {
                     return component as T;
                 }
             }
             return null;
         }


Show more comments

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

77 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 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 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 avatar image avatar image avatar image avatar image avatar image

Related Questions

Interfaces vs methods selection using if(enumerations) 0 Answers

What is the general strategy for multiple monobehaviours implementing the same interface? 0 Answers

What's a good way to handle 'serialization' of interfaces in Unity ? 8 Answers

advice on resource management system architecture 2 Answers

Communication between interfaces and other components 0 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