So I’m a newbie and I’ve been following the 2D Beginner: Adventure Game tutorial on Unity Learn and I’m at this part of the tutorial:
Basically its using 2D Raycast to show a dialog when interacting with an NPC. My question is about how the FindFriend() method is implemented in this tutorial. It’s just using the raycast collision detection to check if the raycast is hitting the NPC collider or not:
void FindFriend()
{
RaycastHit2D hit = Physics2D.Raycast(rigidbody2d.position + Vector2.up * 0.2f, moveDirection, 1.5f, LayerMask.GetMask("NPC"));
if (hit.collider != null)
{
NonPlayerCharacter character = hit.collider.GetComponent<NonPlayerCharacter>();
if (character != null)
{
UIHandler.instance.DisplayDialogue();
}
}
}
I’m just looking at this part and wondering if there’s a better, shorter way to write this? Like is there no other function for the hit.collider to be able to just determine what type of game object it hit?
It just felt weird having to create a NonPlayerCharacter script that does nothing just for this. And having that nested if statement like that doesnt seem nice at all. Cheers in advance.
The hit.collider != null is to determine whether you actually hit something. If you tried to access any properties of the collider if you haven’t hit anything would end up throwing a null-reference exception.
Then it uses the GetComponent<NonPlayerCharacter>() to test whether you’ve hit the right game object. Somewhat impressed they’re doing this over using the tired old layer system.
Game object themselves are just containers for components, so any meaningful information is expressed or contained its components. Using a component like this is a good way to test whether a component expresses something.
Something like layers would box you into only one option, whereas a game object can have any number of components. If any extra functionality needs to be added in, you already have a class defined with which to do so.
Really, the code is fine. The only change I would make is one of my own preferences, which is to use guarded-clauses, and TryGetComponent<T>:
void FindFriend()
{
RaycastHit2D hit = Physics2D.Raycast(rigidbody2d.position + Vector2.up * 0.2f, moveDirection, 1.5f, LayerMask.GetMask("NPC"));
var collider = hit.collider;
if (collider == null)
{
return;
}
if (collider.TryGetComponent(out _) == true) // discard pattern
{
UIHandler.instance.DisplayDialogue();
}
}
But this is just the same code written differently.
I wouldn’t be worrying about stuff like this just yet, particularly when you’re at the early learning stages of coding. Learning to get things working in the first place is more important.
While I was doing the tutorials, I saw how the OnCollider events were being used so I think in my mind, I was thinking surely there would be something like that I could use in this scenario. Cheers anyways.
The physics callbacks are best for reactive behaviour.
Wanting to talk to an NPC is something more ‘on demand’, so in these situations you would instead write code to test whether certain criteria are met, and branching your logic based on that.
if (hit)
{
NonPlayerCharacter character = hit.collider.GetComponent<NonPlayerCharacter>();
if (character != null)
{
UIHandler.instance.DisplayDialogue();
}
}
… because RaycastHit2D has an implicit bool operator to check “collider != null” for you as per the documentation here: Unity - Scripting API: RaycastHit2D.bool
Also, try not to put hardcoded strings in your scripts like this:
LayerMask.GetMask("NPC")
A better way is to add a public field of type “LayerMask” into your script above and then simply refer to that field in your queries or however it’s being used. That way, you can select the layer(s) you’re interested in right in the inspector; it’s quicker (no processing needed) and not sensitive to layer name changes should you ever do them.