Home > Back-end >  How do you change the gravity scale - for one second - at the start of the game and then revert back
How do you change the gravity scale - for one second - at the start of the game and then revert back

Time:06-20

I'm trying to reduce the gravity scale of my flappy bird game for approximately 1 second at start time. I want to give the player a little time cushion before he falls more quickly.

I originally put the function in the start method (does a timer work in a non-update function?) I then put it in the update function.

Regardless, the timer doesn't seem to trigger, and the gravityscale doesnt go to normal (1).

You'll also see my comment where I was trying to lerp the gravity scale instead. Is it possible to lerp a gravity scale?

void Update()
{
    Jump();
    Rotation();
    PlayerHalt();
}

private void PlayerHalt()
{
    float gravityTimer = 0f;
    gravityTimer  = Time.deltaTime;

    if (gravityTimer <= 1)
    {
        rb.gravityScale = .2f;
        //rb.gravityScale = Mathf.Lerp(.2f, 1f, gravityTimer / 1f);
    }
    else
    {
        rb.gravityScale = 1f;
    }
}

CodePudding user response:

You must use IEnumerator that acts like a Timer, at the bottom of the code you will see a simple Gravity Tweener.

private IEnumerator TweenGravity(float target, float duration)
{
    var baseGravity = rigidbody2D.gravityScale;
    
    var progress = 0f;
    while (progress < 1f)
    {
        progress  = Time.deltaTime/duration;

        rigidbody2D.gravityScale = Mathf.Lerp(baseGravity, target, progress);
        
        yield return new WaitForEndOfFrame();
    }
}
public Rigidbody2D rigidbody2D; // setup your rigidbody on inspector

private void Start() => StartCoroutine(TweenGravity(1f, 1f)); // how to call IEnumerator

How To Reverting gravity?

You can use an Action to give a callback at the end of the timer for reverting or other command.

private IEnumerator TweenGravity(float target, float duration, Action OnFinish = null)
{
    ///....

    OnFinish?.Invoke(); // this will call after wait progress finish
}
private void Start() => StartCoroutine(TweenGravity(1f, 1f, () => Debug.Log("On Finish callback")));

CodePudding user response:

Here's a method that uses a simple time delay mechanism by taking note of the start time and measuring the current elapsed interval. You can see it in the Delay class below.

Apart from Time (which you could easily replace with DateTime or even Environment.TickCount) it's essentially technology-neutral. No nasty Unity coroutines necessary (which if used incorrectly are akin to Application.DoEvents()).

Delay class:

public class Delay
{
    private float _lastInterval;

    /// <summary>
    /// The timeout in seconds
    /// </summary>
    /// <param name="timeout"></param>
    private Delay(float timeout)
    {
        Timeout = timeout;
        _lastInterval = Time.time;
    }

    public float Timeout { get; }

    public bool IsTimedOut => Time.time > _lastInterval   Timeout;

    public void Reset()
    {
        _lastInterval = Time.time;
    }

    public static Delay StartNew(float delayInSeconds)
    {
        return new Delay(delayInSeconds);
    }
}

Because it uses Unity's Time.time member it can be safely used from both Update and FixedUpdate.

Usage

private Delay _delay;
private RigidBody rb;

void Start()
{
    rb = ...
    rb.gravityScale = .2f; // reduce the gravity scale

    _delay = Delay.StartNew(1f); // One second
}


void Update()
{
    if (_delay.IsTimedOut)
    {
        rb.gravityScale = 1f;
    }

    .
    .
    .
}

Render throttling / Emulate a periodic timer

It can also be used in scenarios that demand reoccurring opperations spaced by a given frequency.

e.g. I use it a-lot for throttling processing or updates for rendering where I don't want to update things every frame but maybe every 100 ms. I can achieve this by setting a Delay whereby I update rendering to the screen/buffer/RenderTexture/Texture2D like so:

private void Update()
{
    if (!_delay.IsTimedOut)
    {
        // not yet time
        return;
    }

    // perform rendering here


    _delay.Reset();
}

  • Related