Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

How to gradually move object on a single function call?

Discussion in 'Scripting' started by Snicky, Aug 3, 2015.

  1. Snicky

    Snicky

    Joined:
    Mar 5, 2015
    Posts:
    36
    Hi,
    I want to gradually move an object( 3 to be specifc) on a single function call. My game generates random targetpositions if enemy dies or if you press a specific button. So I dont want to update my Objects every frame inside FixedUpdate(), but call a function that moves my object gradually when a random position is generated. I looked into Coroutines but from my understanding that wouldnt work here because they need to be called multiple times to move the object frame by frame(?).
    How could I possibly achieve movement on a single function call?
     
  2. chubbspet

    chubbspet

    Joined:
    Feb 18, 2010
    Posts:
    1,220
    You should create your own Type (Class), do the movement in that type's Update and remove it after the movement has been completed. If you are not sure how to do that, let us know, but try first:)
     
    Snicky likes this.
  3. Snicky

    Snicky

    Joined:
    Mar 5, 2015
    Posts:
    36
    So an extra Class with its Update and then enable/disabling to my needs? If event happens, enable component, if done disable.
     
  4. chubbspet

    chubbspet

    Joined:
    Feb 18, 2010
    Posts:
    1,220
    you can enable/disable or you could let the component destroy itself after the movement has completed. That way it will keep your project neater.
     
  5. Snicky

    Snicky

    Joined:
    Mar 5, 2015
    Posts:
    36
    Thank you, I will try that and update my progress later.
     
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    A coroutine can do this as well:

    Code (csharp):
    1.  
    2. public IEnumerator MoveToCoroutine(Transform targ, Vector3 pos, float dur)
    3. {
    4.     float t = 0f;
    5.     Vector3 start = targ.position;
    6.     Vector3 v = pos - start;
    7.     while(t < dur)
    8.     {
    9.         t += Time.deltaTime;
    10.         targ.position = start + v * t / dur;
    11.         yield return null;
    12.     }
    13.    
    14.     targ.position = pos;
    15. }
    16.  
    Called as:

    Code (csharp):
    1.  
    2. StartCoroutine(MoveToCoroutine(someObjectToMove, somePositionToMoveTo, someDurationToTake));
    3.  
     
    powersjo, Snicky and Kiwasi like this.
  7. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    Your teaching people wrong code practice.. Dont allocate variables inside this IEnumerator

    Code (csharp):
    1.  
    2.  
    3. your variables/ properties  <---- put allocation here
    4. public IEnumerator MoveToCoroutine(Transform targ, Vector3 pos, float dur)
    5. {
    6.     float t = 0f; // <--- allocation will generate overhead...
    7.     Vector3 start = targ.position; // <--- allocation will generate overhead..
    8.     Vector3 v = pos - start; // <--- allocation will generate overhead..
    9.     while(t < dur)
    10.     {
    11.         t += Time.deltaTime;
    12.         targ.position = start + v * t / dur;
    13.         yield return null;
    14.     }
    15.  
    16.     targ.position = pos;
    17. }
    18.  
     
    Snicky likes this.
  8. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    I'm demonstrating an "easy for a novice" way to do something.

    A lot of people are here to get the basics of code down before optimizing.

    This is certainly not how I would do it in my own project, but if I were to hand them my code, it'd be seen as "overkill" by novices.


    Oh, and it's "you're", not "your". It's a contraction of you are.

    If you want to get pedantic about stuff.
     
    RJTimmerman likes this.
  9. chubbspet

    chubbspet

    Joined:
    Feb 18, 2010
    Posts:
    1,220
    This is why I said rather create your own Type for this purpose. It has other benefits also, like you can keep it in an array/list to keep track of what timers are currently running. Also, @Snicky, in my experience it is not needed to use FixedUpdate() except if there are physics involved. But as long as you use deltaTime Update should be fine.
     
    Snicky likes this.
  10. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    Your overkilling them already with wrong basic code practice , you never allocate var inside a object, just moved them out of the object simple as that.. No excuse , do it right..
     
  11. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    O____________________________O

    How about you bring your sophistry back to your own thread instead of derailing someone else's thread. If you have a beef with me, take it up with me, not @Snicky.

    I was merely demonstrating that coroutines are capable of doing what they asked since OP said that they couldn't.

    I'm not going to argue with Ironmax about the legitimacy of where one declares their fields/variables in an unrelated thread.
     
  12. Arcatus

    Arcatus

    Joined:
    May 27, 2015
    Posts:
    45
    I wasn't sure if I would post this, as I don't want to kick a hornets nest, but as I am new to c# I just got curious;

    (And I would probably have posted this without any second thougts in a "normal" thread without the heated background - and this might be interesting for the OP and others I decided I could post)

    I have always declared stuff that are used in a method inside the method; such as placeholders for math, counters for for loops, etc, (such as all three of the above variables, (t, v and start). Single character variables are (for me) typical for a temporary variable that are always declared inside methods.

    My reasoning have been that when the method completes memorypointers will?/can? be cleared.

    For example I could do something like this;

    Code (CSharp):
    1.  
    2. public float DoFooThis(float[] FooArray)
    3. {
    4.   int i=0;
    5.   float foo;
    6.   float result;
    7. // Do something
    8.   return result;
    9. }
    10.  
    11. public float DoBaarThis(float[] BaarArray)
    12. {
    13.   int i=0;
    14.   float baar;
    15.   float result;
    16. // Do something else
    17.   return result;
    18. }
    19.  
    20.  
    According to the post by Ironmax

    it would be better to

    Code (CSharp):
    1.  
    2.  
    3. private int i;
    4. private float foobaar;
    5. private float result;
    6.  
    7. public float DoFooThis(float[] FooArray)
    8. {
    9. i=0;
    10. // Do something
    11.   return result;
    12. }
    13.  
    14. public float DoBaarThis(float[] BaarArray)
    15. {
    16. i=0;
    17. // Do something
    18.   return result;
    19. }
    20.  
    21. // i - *waves* I am still here..! This memory block is awesome, I think I'll float around with my float buddies forever!
    22.  
    23.  
    I think I prefer the tiiiny overhead during init than having garbage left over in memory. Is my reasoning incorrect? Have I been programming C# "incorrectly" for the last 3 months?
     
  13. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    Yes even they might seems like "tiny" overheads they can cause CPU lagg issue, specially on mobiledevice. If you put your objects into Update method that is running every frames you will indeed get overhead and GC will kicks in.. you can see this in the profiler if you have a issue with it, it might be fine if your objects are not being used that much, but in games objects gets reused allot. So the best practice is to declare your properties outside the object or in separated classes where you can inherit. This is highly recommended from Unity as well. Good luck, your code seems fine thought..
     
  14. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    https://docs.unity3d.com/353/Documentation/Manual/iphone-Optimizing-Scripts.html



    Avoid Allocating Memory
    You should avoid allocating new objects unless you really need to, since they increase the garbage collection overhead when they are no longer in use. You can often reuse arrays and other objects rather than allocate new ones and doing so will help to minimise garbage collection. Also, you should use structs instead of classes where possible. Struct variables are allocated from the stack like simple types rather than from the heap like object types. Since stack allocation is faster and involves no garbage collection, structs will often improve performance if they are fairly small in size. While large structs will still avoid allocation/collection overhead, they will incur a separate overhead due to "pass-by-value" copying and may actually be less efficient than the equivalent object classes.
     
  15. blizzy

    blizzy

    Joined:
    Apr 27, 2014
    Posts:
    775
    On the other hand, premature optimization is the root of all evil.
     
    Arcatus, Korno and lordofduct like this.
  16. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    @Arcatus - no, in your examples they won't have any GC/garbage collection issues if you keep them in the functions as you always been doing it. Actually by pulling them out you're being less efficient... I'll explain why...

    floats, vectors, ints, etc are all structs or 'value types'. They are not placed in the heap unless they have been boxed or are members of an object. Instead they persist on the stack, where they are quickly and efficiently pushed into the current operating memory when declared, and popped back off at closure of the function.

    Because functions aren't objects, unless you explicitly box them as well (delegates are objects that point at functions, lambdas/anonymous functions will create an object for its state to persist).

    An exception is in the case of iterator function (like those used by coroutines). Because an iterator function generates an IEnumerator, and persists for a duration of time, it has to maintain its state. Therefore an object representing its state is generated on the heap for the duration of its existence. Variables declared in the iterator function will become members of this object as they too have to persist for the lifetime of the iterator function.

    In this case the footprint of said object in memory will be dictated by the number of variables that must persist for the lifetime of the iterator function, or in the case of unity, the lifetime of the coroutine.

    By declaring the variables as members of the Component that defines the iterator/coroutine, you've moved the state to be a member of the component, rather the object that represents the coroutine when running. By doing so you reduce the size of the iterator/coroutine in memory, which in turn decreases the chances of incurring a call to garbage collect, because the heap isn't filled up as quickly (it's when the heap gets full that mono/unity will perform a garbage collection).

    There are some issues with this though. As with whenever you declare values outside of the scope of where they're used.

    For example, if you use the coroutine twice in succession. Say in my example code above, if we moved the variables outside of the scope of the coroutine like so:

    Code (csharp):
    1.  
    2. public class MoverScript : MonoBehaviour
    3. {
    4.  
    5.     //for quick access so we don't have to call StartCoroutine from the caller
    6.     public void MoveTo(Transform targ, Vector3 pos, float dur)
    7.     {
    8.         this.StartCoroutine(this.MoveToCoroutine(targ, pos, dur));
    9.     }
    10.  
    11.     private float _t;
    12.     private Vector3 _start;
    13.     private Vector3 _v;
    14.  
    15.     public IEnumerator MoveToCoroutine(Transform targ, Vector3 pos, float dur)
    16.     {
    17.         _t = 0f;
    18.         _start = targ.position;
    19.         _v = pos - start;
    20.         while(t < dur)
    21.         {
    22.             _t += Time.deltaTime;
    23.             targ.position = _start + _v * _t / dur;
    24.             yield return null;
    25.         }
    26.  
    27.         targ.position = pos;
    28.     }
    29.  
    30. }
    31.  
    And then we said:

    Code (csharp):
    1.  
    2. mover.MoveTo(targA, new Vector3(10f, 0f, 0f), 1f);
    3. mover.MoveTo(targB, new Vector3(50f, 0f, 0f), 2f);
    4.  
    What will end up happening is that targA will be animated along the path that targB is supposed to take, but at twice the speed of targB, and then magically teleport to the (10,0,0) after 1 second.

    This is why scope is important to consider, and is one of those situation that highlights why a lot of people say "globals are bad" (globals being values stored in the top most scope that it makes for a single state for the ENTIRE program).



    Of course, in your example, you don't use coroutines, and are using just regular old functions. The vectors, floats, and ints you use aren't placed on the heap, and none of this matters.

    You do not need to scope them to the class, and rather scope them to the function. All you're doing by scoping them to the class is your making that function single state (you can't call it again until the previous time it's called is complete), as well as increasing the size of the component on the heap... actually incurring MORE memory usage and increasing the odds of garbage collection.

    That's right... by pulling them from the function, you increase heap usage (because now they're scoped to the 'object' that is your component), and as we previously stated, increased heap usage will result in GC calls more frequently as it fills up faster.
     
    Last edited: Aug 3, 2015
    Arcatus, blizzy and Korno like this.
  17. Snicky

    Snicky

    Joined:
    Mar 5, 2015
    Posts:
    36
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class EnableSliderMovement : MonoBehaviour {
    5.  
    6.     public GameObject Slider;
    7.     public EnableSliderMovement enable;
    8.     public AbilitySliderRight abiliSlider;
    9.     float position;
    10.  
    11.  
    12.     void Start(){
    13.     }
    14.  
    15.  
    16.     void OnEnable(){
    17.         position = Random.Range (abiliSlider.maxWidthLeft, abiliSlider.maxWidthRight);
    18.     }
    19.    
    20.     void Update () {
    21.         RandomPosition ();
    22.         if(transform.position.x >position && transform.position.x<position+0.05||transform.position.x <position && transform.position.x>position-0.05)
    23.         enable.enabled = false;
    24.  
    25.  
    26.     }
    27.  
    28.     public void RandomPosition(){
    29.         transform.Translate  (new Vector3((position-transform.position.x)*Time.deltaTime*5,0,0));
    30.     }
    31. }
    32.  
    Works like it should.
    @lordofduct thx for showing that its possible in coroutines and to other participants for the informative debate about how to code correctly, even tough I dont rly understand :D