Search Unity

Question about transform.translate

Discussion in 'Scripting' started by D-007, Jul 15, 2022.

  1. D-007

    D-007

    Joined:
    Sep 14, 2021
    Posts:
    70
    Hey, I have a question regarding transform.translate of an object.

    So in my scene I have an object which is my player object, at this player location i want to instantiate another object which is a prefab. I had been trying to code before, but the laser prefab was not moving when I was testing this. Was it not moving because the code is on the player object rather than the laser prefab.

    I have it working now, I moved the laser.transform.translate into its own script and attached that to the laser prefab, so it moves correctly now. But I am just wanting to know why it did not work originally?

    Code (CSharp):
    1. public class playerFire : MonoBehaviour
    2. {
    3.  
    4.     [SerializeField] Transform gunPosition;
    5.     [SerializeField] GameObject laserPrefab;
    6.     GameObject laser;
    7.  
    8.  
    9.     [SerializeField] float laserSpeed;
    10.     [SerializeField] float nextFire;
    11.     [SerializeField] float coolDown;
    12.  
    13.  
    14.     void Start()
    15.     {
    16.        
    17.     }
    18.  
    19.     void Update()
    20.     {
    21.         fireLaser();
    22.     }
    23.  
    24.     void fireLaser()
    25.     {
    26.  
    27.         if (Time.time > nextFire )
    28.         {
    29.             nextFire = Time.time + coolDown;
    30.             laser = Instantiate(laserPrefab, gunPosition.position, Quaternion.identity);
    31.             laser.transform.Translate(Vector3.up * laserSpeed * Time.deltaTime);
    32.         }
    33.  
    34.     }
     
  2. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    1,929
    The code you've shared checks whether enough time has passed to fire a laser, and then fires (aka, instantiates) it and translates it forward a little bit. This is done only once, since nextFire is immediately set to whatever the current time is plus some coolDown time.

    If you want the laser to move, you need to translate it forward a little bit every frame.

    Chances are your new code is something along the lines of:
    Code (CSharp):
    1. void Update()
    2. {
    3.      transform.Translate(Vector3.up * laserSpeed * Time.deltaTime);
    4. }
    Since Update() is called every frame and you're not checking whether it's time to fire a laser or not, this is done every frame, and you see the laser moving.

    So your old code and your new code are not equivalent at all: the old one moves the laser forward just once when it's created. Your new code moves it forward constantly.
     
  3. D-007

    D-007

    Joined:
    Sep 14, 2021
    Posts:
    70
    Ok I understand that. I can move the transform into the update function of the script where it is being instantiated, but an error would occur since the object might not exist before the update is trying to move it. What would be the best way to deal with this issue. As I said I do have it working in its own script, but would like to know what the best way to deal with it would be
     
  4. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    1,929
    Nope, you can't really do that. Or rather, you shouldn't.

    There's potentially more than one laser object being instantiated by your playerFire class: every time the coolDown time ends, another laser is fired. You could keep track of the latest laser being fired, checking if it's null (that is, non existent yet) to avoid nullreference errors, and update it:

    Code (CSharp):
    1.  
    2.  
    3. GameObject laser;
    4.     void Update()
    5.     {
    6.         fireLaser();
    7.         // check if the laser exists
    8.         if (laser != null)
    9.         {
    10.             // update it
    11.             laser.transform.Translate(Vector3.up * laserSpeed * Time.deltaTime);
    12.         }
    13.     }
    14.  
    15.     void fireLaser()
    16.     {
    17.         if (Time.time > nextFire )
    18.         {
    19.             nextFire = Time.time + coolDown;
    20.             laser = Instantiate(laserPrefab, gunPosition.position, Quaternion.identity);
    21.         }
    22.     }
    Obviously this will work only for the last laser instance being fired, since the "laser" reference will be replaced by a new one each time the following line executes:

    Code (CSharp):
    1.  
    2. // whatever "laser" was up to this point (either null, or the previously fired laser), replace it with a new laser:
    3. laser = Instantiate(laserPrefab, gunPosition.position, Quaternion.identity);
    To extend this to all laser instances, you could have an array / list of lasers, to keep track of all lasers that have been fired and update them all in a loop:

    Code (CSharp):
    1.  
    2. List<GameObject> lasers = new List<GameObject>();
    3.     void Update()
    4.     {
    5.        fireLaser();
    6.        foreach(laser in lasers)
    7.        {
    8.           laser.transform.Translate(Vector3.up * laserSpeed * Time.deltaTime);
    9.        }
    10.     }
    11.  
    12.     void fireLaser()
    13.     {
    14.  
    15.         if (Time.time > nextFire )
    16.         {
    17.             nextFire = Time.time + coolDown;
    18.             lasers.Add(Instantiate(laserPrefab, gunPosition.position, Quaternion.identity));
    19.         }
    20.  
    21.     }
    22.  
    But this quickly becomes overly complicated: what happens when a laser is destroyed? you need to remove it from the list of lasers... what if you then want to fire bombs instead of lasers? you would have to basically copy the entire fireLaser script and replace lasers with bombs...

    So, the answer is simple: once instantiated, each laser should be reponsible for its own behavior. So the code to move a laser should really be in its own script.
     
    D-007 likes this.