0
\$\begingroup\$

I'm making a playable one level only FPS death match game in Unity. I'm in the part where the AIs now can find and shoot their own target and keep looping to find targets until nothing left. Every AIs and including player were tagged with tag "Target" for the AIs to randomly find their target. (I already excluded self finding).

Now I want to split them in 2 teams. And here is the thing that I keep wondering. Should I use 2 tags for 2 teams (like team red, team blue for ex) then make and attach 2 scripts separately for each AI in the team (the logic in the script will be like "team red" will find game object with tag "team blue" and vice versa). Is that how it works? Is there more efficient way or should I say, "smarter" way to work around this problem?

Here is the script if you need to know more about what I am trying to say https://github.com/Bezari0us/FPS_AI_Behaviour

\$\endgroup\$
2
\$\begingroup\$

Use tags (but one script)

Well, your tag might not be using the Unity tag system. But you absolutely do not want to use different scripts! You'd be copying the entire class and changing one line. As soon as you find yourself duplicating code in order to make a simple behavioral differentiation, you should realize that your design is broken.

For example, if you wanted to move from 2 teams to 3, how much code would you have to change? If the answer is "all the work you did to make 2 teams all over again" then you did everything wrong. All you should have to do is add a single entry in a single place (e.g. adding the new team name to EnumTeams or similar) and everything else handles it automatically. You might never reach that ideal, but in thinking about it you can identify "oh, if I do this, I'll have to do a lot of work again in the future if I wanted to add a third team, is there a better way I can do it instead?"

What you should do instead is to make that "one line change" some sort of variable factor. Either the script has a public field that says "I am on blue team" (and then your random targeting logic looks at that field, and if the target's team is the same as self's team, ignore that target and find a new one) or you adjust the architecture to introduce a new class that defines the team and how targeting logic should handle teams.

What I mean by a new class here is some sort of "team" concept that allows your AI to identify what team it's on, and what teams its team is allied with (if you had 8 teams and wanted to have alliances, now you have teams of teams, how much rework would you have to do again!?). You might even want to have the "find me a target" code here, so that you can choose randomly from a list of only valid targets (eg. writing a function that automatically finds all living players that are NOT on the current team or its allied teams, and selecting a random one from that list, avoiding having to write a loop that keeps randomly selecting until a valid object is found).

\$\endgroup\$
  • \$\begingroup\$ I have already done that cumbersome way to make 2D ais and hardcode every tags for each of them. That's why I'm asking here for a more efficient way to work around this problem. I think I will go with the idea of saying "I am on which team" first for testing purpose. \$\endgroup\$ – Bezari0us Jul 12 '19 at 15:35
0
\$\begingroup\$

I totally agree on Draco18s answer.

you absolutely do not want to use different scripts!

I could add my opionion on WHAT you might use to differentiate teams.

enums
Probably the easier and safer if you want something quick.

public enum TeamType { Blue, Red }

public class TeamScript : MonoBehaviour
{ 
      public TeamType team;
      public void AssignTeam(TeamType teamToAssign) => team = teamToAssign;
}

string tags
That might be used together with unity tags.

public class TeamScript : MonoBehaviour
{ 
      public string team;
      public void AssignTeam(string teamToAssign) => team = teamToAssign;
}

Scriptable Objects
My favorite. Probably the most advanced.

//this might be also an empty script
[CreateAssetMenu(menuName = "TeamIdentifier")]
public class TeamIdentifier : ScriptableObject
{          
}

public class TeamScript : MonoBehaviour
{ 
      public TeamIdentifier team;
      public void AssignTeam(TeamIdentifier teamToAssign) => team = teamToAssign;
}

The Scriptable Object needs also to be instantiated in the Project Window by:
Right Click => Create => TeamIdentifier


All of these options might be assigned in the Inspector Window.
And you may also easily assign them from the script (with the AssignTeam method) or checking them (from the field TeamScript.team).

\$\endgroup\$
  • \$\begingroup\$ I will give them a try and comment back if it works. Thank you. \$\endgroup\$ – Bezari0us Jul 12 '19 at 15:48
  • \$\begingroup\$ I'd recommend using physics layers to distinguish the teams. That lets you efficiently filter collisions / triggers / objects in a zone based on team identity, without needing to check & discard false positives. \$\endgroup\$ – DMGregory Jul 12 '19 at 16:06
  • \$\begingroup\$ @DMGregory, I agree on having a proper physic layer, but I wouldn't use the same layer to check the team type via code. I would just use the physics layer for physics and another identifier for the script logic. In my specific case I would set the Physics Layer as a field in the SO (scriptable object), and use the same SO as identifier. \$\endgroup\$ – Jack Mariani Jul 12 '19 at 16:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.