There are multiple ways of handling the conditions when selecting/targeting for the Interaction.
A Layer mask is one that is kinda easy and can avoid a lot of painful problem along the line. Especially when you start adding World-based UI or various kinds of interactions.
I also like to define my generic list of potential targets via layer mask such as having 2 layer masks: 1 called “Actors” and another called “Interactif”. (This allows me to know what kind of script I get to call from because Interactive assets like lever or chest or door might have a different behavior than enemies.)
But the one issue that will come with the Layers is that you’re limited to a total of 27 custom Layers. You might think that it’s a lot of layers, it depends on what you’re using the layers for and as. As an example, in my project, I’m using 4 layers for the environment alone:
Path and Physics: For anything that are never rendered like colliders that are to be included in the Path generated by the NavMeshSurface.
Render Only: For anything that is only rendered, but never included in the physics engine.
Path and Render: When the asset is both rendered and calculated in the physics.
Obstacles and Render: When the asset is rendered and taken into account in the path data, but not in the interaction level. For example, fences are included in this because when the player click on a fence to move, usually the player wants to click the ground on the other side of the fence. Same with something like a stack of barrels. If the player click at the top of the stack of barrel (and they can’t be destroyed), the player obviously want to move behind the stack and not “on top” of the stack (unless it’s possible to get on top). So that layer allows me to easily skip, on click, any colliders like that.
An example of the Render Only and Path and Physics that I uses is with the buildings. The building objects with the Renderer are on the Render Only layer while I placed the colliders on an empty child object. The reason why I did this is because it allows me to manage the building collision separately from the renderer itself. It’s extremely useful when you want a good transition between interior and exterior. In my game, when a player enter a building, I can safely disable the roof and I know that the physics related to that roof is still active so, for example, I can keep a rain effect going outside and it wouldn’t suddenly go inside because the roof would be disabled.
And then, I’m also using the Tags to differentiate on a 2nd level.
Unlike the Layers, you’re not limited to 27 custom Tags as it’s an array of strings and you can dynamically change anything’s tag without affecting physics nor rendering. This is why I deal with the “type” of actors in tags instead of having a layer for friendlies, enemies, etc.
The only issue with using Tags is that it can easily be a memory sink if you’re not setting it up properly.
Personally, I’m using a readonly reference script to manage such thing:
Something like this script:
using UnityEngine;
public class References
{
public static readonly string Tag_Player = "Players";
public static readonly string Tag_NPC_Hostile = "NPC-Hostile";
public static readonly string Tag_NPC_Friendly = "NPC-Friendly";
public static readonly string Tag_Interactive = "Interactive";
public static readonly string Tag_Terrain = "Terrain";
public static readonly string string_Null = "null";
public static readonly int Hash_Animation_ForwardMovement = Animator.StringToHash("Move Forward");
public static readonly int Hash_Animation_LeftMovement = Animator.StringToHash("Move Left");
public static readonly int Hash_Animation_MoveSpeedMultiplier = Animator.StringToHash("Movement Speed");
}
Then all I have to do is compare the Tags with whichever Tag reference I’m looking for or avoiding.
In this course example, you could do something like:
[SerializeField]
private float MaxDistanceFromCamera = 200f
[SerializeField]
private LayerMask TargetingMask;
private bool InteractWithCombat()
{
RaycastHit[] hits = Physics.RaycastAll(GetMouseRay(), MaxDistanceFromCamera , TargetingMask;);
foreach(RaycastHit hit in hits){
CombatTarget target = hit.transform.GetComponent<CombatTarget>();
if(target == null || hit.transform.tag != References.Tag_NPC_Hostile){
continue;
}
if(Input.GetMouseButtonDown(0)){
GetComponent<Fighter>().Attack(target);
}
return true
}
return false;
}
Obviously, that example could be worked better with better conditioning (such as if the player want to use something that can target a friendly NPC like a summon or a player, like a healing skill/spell/item), but that gives ample controls and the cool thing about this is that changing the name/string of the Tag only requires an update in the References script list of strings so no need to revert back in any scripts who might be looking at it by reference.
I added a distance value for the Raycast because it’s always better to put one to limit the out-of-scope results. If the camera doesn’t render more than 200 units, it’s useless to tell the Raycast to cast further than that.