Sorry, I know this is a dumb question but my brain needs some strong methods to help it gets through. I found a video on youtube about Interface => HERE the situation and codes were showed here. And My question:
private void Update()
{
var nearestGameObject = GetNearestGameObject();
if (nearestGameObject == null) return;
if (Input.GetButtonDown("Fire1"))
{
var interactable = nearestGameObject.GetComponent<IInteractable>();
interactable?.Interact();
}
}
In Update function => this line of code => var interactable = nearestGameObject.GetComponent<IInteractable>();
The GetComponent<IInteractable>()
, what actually does it get ? get the interface ???? or get the Game Object, I have never seen an interface attached to the Gobj in the inspector before.
I see there is still some confusion for what exactly this is good for.
So I decided to extend on this answer and my comments a bit with a practical example.
Let's say you have the given interface
e.g.
public interface IInteractable
{
void Interact();
}
now you can have as many different implementations as you wish. An interface
is just a kind of template for a type and tells the implementing type that there a certain members it needs to provide. And it tells the user of such an interface (like in your case the code you shared) that whatever actual type is returned - we can be sure there are certain members implemented by it we can use.
For example there could be one that simply destroys itself once you interact with it
public class DestroyInteractable : MonoBehaviour, IInteractable
{
public void Interact()
{
Destroy(gameObject);
}
}
Or why not one that moves e.g. for triggering a platform
public class MoveInteractable : Monobehaviour, IInteractable
{
[SerializeField] Rigidbody _rigidbody;
[SerializeField] float moveDuration = 1f;
[SerializeField] Vector3 targetPosition;
public void Interact()
{
StartCoroutine(Move());
}
IEnumerator Move()
{
yield return new WaitForFixedUpdate();
var startPosoition = _rigidbody.position;
for(var timePassed = 0f; timePassed < moveDuration; timePassed += Time.deltaTime)
{
_rigidbody.MovePosition(Vector3.Lerp(startPosition, targetPosition, timePassed / moveDuration));
yield return new WaitForFixedUpdate();
}
}
}
ETC ... You can do what ever you want to trigger in your interactable objects.
And now we come to your shared code.
Allow me a small but important excurs at this point.
There is one huge flaw: NEVER use ?.
operator on UnityEngine.Object
! (see Why does C# null-conditional operator not work with Unity serializable variables?)
In general it should rather be
if(nearestGameObject.TryGetComponent<IInteractable>(out var interactable))
{
interactable.Interact();
}
Now what does this do?
It searches for any component type on the given GameObject that implements the IInteractable
interface. If it finds one it returns a reference to that found (which means already existent) component as IInteractable
. If none is present it returns null
.
Your code doesn't care at all what the exact actual class type is, since it doesn't need to know. For itself to work all it needs to know is that there is a method called Interact()
it can call. Whatever happens after this call is not our business and doesn't matter to us at this point.
Now you see why this is so powerful: With a single controller on your player object you can now interact with all the different types of IInteractble
objects, each can define a different reaction to your interaction - you don't have to care at all.
I hope that spreads a bit more light into not only what it does but why it is useful.