Simple derivative by invoking recurrently - don't work

Hi!
I’m animating pistons in my walker model. I want them to play two kinds of sound depending if the distance is shortening or getting bigger.
So here is what i wrote:

using UnityEngine;
using System.Collections;

public class PlayerSoundsHandler  : MonoBehaviour {
	public Transform Shaft;
	public Transform Cylinder;
	
	private float[] dist = new float[3];
	private int InvokedNo;

	void Start () {
		InvokedNo=0;
		SetDist();
		Invoke("SetDist",0.1f);
		Invoke("SetDist",0.2f);
	}
	
	void Update () {

		if (InvokedNo==2){
		Debug.DrawLine(new Vector3(-0.5f,0f,0f),new Vector3(-0.5f,dist[0],0f));
		Debug.DrawLine(new Vector3(-0.5f,0f,0.1f),new Vector3(-0.5f,dist[1],0.1f));
		Debug.DrawLine(new Vector3(-0.5f,0f,0.2f),new Vector3(-0.5f,dist[2],0.2f));
		}
	}

	void SetDist(){
		Debug.Log (InvokedNo);
		dist[InvokedNo] = Vector3.Distance(Shaft.position,Cylinder.position);
		Debug.Log (dist[0]+"/"+dist[1]+"/"+dist[2]);
		if (InvokedNo==2){
			if ((dist[0]<dist[1] && dist[2]<dist[1])||(dist[0]>dist[1] && dist[2]>dist[1]))
				audio.Play ();
			InvokedNo=-1;
			}
		InvokedNo=InvokedNo+1;	
		Invoke("SetDist",0.3f);
	}
	
}

So my question is: what is wrong with that code? I’m trying to figure it out for like 3 hours. The issue is, that even if something is moving with const. speed dist[2] is always smaller or bigger than the others, except first three times function is invoked (then everything works fine). I’ve tested it both with my model and on simle spheres (one static, second falling down) - so it is not my model problem.
Maybe I missunderstood how to work with Invoke? Thanks for help.

I have to say that is really difficult to understand what you actually want to do and what’s your exact problem. You said you want to play two different sounds but you only have one audio source and it seems you don’t switch the audioclip.

Next thing is, why are you executing SetDist 3 times with 0.1 sec delay? This would “schedule” SetDist 3 times and each time one is executed it would reschedule itself. Why don’t use simply call SetDist once and have it schedule itself every 0.1 seconds since that’s effectively what you’re doing now but way more complicated.

In such a case InvokeRepeating or a coroutine would make more sense i guess.

Next thing is your “InvokedNo” starts at 0 and is increased each time SetDist runs until it reaches 2 where it would stay for the rest of the game.

That means dist[0] and dist[1] are only set once when InvokedNo is 0 and 1 respectively. Once InvokedNo is 2 you only set dist[2] each call of SetDist.

I actually don’t get why you store 3 values. To calculate the delta you only need the old value and the new value.

Something like this should be enough:

using UnityEngine;
using System.Collections;
 
public class PlayerSoundsHandler : MonoBehaviour
{
    public Transform Shaft;
    public Transform Cylinder;
    private float oldDist = 0;

    float CalculateDist()
    {
        return Vector3.Distance(Shaft.position, Cylinder.position);
    }
    
    void Start ()
    {
        oldDist = CalculateDist();
        InvokeRepeating("CheckDist", 0.1f, 0.1f);
    }
    
    void CheckDist()
    {
        float dist = CalculateDist();
        float delta = dist - oldDist;
        oldDist = dist;
        if (delta > 0f)
        {
            // extending
        }
        else if(delta < 0f)
        {
            // retracting
        }
        else
        {
            // doesn't move
        }
    }
}

edit
ps: if you only want to know when the delta changes you just need to take the delta from the delta :wink: So only another variable that holds the current “direction” and just check if the direction has changed:

    // [...]
    private float oldDir = 0f;
    
    // [...]
    float dir = Mathf.Sign(delta);
    if (dir != oldDir)
    {
        if (delta > 0f)
        // [...]
    }
    oldDir = dir;
    //[...]

In this case it makes more sense to call CheckDist simply in Update and don’t use Invoke at all.