How to assign a dynamic variable to instantiated prefab clones?

I have a projectile script (ScriptProjectile) that determines the position of each instantiated clone.

rigidbody.position = Vector3.Lerp(transform.position, target.transform.position, ProjSpeed * Time.deltaTime);

the target.transform.position bit is pulling from another script that allows the player to select the nearest object tagged “Enemy”. This targeting script works on its own, but I want each projectile clone to have its public GameObject target variable set to the currently selected enemy so I can use it in the Vector3.Lerp and have it travel towards the enemy. This is from the targeting script:

public void SelectTarget()
{
   selectedTarget.renderer.material.color = Color.yellow;

   projectile.target = selectedTarget.gameObject;
}

The projectile.target is referring to the GameObject target in my projectile script. My problem is that each projectile clone lacks the GameObject target information from the targeting script. Here’s the exact error: “UnassignedReferenceException: The variable target of ScriptProjectile has not been assigned. You probably need to assign the target variable of the ScriptProjectile script in the inspector.”

How can I pass that variable to each instantiated projectile?

Thanks for any help! Let me know if more information is needed.

MORE INFO Referring to the scripts I posted in comments below. ScriptProjectile is assigned to the projectile prefab. TargetEnemy script is assigned to the Player. PlayerRanged, the script that instantialises the projectile prefabs, is also assigned to the player.

The problem seems to be you tries to access an instance of projectile without correctly referring to it. Give more code samples, especially concerning projectile.target assignement in your targeting script and your target variable from ScriptProjectile. It should be enough.

When you get this error, can you tell me what is the selectedTarget color ?

Yeah, the colour is yellow.

In fact it's a bit hard to debug this for you since your 3 scripts seems to refer to other ones. You should try to [use the debugger][1] to localize your problem and then come back to post what exactly doesn't work. Sorry if i can't help you anymore. Here is [a very good tutorial][2] on the subject. [1]: http://docs.unity3d.com/Manual/MonoDevelop.html [2]: https://www.youtube.com/watch?v=-D6qXaGSC2k

Thanks all the same Thom! Perhaps I haven't approached this feature the right way I'm not sure... I will check out those links. Cheers!

2 Answers

2

If I understand it correctly, this is how you would get the script from the clone (instantiatedPrefab) you created and set the target:

// Get the script on the cloned prefab
var scriptProjectile = (ScriptProjectile)instantiatedPrefab.GetComponent<ScriptProjectile>();

// Now you can set the target in the script
scriptProjectile.target = selectedTarget.gameObject;

Thanks for the response! Sorry if I'm a bit slow (I'm not really a coder), but I'm still a little confused. And maybe this is because I actually have 3 scripts involved here. I posted them in comments above. 1 is for targeting behaviour, 1 for projectile behaviour and the other actually instantiates the projectile object. Would that change your answer at all? I'm thinking I would reference the script that instantiates the object in your first line of code? I'll try some things out in the meantime. Thanks! Again, sorry if I'm slow.

What have you changed ?

  • Line 1 : In order to use Lists
  • Line 6 : List field
  • Line 38 : add Projectile Instance to you list

Note

You should somewhere delete destroyed instances using this in your ScriptProjectile script :

void OnDestroy () {
    YourInstanciatorScript.projectileScripts.Remove(this);
}

YourInstanciatorScript.cs

using System.Collections.Generic;

public class YourInstanciatorScript : MonoBehaviour {

	//List to save newly instantiated Projectiles' scripts
	public List<ScriptProjectile> projectileScripts = new List<ScriptProjectile>();

	//...

