0
\$\begingroup\$

I have some custom script for enemy's AI. Let's call it: "firstEnemyAI.cs"

And I have "master" script called "Enemy.cs" attached to the GameObject.

Is there any way to attach "firstEnemyAI.cs" script to the "Enemy.cs" script for calling some "firstEnemyAI.cs" methods, without need to hardcoded class name of "firstEnemyAI.cs" script?

If I access it in "Enemy.cs" via

GetComponent<FirstEnemyAI>();

I will need to edit my "Enemy.cs" script every time, I want to try another AI pattern :(

\$\endgroup\$
  • 1
    \$\begingroup\$ I'm not too familiar with Unity, but would inheritance help? \$\endgroup\$ – Vaillancourt Dec 24 '19 at 23:48
  • 2
    \$\begingroup\$ This sounds like it could also be a candidate for Interfaces or Events. Can you give a more concrete example of the kinds of behavior you want to specialize this way, or the kinds of changes you want to be able to make to the underlying implementation without altering the client code? \$\endgroup\$ – DMGregory Dec 25 '19 at 1:07
2
\$\begingroup\$

The most convenient way: ScriptableObjects.

ScriptableObjects are Unity scripts, but unlike MonoBehaviours, they do not exist in the scene, they exist as .asset file and can be referenced from anywhere.

First, you should create AI template class. It's abstract, so it cannot be instantiated itself, only its inheritors. And it inherits from SO for extended functionality:

public abstract class AiTemplate : ScriptableObject
{
    // Method you want to call. Inheritors will implement different versions of it.
    public abstract void AiMethod(GameObject enemy);
}

Then you should create children of your template:

[CreateAssetMenu(menuName = "AI/First")]
public class FirstEnemyAI : AiTemplate
{
    public override void AiMethod(GameObject enemy)
    {
        // Do something with enemy.
    }
}

[CreateAssetMenu(menuName = "AI/Second")]
public class SecondEnemyAI : AiTemplate
{
    public override void AiMethod(GameObject enemy)
    {
        // Do something else with enemy.
    }
}

CreateAssetMenu attribute allows you to create your SO inside the editor (Toolbar/Create/AI/...).

After that you should modify your Enemy script:

class Enemy
{
    // Creating AI template variable which you can assign via inspector.
    [SerializeField] AiTemplate template;

    // ...
        // Calling method from current AI template.
        template.AiMethod(this.gameobject);
    // ...
}

Now you can write as many AiTemplates as you like and assign any of them to your enemy via inspector without modifying Enemy script ever again!

Important notice! ScriptableObjects used this way are managers, not data holders. They should not contain any enemy-related data! Simple example: first enemy calls your script and saves its HP inside it. After that second enemy calls the same manager and overwrites stored HP. When first enemy calls manager again, it receives HP of second enemy, which leads to incorrect behaviour.

Your managers should always get values, modify them and return back, so multiple enemies could work with the same manager independently.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thank you a lot. That solution works fine for me. \$\endgroup\$ – Slowbodynuke Dec 25 '19 at 22:14
0
\$\begingroup\$

According to the Unity Documentation (Add Component) you can add components to any gameobject at runtime.

You can do something like this:

FirstEnemyAI FEScript = this.gameObject.AddComponent<FirstEnemyAI>();
FEScript.YourCallFunction();

The variable will hold the attached script so you can reference it anywhere in that session.

This Forum link will provide you a heads up!

| improve this answer | |
\$\endgroup\$

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.