1

First off, I am not using any kind of game engine, I am modding a game in C# and I am NOT using UnityEngine API so I do not have any Update() functions.

So I am trying to figure out how I could create a timer, some standard out of the box C# timer that would increase the lerp distance over a set speed.

model.rotationM = Vector3.Lerp(model.rotation, model.rotationM, (float)0.016);
NAPI.Entity.SetEntityRotation(model.handle, model.rotationM);

I would like to wrap this in a timer that every 100ms it will increase the float at the end of the lerp by some set amount over the duration of a time, so say I set float speed = 5f; I want to increase that lerp distance every 100ms for 5 seconds until it reaches its goal.

Is this possible to do?

KevinM1990112qwq
  • 715
  • 2
  • 10
  • 23
  • Replace `(float)0.016` as a variable outside of the timer and add `0.016` to it each time the timer fires? – ProgrammingLlama Oct 04 '18 at 06:55
  • @John Ok, but can you show an example of a timer I could use? I am so confused with timer that would work for this. – KevinM1990112qwq Oct 04 '18 at 06:57
  • I don't know if there's a specific timer that will give you the total elapsed time. – ProgrammingLlama Oct 04 '18 at 06:58
  • Dang, well that's the question asked here, how to create the timer for this. It's so nuts its like it doesn't exist outside of Unity. – KevinM1990112qwq Oct 04 '18 at 06:59
  • Are you modding GTA as per [here](https://wiki.gtanet.work/index.php?title=OnUpdate)? If so, there's a server-side (assuming you want SS) `Update` function (see link). You could calculate the time between calls to `OnUpdate` if you needed to get the elapsed time between. Alternatively you can use the [`Timer class`](https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer?view=netframework-4.7.2) in C#, but it doesn't give you elapsed time - that's for you to track. – ProgrammingLlama Oct 04 '18 at 06:59
  • So another dumb question, would that OnUpdate() function run just like Update() in unity? If so, I should just be able to put those two calls above inside of the update. Would that automatically increment the 0.016f since it's in an update function until it reach 1.0? – KevinM1990112qwq Oct 04 '18 at 07:04
  • I'm not familiar with that `Lerp` function, but I assume it's parameters are something like `startPosition, endPosition, percentage/100` - so passing in `0.016` may get you the same result every time. As it is, you're passing in the current rotation as the target rotation, so you might end up getting closer and closer to the target without ever reaching it. – ProgrammingLlama Oct 04 '18 at 07:07
  • 1
    Hmm alright, Im giving it a whirl, I'll let you know. Thanks. – KevinM1990112qwq Oct 04 '18 at 07:09
  • @John Is it possible to put a function inside of a public void like that? For instance, Can I put the function Move() inside of the the OnUpdate? when I try, it says the modifier public is not valid for this item, when I switch to just void, it acts like it's not there. – KevinM1990112qwq Oct 04 '18 at 07:16

1 Answers1

1

I've created an example timer class which will slowly increment a value by a given amount until it reaches 100% (1.0):

public class LerpTimer : IDisposable
{
    private readonly Timer _timer;
    private readonly float _incrementPercentage = 0;
    public event EventHandler<float> DoLerp;
    public event EventHandler Complete;
    private bool _isDisposed = false;
    private float _current;

    public LerpTimer(double frequencyMs, float incrementPercentage)
    {
        if (frequencyMs <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(frequencyMs), "Frequency must be greater than 1ms.");
        }

        if (incrementPercentage < 0 || incrementPercentage > 1)
        {
            throw new ArgumentOutOfRangeException(nameof(incrementPercentage), "Increment percentage must be a value between 0 and 1");
        }
        _timer = new Timer(frequencyMs);
        _timer.Elapsed += _timer_Elapsed;
        _incrementPercentage = incrementPercentage;
    }

    private void _timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        if (_isDisposed)
        {
            return;
        }

        if (this.Current < 1)
        {
            this.Current = Math.Min(1, this.Current + _incrementPercentage);
            this.DoLerp?.Invoke(this, this.Current);
        }

        if (this.Current >= 1)
        {
            this._timer.Stop();
            this.Complete?.Invoke(this, EventArgs.Empty);
        }
    }

    public float Current
    {
        get
        { 
            if (_isDisposed)
            {
                throw new ObjectDisposedException(nameof(LerpTimer));
            }
            return _current;
        }
        set => _current = value;
    }

    public void Start()
    {
        if (_isDisposed)
        {
            throw new ObjectDisposedException(nameof(LerpTimer));
        }

        if (_timer.Enabled)
        {
            throw new InvalidOperationException("Timer already running.");
        }
        this.Current = 0;
        _timer.Start();
    }

    public void Stop()
    {
        if (_isDisposed)
        {
            throw new ObjectDisposedException(nameof(LerpTimer));
        }

        if (!_timer.Enabled)
        {
            throw new InvalidOperationException("Timer not running.");
        }
        _timer.Stop();
    }

    public void Dispose()
    {
        _isDisposed = true;
        _timer?.Dispose();
    }
}

Sample usage:

var lerpTimer = new LerpTimer(100, 0.016f);
lerpTimer.DoLerp += (sender, value) => {
    model.rotationM = Vector3.Lerp(startRotation, endRotation, value);
    NAPI.Entity.SetEntityRotation(model.handle, model.rotationM);
};
lerpTimer.Start();