	//Stop and wait before resuming function.   
	IEnumerator WaitAndFire (float waitTime) {
		//Wait for as much time as the float waitTime inputted.
		yield return new WaitForSeconds(waitTime);
		//Create a new vector 3 and zero it out.
		Vector3 spawnPos = Vector3.zero;
		
		//Create spawn position if it doesn't exist
		if (!SpawnPosition) {
			spawnPos = gameObject.transform.position + gameObject.transform.forward + Vector3.up * 0.45f;
		} else {
			//If spawn position already exists
			spawnPos = SpawnPosition.position;
		}
		
		//Create a projectile object if one hasn't been set
		if (!ProjectileObject) {
			LastFiredProjectile = CreateProjectile();
			LastFiredProjectile.transform.rotation = gameObject.transform.rotation;
			LastFiredProjectile.transform.position = spawnPos;
		} else {
			//Instantiate created object on our spawn position and rotate it propertly
			LastFiredProjectile = GameObject.Instantiate(ProjectileObject, spawnPos, gameObject.transform.rotation) as GameObject;
		}
		//Initialize newly spawned Projectile
		LastFiredProjectile.GetComponent<ScriptProjectile>().Initialize();

		//Add the newly instantiated Projectile' script to the List<ScriptProjectile>
		projectileScripts.Add(LastFiredProjectile.GetComponent<ScriptProjectile>());
	}
}

What have you changed ?

  • Line 1 : In order to use Lists
  • Line 19 : Delete this because we wants instance values, not prefab’s
  • Lines 60 → 65 : Retrieve instances and set their target to selectedTarget.gameObject

TargetEnemy.cs

using System.Collections.Generic;

public class TargetEnemy : MonoBehaviour {
	
	public List<Transform> targets;
	public Transform selectedTarget;
	private Transform myTransform;
	private ScriptProjectile projectile;
	
	// Use this for initialization
	void Start () {
		targets = new List<Transform>();
		selectedTarget = null;
		myTransform = transform;
		
		AddAllEnemies();

		//suppress this
		//--> projectile = GetComponent<ScriptProjectile>();
	}
	
	public void AddAllEnemies () {
		GameObject[] go = GameObject.FindGameObjectsWithTag("Enemy");
		
		foreach (GameObject enemy in go)
			AddTarget(enemy.transform);
	}
	
	public void AddTarget (Transform enemy) {
		targets.Add(enemy);
	}
	
	public void SortTargetsByDistance () {
		targets.Sort(delegate(Transform t1, Transform t2) { 
			return Vector3.Distance(t1.position, myTransform.position).CompareTo(Vector3.Distance(t2.position, myTransform.position));
		});
	}
	
	public void TargetFoe () {
		if (selectedTarget == null) {
			SortTargetsByDistance();
			selectedTarget = targets[0];
		} else {
			int index = targets.IndexOf(selectedTarget);
			
			if (index < targets.Count - 1) {
				index++;
			} else {
				index = 0;
			}
			DeselectTarget();
			selectedTarget = targets[index];
		}
		SelectTarget();
	}
	
	public void SelectTarget () {
		selectedTarget.renderer.material.color = Color.yellow;

		//retrieve instancies
		List<ScriptProjectile> instanceListe = YourInstanciatorScript.projectileScripts;
		//set target
		foreach (ScriptProjectile instance in instanceListe) {
			instance.target = selectedTarget.gameObject;
		}
	}
	
	public void DeselectTarget () {
		selectedTarget.renderer.material.color = Color.blue;
		selectedTarget = null;
	}
	
	// Update is called once per frame
	void Update () {
		if (Input.GetButtonDown("Sprint")) {
			TargetFoe();
		}
	}
}

Previously said :

I’ve found one possible mean to produce your error : if GameObject.FindGameObjectsWithTag("Enemy") returns a null array your error happens surely, so do you have “Enemy” tagged GameObjects in your scene ?

I do have 2 GameObjects in the scene tagged "Enemy". That's how I saw that one turned yellow when I targeted it (targeting and shooting are assigned to the same button, but I don't think that should screw things up). The problem is that the "target" variable for each newly instantiated projectile is not being set upon instantiation so they don't know where to go and I get the error. If I drag a projectile into the scene, that projectile DOES get the target info. It's just the projectile objects that are instantiated during play that can't get the target.

I just send a long answer for your problem that should work but this forum suppress it saying that I'm not allowed to publish more than one answer for each topic... So I will answer you with a big code to heavier their servers

Ah I see the code now! I'm not able to try this out at the moment, but I will as soon as I can and let you know. Thank you!

So I've implemented your code Thom and was still getting the same error. So what I decided to do was change my targeting behaviour and always start with one target selected by default. This has resolved the error. Because it doesn't exactly solve the problem I originally posted, I guess I can't mark this one as answered just yet but I'm fine with this now. Thanks for your work!

Do you get this error before you starts the game or during the game ?