Search code examples
c#.netlerp

Run lerp over timer


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?


Solution

  • 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