Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 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
0
Question by Mjeno · Nov 04, 2017 at 09:09 AM · scripting problemcomponentsinteraction

Call method from unknown class

Dear programming experts,

I've been coding in Unity for a year and have only just discovered the Component-based system's potential. However, I've run into trouble trying to use it fully.

The concept: I have a class called "InteractableObject.cs" which holds the basics for the player to interact with certain game objects. When added to a game object, the player can press E while inside its collider, and InteractableObject.cs will call its method "ActivateInteractable()". What exactly this method does, however, depends on which other Component(s) the game object holds. For instance, if the game object is a bed, it also contains a Bed.cs component. I want to set it up so that, when ActivateInteractable() in InteractableObject.cs is called, it calls ActivateInteractable() in the other class, e.g. in Bed.cs.

The problem: This is easy to achieve as long as InteractableObject.cs knows what the other script is. I would: 1. assign it in InteractableObject.cs by declaring it as a variable (e.g. "Bed bed;"), 2. call GetComponent() (e.g. GetComponent();), 3. then call bed.ActivateInteractable() from the ActivateInteractable() function inside InteractableObject.cs. Unfortunately, this isn't feasable because InteractableObject.cs doesn't know what the other script is. It could be Bed.cs, but also Chair.cs, Elevator.cs, NPC.cs, or some other class. In ActivateInteractable() inside InteractableObject.cs, I want to essentially call WhichEverClassTheInteractableIsInThisCase.ActivateInteractable().

That way, in my opinion, I'm using the Component system the way it is intended to be used: I'm adding a Component with a certain functionality (here: being activated by pressing E) to a game object which holds several components that make up its unique behavior. For these components to interact, however, they need to know what the other components are, which seems like a huge disadvantage to me. One I'm sure can be dealt with easily, but I don't know how - do you guys?

Thanks a lot in advance!

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

3 Replies

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

Answer by MaxGuernseyIII · Nov 04, 2017 at 09:52 AM

Something needs to do a similar kind of activity regardless of context but the context causes part of that activity to vary. You have a strategy pattern problem.

Generally, the way people solve this problem is to create an abstraction (e.g., an abstract base class or an interface) and make all of the variants implement that abstraction.

In your case, you need a way to publish the correct implementation of an abstraction. So I would make something like a container behavior.

 public interface InteractionTarget {
   void Interact();
 }
 
 public class InteractionContainer : MonoBehaviour {
   public InteractionTarget Target;
 }
 
 public class Bed : MonoBehaviour, InteractionTarget {
   public void Interact() {
     // whatever a bed does
   }
 }
 
 public class InteractionConsumer : MonoBehaviour {
   // I'm assuming this is called from some collision
   void InteractWith(GameObject go) {
     var interactionTarget = go.GetComponent<InteractionContainer>().Target;
     interactionTarget.Interact();
   }
 }
Comment
Add comment · Show 5 · 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 · Nov 04, 2017 at 10:27 AM 0
Share

Why the InteractionContainer ? You don't use this class anywhere. You use GetComponent on InteractionTarget which doesn't have a "Target" field.

avatar image MaxGuernseyIII Bunny83 · Nov 04, 2017 at 05:20 PM 0
Share

There was a typo.

avatar image Mjeno · Nov 04, 2017 at 03:30 PM 0
Share

Hey, thanks a lot for taking the time! I failed to mention that I originally worked with inheritance but wanted to move away from it. However, your solution seems a little different than when I used inheritance, and although it's intruiging, I can't say I completely understand. Especially because, like Bunny83 pointed out, I don't know what "Target" is. Would you $$anonymous$$d elaborating a little further? (:

avatar image MaxGuernseyIII Mjeno · Nov 04, 2017 at 05:35 PM 0
Share

You have to use some kind of inheritance or you're stuck with something like a switch statement. The important thing to remember is how to use inheritance: use it to categorize rather than to specialize. In other words, create base classes or interfaces that represent groups of interchangeable variants and make each inheritor an alternative to it's siblings rather than taking something that works and trying to make a new "flavor" of it with inheritance.