So you would call this once, and then it would keep going until it reaches 100% (endRotation).

It's not necessarily the code you should use, but it should illustrate how you can use a timer to increase the value over time.


Edit to add some clarity to what a lerp function does:

double lerp(double start, double end, double percentage)
{
    return start + ((end - start) * percentage);
}

Imagine we call this every 10% from 4 to 125. We would get the following results:

0%    4
10%   16.1
20%   28.2
30%   40.3
40%   52.4
50%   64.5
60%   76.6
70%   88.7
80%   100.8
90%   112.9
100%  125

Try it online

ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86
  • @Kevin Sorry, that was a typo on my part. It should be `+=` not `=`. – ProgrammingLlama Oct 04 '18 at 07:39
  • This solution moves the item a small fraction of the endPos, then immediately moves it back to the startPos. I'm not sure why – KevinM1990112qwq Oct 04 '18 at 07:43
  • Are you passing in `startRotation` and `endRotation` (i.e. start = where the object was before it starts moving, end = where the object should be when it stops moving)? – ProgrammingLlama Oct 04 '18 at 07:46
  • Yep, they are the same from above. model.rotation is the start, and model.rotationM is the end. I changed them to those in your example. – KevinM1990112qwq Oct 04 '18 at 07:47
  • `model.rotationM = Vector3.Lerp(model.rotation, model.rotationM, lerpFactor)` is 100% wrong. Consider this: Lerp(s, e, 0.1) ... Lerp(0, 10, 0.1) -> you get 1. Now the variable (e) that previously held 10, you reassign it to hold 1. Then you call Lerp(s, e, 0.2) ... Lerp(0, 1, 0.2) -> you get 0.2 Then you reassign it to hold 0.2. You call Lerp(s, e, 0.3) ... Lerp(0, 0.2, 0.3) -> you get 0.06. The reason is that you're constantly moving the end position closer to the start, meaning that the overall percentage that the third parameter represents is getting increasingly small. – ProgrammingLlama Oct 04 '18 at 07:52
  • `Vector3 startRot = model.rotation; Vector3 endRot = model.rotationM; var lerpTimer = new LerpTimer(100, 0.016f); lerpTimer.DoLerp += (sender, value) => { Vector3 goal = Vector3.Lerp(startRot, endRot, value); NAPI.Entity.SetEntityRotation(model.handle, goal); }; lerpTimer.Start();` I changed it to this and It WORKS. One problem though, it never stops lol. It goes right past the end points and just keeps doing circles at a nice slow speed. – KevinM1990112qwq Oct 04 '18 at 07:57
  • Try the modified `_timer_Elapsed` from my edited answer. – ProgrammingLlama Oct 04 '18 at 08:02
  • Nope still just continues its movement without stopping. – KevinM1990112qwq Oct 04 '18 at 08:05
  • You aren't calling the "sample usage" code in a loop, are you? – ProgrammingLlama Oct 04 '18 at 08:07
  • No, i'm just calling it once. – KevinM1990112qwq Oct 04 '18 at 08:09
  • I'm an idiot. :-) Check `_timer_Elapsed` in my edited answer. I forgot that I was counting percentages as 0-1, not 0-100. – ProgrammingLlama Oct 04 '18 at 08:12
  • Why? lol? Were you supposed to call lerpTimer.stop(); or something? – KevinM1990112qwq Oct 04 '18 at 08:13
  • Ah ok, ill check it out now. – KevinM1990112qwq Oct 04 '18 at 08:13
  • Amazing! Hat off to you man, not only is this working, But I've learned more then I could have ever imagined in the process, lol. – KevinM1990112qwq Oct 04 '18 at 08:15
  • I saw your question the other day and it seemed like you've been struggling to wrap your head around this so I figured I'd add an answer to help you along :) – ProgrammingLlama Oct 04 '18 at 08:17
  • Yes, I've been trying to figure this out for 3 days. This is amazing. Again, 1000 thanks to you. – KevinM1990112qwq Oct 04 '18 at 08:21
  • One more question, is it possible to add a speed(overall time) to this. Like 5 seconds, I want it to take 5 seconds "total" to finish the animation, in that 5 seconds, it will move that 0.016 every 100ms and so fourth. Or ten seconds and vice versa. – KevinM1990112qwq Oct 04 '18 at 15:52
  • If you mean you want to calculate the interval for 5 minutes moving 0.016 every time, then you should divide the target time (in milliseconds, so *1000) by one over the increment (1 / 0.016). So for 5 seconds it would be (5 * 1000) / (1 / 0.016) = 80ms (or 5 * 1000 * 0.016) – ProgrammingLlama Oct 05 '18 at 00:16
  • Could I do the same thing, just change the 0.016 to get a smoother movement? Because when I did for instance `var lerpTimer = new LerpTimer(5 * 1000 * 0.001, 0.001f);` The movement process too way longer then 5 seonds, but if I leave it at 0.016 its perfect timing, just very choppy. – KevinM1990112qwq Oct 05 '18 at 19:25
  • Yeah, sure :) just change it as you need to – ProgrammingLlama Oct 06 '18 at 07:10
  • It's not working though, when I do what I did above, and say set it to 5, it takes like 20+ seconds. Extremely slow. – KevinM1990112qwq Oct 06 '18 at 18:00