Dynamically Adding Variable from a script

Hey guys, I’m having a problem trying to access an Enemy variable for the RPG we’re working on, basically what’s going on is that when the player chooses attack, the script looks for the EnemyName object provided by the enemy collider’s parent.

After that a string variable called EnemyBattleCode takes the enemy name, and adds B_ before it so that we have the enemy battlescript’s name.

Finally, it grabs the enemy’s script and assigns it to EnemyBattleScript. All of this works just fine, the problem I’m running in to is getting the Health Variable from that script, I’m not exactly sure how to code it as adding the statement “EnemyBattleScript.Health” won’t work since it’s not grabbing it until after the game’s started and a collision has taken place, any ideas?

I’m basically trying to make it as dynamic as possible so that any other enemies we have in the future will be able to work inside of this code.

void H_Attack()
{

    // This will find our Enemy GameObect that's found first inside of EnemyCollision
    EnemyName = GameObject.Find(EnemyCollision.EnemyName);

    //TODO Let the player chose WHO to attack.

    //Creates the scriptname to find
    EnemyBattleCode = "B_" + EnemyName;

    // Dynamically grabs the enemy's script
    EnemyBattleScript = EnemyName.GetComponent(EnemyBattleCode);

    //TODO Find the Health variable inside of the newly formed EnemyBattleScript
    

    //TODO Execute the attack.

    //TODO Find out how much damage the attack did and if it's <0 for the enemy they die.

    //TODO If enemy's damage is <0 the battle's over.

    print("Soon to be coded for attack");
    H_BattleTimer = 5;
    H_BattleMessageTimer = 5;
    H_BattleMessage = H_Name + " Attacked!";
    H_DisplayBattleMessage = true;
}

3 Answers

3

Use an interface to define what variables you can access from any other script!

In some common file (preferably inside a known namespace) define something like this to show what functions you need to implement on a script for it to qualify as 'enemyCode'-

public interface Enemy{
    float GetHealth();
    void DealDamage(float hitAmount);
    float GetDamage();
}

Then when you make your enemy script, put

public class EnemyTypeOne : MonoBehaviour, Enemy

before the rest of the class. Then you need to have public methods with the same interface as the ones described above (that is to say, any method called GetHealth() must not take any parameters, and must return a float).

Then in your Attack script, have a variable called

Enemy enemy;

Which you can assign any object which inherits from the Enemy interface to!

If you want it to work nicely with Unity's inspector GUI (which I suspect you do) there's a slightly hacky workaround to the fact that Unity does not serialize Interface members (ever). You can create a short MonoBehaviour component, which looks like this-

public class EnemyHolder : MonoBehaviour {
    public MonoBehaviour enemyScript;
    private Enemy actualEnemy;

    void Start()
    {
        // This is the dangerous, hacky bit- it will only work if you have
        // assigned a MonoBehaviour which also inherits from Enemy!
        actualEnemy = (Enemy)enemyScript;
    }

    public Enemy GetEnemy()
    {
        return actualEnemy();
    }
}

Then, to assign the Enemy enemy from earlier, use

enemy = enemyGameObject.GetComponent<EnemyHolder>().GetEnemy();

and from there you can use

float health = enemy.GetHealth();
enemy.DealDamage(9001);
float enemyDamageDealing = enemy.GetDamage();

and any other functions you have defined in your interface! When you want to create a new enemy type, just make a new script which implements the methods differently!

I've been trying to mess with this for the last day, haven't been able to get it to work yet but have been trying to understand what it does. Can you explain by chance what the interface override does by chance?

–

var EnemyBattleScript : EnemyBattleCode = EnemyName.GetComponent(EnemyBattleCode);

Then you can use EnemyBattleScript.Health. With dynamic typing, the compiler doesn’t know Health is a property of EnemyBattleScript, but if you use static typing it knows about the property.

If you want to create a generic infrastructure that is easy to extend later, you may want to rework your code a bit. The GameObject.Find is not the best method, however without more information about what you want to do it is difficult to give you a better solution.

C# does not allow you to inject a dynamic variable.

However you can create a mono behaviour which contains a dictionary of string and objects and add this behaviour as a component to any game object and use the dictionary there.

Here is an example; ScaleMemory is a dummy behavior which only contains two variables

var at = textComponent.gameObject.GetComponent<ScaleMemory> ();
				if (at == null) {
						at = textComponent.gameObject.AddComponent<ScaleMemory> ();
						at.OriginalLocaleScale = textComponent.rectTransform.localScale;
						at.OriginalRect = textComponent.rectTransform.rect;
				}
.. //later on i can reach scale and rect any time..
..

ScaleMemory as:

public class ScaleMemory : MonoBehaviour
{
		public Rect OriginalRect ;
		public Vector3 OriginalLocaleScale;
	
}