- Home /
Transform List not updating position of prefab.
my code is supposed to spawn prefab objects every couple of seconds. Those spawned prefabs are then to move towards different prefab objects (endPoints) already in the world. those endPoint objects are slowing moving and that is where the problem arises. The spawned prefabs do not update the position of the endPoints, so they end up traveling towards the starting position of the endPoints. The problem might be my poor understanding of lists because I have an enemy prefab that uses the same moveTowards code as the one located on the neutralAIMovement script to follow my player prefab without any issue.
I am using a for loop to iterate through each element in the factionRelationship list while at the same time passing the i value to the index variable listNumber located on the neutralAIMovement script.
One thing I did try is to start my scene with the prefab that will travel towards endPoint already in the world as opposed to instantiating it every couple of seconds. This gave the same result. Any help would be appreciated. Thanks in advance.
public class TradingAndTradeShips : MonoBehaviour
{
public List<float> factionRelationship = new List<float>();
public NeutralAIMovement tradeShipScript;
public GameObject tradeShip;
public float waitToSpawn;
public int factionNumber;
private bool continueLoop;
void Start()
{
continueLoop = true;
StartCoroutine(SpawnTradeShip());
}
IEnumerator SpawnTradeShip()
{
while (continueLoop)
{
for (int i = 0; i < factionRelationship.Count; i++)
{
if (factionRelationship[i] >= 50f)
{
tradeShipScript.listNumber = i;
GameObject tradeObj = Instantiate(tradeShip, new Vector3(transform.position.x, transform.position.y, tradeShip.transform.position.z), tradeShip.transform.rotation);
}
yield return new WaitForSeconds(waitToSpawn);
}
}
}
public class NeutralAIMovement : MonoBehaviour {
public List<Transform> tradeEndPointList = new List<Transform>();
public TradingAndTradeShips homeRelationship;
public int listNumber;
public float speed, rotationSpeed;
void Start () {
}
void Update ()
{
Debug.Log(tradeEndPointList[listNumber].position);
transform.position = Vector2.MoveTowards(transform.position, tradeEndPointList[listNumber].position, speed * Time.deltaTime);
}
The problem (or one of them) is that you assign i
to listNumber
of a NeutralAI$$anonymous$$ovement
reference that has not been instantiated. You need to get the NeutralAI$$anonymous$$ovement
from the tradeShip
instance that you call tradeObj
like this:
NeutralAI$$anonymous$$ovement neutralAI$$anonymous$$ovement = tradeObj.GetComponent<NeutralAI$$anonymous$$ovement>();
if (neutralAI$$anonymous$$ovement != null) { neutralAI$$anonymous$$ovement.listNumber = i; }
However, you also must make sure that tradeEndPointList
is initialized and has at least i
elements in the prefab reference tradeShip
, otherwise you will get an IndexOutOfBoundsException
when you are trying to use the position
of tradeEndPointList[listNumber]
.
Do you get any errors in the console? Please share them if you do.
No I am not getting any errors.
I set four elements for each of the two lists in the inspector.
I tried what you suggested and added your code in the for loop, but the result was the same.
for (int i = 0; i < factionRelationship.Count; i++)
{
if (factionRelationship[i] >= 50f)
{
tradeShipScript.listNumber = i;
GameObject tradeObj = Instantiate(tradeShip, new Vector3(transform.position.x, transform.position.y, tradeShip.transform.position.z), tradeShip.transform.rotation);
NeutralAI$$anonymous$$ovement neutralAI$$anonymous$$ovement = tradeObj.GetComponent<NeutralAI$$anonymous$$ovement>();
if (neutralAI$$anonymous$$ovement != null) { neutralAI$$anonymous$$ovement.listNumber = i; }
}
yield return new WaitForSeconds(waitToSpawn);
}
I would like to help, but I am not sure of how your scene is set up, what objects get instantiated and which are already present in the scene. Could you give a more detailed description, possibly adding some screenshots of the Hierarchy view and Inspector of trade ship prefab?
Answer by JDelekto · Jun 26, 2018 at 10:00 AM
I think the problem is that the objects whose references are contained in your tradeEndPointList are not having their positions updated.
I would expect that somewhere you would have some code which does something like tradeEndPointList[index].position = someNewPosition;
but it is not obvious from the code that you have provided.
If the tradeEndPointList is meant to be a list of prefabs with which to generate endpoints, then consider making a new list of the instantiated endpoint prefabs, but make sure that somewhere you are updating the endpoints.
Answer by Harinezumi · Jun 27, 2018 at 03:24 PM
It turned out that the problem was that the NeutralAIMovement
component's tradeEndPointList
variable was assigned in the prefab, which means they were not the same objects as the ones in the scene. One solution is to group the end point instances under an empty game object, gather them in a script, then get that list from the script and assign it to the NeutralAIMovement
when instantiating the tradeShip. In fact TradingAndTradeShips
can just get the reference to the script once, and use it whenever instantiating a trade ship.
However, the logic can be greatly cleaned up if we analyse the required functionality. There are 3 main logic elements: instantiating trade ships, keeping track of trade end points, and moving towards your trade end point. Each can be separated from each other, and using references of the correct type, the code will be faster, simpler, and cleaner.
public class TradeEndPoints : MonoBehaviour {
// you can set up the end points manually on an instance of the script in the scene, or with code - see Awake() method below
[SerializeField] private List<Transform> tradeEndPoints = new List<Transform>();
public Transform GetTradeEndPoint (int index) { return i < tradeEndPoints.Count ? tradeEndPoints[i] : null; }
private void Awake () {
// you can automatically set it up if there is nothing assigned from Editor with this condition
if (tradeEndPoints.Count == 0) {
Transform[] childTransforms = GetComponentsInChildren<Transform>();
if (childTransforms != null) { tradeEndPoints.AddRange(childTransforms); }
}
}
}
// it would be better to call this class TradeShipSpawner - because that's what it does
public class TradingAndTradeShips : MonoBehaviour {
// alternative approach is to set up this list with values in an instance of the script in the scene
// [SerializeField] private List<Transform> tradeEndPoints = new List<Transform>();
// assign this value in the Editor to an instance of this script
[SerializeField] private TradeEndPoints tradeEndPoints = null;
[SerializeField] private NeutralAIMovement tradeShipPrefab = null;
[SerializeField] private float waitToSpawn;
[SerializeField] private List<float> factionRelationship = new List<float>(); // remember to set up the faction relationships; I suspect that you will need a separate class for this in the future
// public int factionNumber; // I removed factionNumber variable, because you don't use it
private Coroutine spawnTradeShipsCoroutine;
private void Start () {
// instead of a bool flag you can use the actual Coroutine object returned from StartCoroutine() and call StopCoroutine() on it when you want to stop it
spawnTradeShipsCoroutine = StartCoroutine(SpawnTradeShips());
}
private IEnumerator SpawnTradeShips () {
while (true) {
for (int i = 0; i < factionRelationship.Count; ++i) {
if (factionRelationShip[i] > 50) {
Transform tradeEndPoint = tradeEndPoints.GetTradeEndPoint(i);
if (tradeEndPoint != null) { // make sure the trade end point exists
// you can instantiate using a component on a prefab and you automatically get a reference to the component! Very useful, and spares you a GetComponent() call
NeutralAIMovement tradeShip = Instantiate(tradeShipPrefab); // don't need to specify position and rotation if you want to use the values from the prefab
tradeShip.SetTradeEndPoint(tradeEndPoint);
}
}
}
// I think you wanted to check all relationships and then wait, not wait between each check ;)
yield return new WaitForSeconds(waitToSpawn);
}
}
}
public class NeutralAIMovement : MonoBehaviour {
[SerializeField] private float speed = 0;
// with the help of storing this variable, you don't need the index nor the list anymore
private Transform tradeEndPoint;
public void SetTradeEndPoint (Transform value) { tradeEndPoint = value; }
private void Update () {
if (tradeEndPoint != null) {
// slightly more refined debug printout
Debug.Log("NeutralAIMovement \"" + name + "\" target position: " + tradeEndPoint.position);
transform.position = Vector2.MoveTowards(transform.position, tradeEndPoint.position, speed * Time.deltaTime);
}
}
}
Your answer
Follow this Question
Related Questions
Using Vector3.MoveTowards with each GameObject in a list 2 Answers
Changing LocalScale in code affecting prefab stored in Assets 1 Answer
How do I re-instantiate original prefab after a game object inside has been destroyed? 2 Answers
[C#] How can I destroy instantiated prefabs when many are created with the same name? 2 Answers