There was a typo in my example, which may have been focusing you. I've fixed it.

The point of this design is to have everything do one job. The container is the thing that you search for with something like GetComponent. The interface represents the commonality in the interchangeable group of things with which you can interact. Bed represents one of those interactable variants. The consumer represents the part of the rule that does not change.

Both my explanation and @Bunny83's OOP explanation hinge on our understanding what your problem really is. If, for instance, the thing that is driving interaction absolutely needs to travel differently to different kinds of interactable objects, that's probably a visitor pattern and will demand a different design.

avatar image Mjeno MaxGuernseyIII · Nov 13, 2017 at 06:26 PM 1
Share

Hey, thanks again for your effort and sorry for the long delay on my end. Based on the answers I got to my question, I tried out different things and ended up using an Interface the way you suggested. The different classes implementing ActivateInteractable() all inherit IInteractable() now and their instances are called from the outside using IInteractable.ActivateInteractable().

avatar image
2

Answer by Bunny83 · Nov 04, 2017 at 10:25 AM

There are several approaches to this. One is to use Unity's SendMessage system. It's the most flexible as you only need to know the method name. However it has some downsides. It's rather slow compared to a direct call. Also since it's based on a string you don't get a compiler error if you mistype it.

However in most cases since you only call the method on certain events the speed is quite irrelevant.


Another way is to use normal OOP. So you could use a base class with a virtual method which you can override in the derived classes. Instead of a base class you can also use interfaces. Personally i prefer interfaces over base classes. A base class is useful if you have a certain type of component which all share the same fields / functionality with only slight variations. The base class allows you to define the common things there so all components derived from the base class have those as well.


interfaces are more flexible than baseclasses. Unlike base classes a class can implement several interfaces. Imagine you have an interface like this:

 public interface IInteractableObject
 {
     void Activate();
 }

Now you can create several components which implement this interface:

 public class Bed : MonoBehaviour, IInteractableObject
 {
     public void Activate()
     {
         Debug.Log("Activate Bed");
     }
 }
 
 // or
 
 public class Chair : MonoBehaviour, IInteractableObject
 {
     public void Activate()
     {
         Debug.Log("Activate Chair");
     }
 }


Now to actually use the interface you can simply do;

 someGameObject.GetComponent<IInteractableObject>().Activate();

This will work for any component that implements this interface. Unfortunately Unity can't serialize interface references in the inspector. This would work with base classes. So if you have a base class like this:

 public abstract class InteractableObject : MonoBehaviour
 {
     public abstract void Activate();
 }
 
 public class Bed : InteractableObject
 {
     public override void Activate()
     {
         Debug.Log("Activate Bed");
     }
 }

In another class you can now define a public variable of type "InteractableObject" where you can assign any class that is derived from InteractableObject:

 public InteractableObject someObj;
 // ...
 someObj.Activate();


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 Mjeno · Nov 04, 2017 at 03:42 PM 0
Share

Awesome! Thank you very much for taking the time. I'm actually inclined to use the Send$$anonymous$$essage system since the method is never called in Update(), but only every few seconds or, more likely, $$anonymous$$utes when the player interacts with an object. Thus, I think, its high cost isn't an important factor.

I did use inheritance before but felt like it wasn't exactly what I wanted. The system has been working fine using inheritance for a couple of months now, but the ability to just add and remove features (like "interactable") to and from objects sounds like exactly what I'd like to be able to do in the future. However, I like your idea with Interfaces a lot and I'm going to give it a spin as well to see which system works best. Thanks again!

avatar image
0

Answer by Daemonhahn · Nov 06, 2017 at 04:18 PM

You could make an interface, or use reflection.

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

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

127 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 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

HoloLens: detect Airtap regardless of Raycast hit 0 Answers

How to achieve cross platform deterministic physics(Android , IOS) ? 1 Answer

Is there a thing like a type of a script? 3 Answers

I cannot get script component. 3 Answers

Can't disable component, little checkbox is missing from inspector 1 Answer


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