- Home /
Using interfaces in C#
In my project there is a robot. It has two important components:
An AI that chooses an enemy object and moves towards it.
A weapon that automatically shoots at the chosen enemy object.
These two components are independent and only need one piece of communication. After the movement AI has chosen a target it needs to inform the weapon system of its decision. Unfortunately I can't just call GetComponent()
since there are difference weapon systems. I need an abstraction layer. Its the typical case for an interface that contains jsut that one method setTarget
. I've added the interface to my project and implemented it in my weapon script. Now I thought it would be possible to access it with GetComponent()
. But a component must be a MonoBehaviour, so my interface doesn't work.
How do I abstract over the different weapon systems? I want to have one movement AI work with all possible weapons.
Answer by MaGuSware™ · Dec 24, 2012 at 01:02 PM
you have said the key word yourself, "abstract"
abstract public class AbsClass : InheritedObject {
//functions here
abstract returntype FunctionName();
}
public class Class : AbsClass {
override returntype FunctionName()
{
//do stuff
}
}
"C# doesn't support multiple inheritance" But support chain, Inhere AbsClass from $$anonymous$$onoBehaviour and You class from AbsClass.
Hm, that sounds great. +1 for this comment. I'll test this idea when I'm back home.
The answer below is much more object oriented, which should make C# practitioners happier.
"$$anonymous$$uch more" isn't really appropriate here. Yes, interfaces are more flexible, however abstract baseclasses are as appropriate as interfaces in this case. Interfaces aren't "more OO" than abstract base classes as it depends on the usecase. Since both answers are valid answers and it's the OP's decision which one he accepts so we won't change anything here. Just because an answer has more upvotes doesn't mean it "has to" be the accepted one. We (the moderators) only revoke the accepted state if the accepted answer contains wrong information or doesn't answer the question which isn't the case here.
Feel free to upvote the answer that you like best. $$anonymous$$eep in $$anonymous$$d that downvoting should only be used for wrong / misleading information and that it costs you some karma. Read the FAQs especially the section "be honest". Don't forget this is not a competition. Unityanswers is just a collection of information. What's useful and what's not is indicated by the votes an answer receives. The accepted answer is a personal thing that belongs to the OP. The OP asked the question and the accepted answer is a way to reward one of the answers that he personally picked as the answer that answered his question.
Answer by mouurusai · Dec 24, 2012 at 12:57 PM
Key point is: ITest test = (ITest)GetComponent(typeof(ITest));
public class Test:MonoBehaviour
{
void Start()
{
ITest test = (ITest)GetComponent(typeof(ITest));
if (test != null)
test.Test();
}
}
//Car.cs
public class Car : MonoBehaviour, ITest
{
public void Test()
{
Debug.Log("car");
}
}
//Robot.cs
public class Robot : MonoBehaviour, ITest
{
public void Test()
{
Debug.Log("robot");
}
}
//ITest.cs
public interface ITest
{
void Test();
}
P.S. In Unity 3.x interface must be in separate file.
I will try that as soon as possible. But I doubt that it will work. It's basically the same setup that I tried: Calling GetComponent with the type of the interface. $$anonymous$$y result was an error message that GetComponent only works with Component types. I guess that means that only classes that extend $$anonymous$$onobehaviour can be used as components. Nevertheless, I'll try it.
This works, too. I'll upvote it, but sadly I can only accept one answer. Since I tested the other answer first, I already accepted it. Thank you for your efforts
Answer by Dave-Carlile · Dec 24, 2012 at 12:58 PM
In your component script, add a reference to the weapon system interface...
public IWeaponSystem WeaponSystem;
Call GetComponent to get the script component, then use the WeaponSystem property to access the weapon system.
Sorry but I don't understand this. You mean that I just add the two scripts to my GameObject and assign the weapon script to a variable of my movement script ? That would probably work, but it doesn't answer my question. So far that was the content of the first two lines of your response, I got it. But in the final line you advise me to use "GetComponent to get the script component". That's not possible. I said so in my question. $$anonymous$$oreover it doesn't make sense in combination with the first part of your answer. It's either assignment in the editor or GetComponent in the script, not both
You were stating that you couldn't call GetComponent because it couldn't deal with the interface. Just move the interface reference to a property on some script component, assign the reference however you need to, then when you need to get access to the interface, call GetComponent() and you'll have a reference to the interface on that script.
Thank you for your efforts, but I really don't understand this. You want me to add a public variable of type $$anonymous$$yInterface in my movement-ai class and assign the weapon-ai component to this variable in the unity editor, right ? That should work fine, I'll test it when I'm back at my desk. But why do I need GetComponent after that ? You said "then when you need to get access to the interface, call GetComponent() and you'll have a reference to the interface on that script." That's just not possible. GetComponent won't work with the interface. $$anonymous$$oreover the call to GetComponent wouldn't be necessary after I got the interface assigned to my public variable ? Could you show me some code, please ? How do you intend to call GetComponent() ?
Answer by jankymidget · Jul 22, 2017 at 09:50 PM
Interface for your robot to the weapon system and some proxy function to shoot.
public interface IRobot {
IWeaponSystem WeaponSystem { get; set; }
void ShootTarget();
}
Interface for your weapon system and some undefined way to settarget and shoot.
public interface IWeaponSystem {
void SetTarget();
void Shoot();
}
Base class for the Robot that extends MonoBehaviour, and uses the IRobot interface.
public class Robot : MonoBehaviour, IRobot {
private IWeaponSystem weaponSystem;
/// <summary>
/// Can be set to some initial weapon system in UI
/// or set programmatically from code.
/// </summary>
public IWeaponSystem WeaponSystem {
// assumes some sort of IWeaponSystem is attached
get { return weaponSystem; }
set { weaponSystem = value; }
}
void Update() {
WeaponSystem.SetTarget(); // if there is one
ShootTarget(); // same as above, but the weapon system still handles it
}
public void ShootTarget() {
WeaponSystem.Shoot();
}
}
Base astract class for WeaponSystem. Can't be instantiated i.e. abstract, extend this for all weapon systems.
public abstract class WeaponSystem : MonoBehaviour, IWeaponSystem {
void IWeaponSystem.SetTarget() {
/*do set target ai logic at this level*/
}
void IWeaponSystem.Shoot() { /*do nothing abstract base*/ }
}
Subclass for some turret weapon system.
public class TurretWeaponSystem : WeaponSystem {
// re-define shoot here
public void Shoot() {
// do TurretWeaponSystem specific Shoot() behavior
}
}
Most of your logic will take place elsewhere, but here is a rough example of the indirect interactions.
void exampleUsage() {
Robot myRobot = new Robot();
WeaponSystem genericWeaponSystem = new TurretWeaponSystem();
// noting that myRobot.WeaponSystem is of IWeaponSystem type
myRobot.WeaponSystem = genericWeaponSystem;
myRobot.ShootTarget();
}
This isn't exactly how I would do it, but based on what I read, I tried to keep the interfaces in there. With interfaces, if performance is a concern, you'll want to avoid boxing and unboxing of the real types the interfaces point to as often as possible. Once it gets pass and set to to a variable, it should be fine, but constant marshaling will amount to some level of performance hit across the whole system if you use the pattern a lot.
Your answer
Follow this Question
Related Questions
Restrict access to components on same object 1 Answer
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Why doesn't Unity support default interface methods? 1 Answer