1
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GateControl : MonoBehaviour
{
    public Transform door;
    public float doorSpeed = 1.0f;
    public bool randomDoorSpeed = false;
    [Range(0.3f, 10)]
    public float randomSpeedRange;

    private Vector3 originalDoorPosition;

    // Use this for initialization
    void Start()
    {
        originalDoorPosition = door.position;
    }

    // Update is called once per frame
    void Update()
    {
        if (randomDoorSpeed == true && randomSpeedRange > 0.3f)
        {
            StartCoroutine(DoorSpeedWaitForSeconds());
        }
        door.position = Vector3.Lerp(originalDoorPosition,
            new Vector3(originalDoorPosition.x, originalDoorPosition.y, 64f),
            Mathf.PingPong(Time.time * doorSpeed, 1.0f));
    }

    IEnumerator DoorSpeedWaitForSeconds()
    {
        doorSpeed = Random.Range(0.3f, randomSpeedRange);

        yield return new WaitForSeconds(3);
    }
}

Making StartCoroutine inside the Update is a bad idea. But I want that it will take one random speed when running the game then will wait 3 seconds and change to a new random speed then wait another 3 seconds and change for another new random speed and so on.

And while it's waiting 3 seconds to keep the current speed constant until the next change.

Update:

This is what I tried:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GateControl : MonoBehaviour
{
    public Transform door;
    public float doorSpeed = 1.0f;
    public bool randomDoorSpeed = false;
    public bool IsGameRunning = false;
    [Range(0.3f, 10)]
    public float randomSpeedRange;

    private Vector3 originalDoorPosition;

    // Use this for initialization
    void Start()
    {
        IsGameRunning = true;
        originalDoorPosition = door.position;

        StartCoroutine(DoorSpeedWaitForSeconds());
    }

    // Update is called once per frame
    void Update()
    {
        door.position = Vector3.Lerp(originalDoorPosition,
            new Vector3(originalDoorPosition.x, originalDoorPosition.y, 64f),
            Mathf.PingPong(Time.time * doorSpeed, 1.0f));
    }

    IEnumerator DoorSpeedWaitForSeconds()
    {
        var delay = new WaitForSeconds(3);//define ONCE to avoid memory leak
        while (IsGameRunning)
        {
            if (randomDoorSpeed == true && randomSpeedRange > 0.3f)
                doorSpeed = Random.Range(0.3f, randomSpeedRange);

            yield return delay;
        }
    }
}

But there are two problems.

The first problem is that every 3 seconds, when it's changing the speed of the door, it's also changing the door position from its current position. So it looks like the door position is jumping to another position and then continue from there. How can I make that it will change the speed meanwhile the door keep moving from its current position ?

Second problem is how can I change the randomDorrSpeed flag so it will take effect while the game is running? I Want that if randomDorrSpeed is false use the original speed of the door (1.0) and if it`s true use the random speed.

Ignacio Alorre
  • 7,307
  • 8
  • 57
  • 94
Benzi Avrumi
  • 937
  • 1
  • 8
  • 24
  • 1
    Consider doing a calculation in your `Update` method to determine how many seconds have passed since your last change, your approach for the random is fine. I would keep track of it with `DateTime.Now - lastChange`. – Hazel へいぜる Jul 30 '18 at 21:07
  • Can you launch an external thread that will raise an event or update a shared variable when three seconds has passed ? – snowCrabs Jul 30 '18 at 21:08
  • @snowCrabs I wouldn't use external threads for this. There are so many better options. – Draco18s no longer trusts SE Jul 30 '18 at 21:17
  • 1
    One question about the second problem. For what do you need to check if game is running? I mean if you are in the script is because the game is running. Under what circumstances you consider randomDorrSpeed can be false? – Ignacio Alorre Jul 30 '18 at 22:16
  • @IgnacioAlorre I can use while(true) instead while(isgamerunning) so I don't need the isgamerunning at all. And the randomDoorSpeed is public if it's false it will use the original constant speed 1 and if it's true it will use the random speed. Look at Bijan solution answer. So I don't need the IsGameRunning but randomDoorspeed is to decide if to use or not random speed. – Benzi Avrumi Jul 30 '18 at 23:15

3 Answers3

3

You already know that the coroutine should start from Start:

void Start()
{
     //initialization
     StartCoroutine(DoorSpeedWaitForSeconds());
}

So make the coroutine a loop with the proper terminate condition:

IEnumerator DoorSpeedWaitForSeconds()
{
    var delay = new WaitForSeconds(3);//define ONCE to avoid memory leak
    while(IsGameRunning)
    {
        if(randomDoorSpeed == true && randomSpeedRange > 0.3f)
            doorSpeed = Random.Range(0.3f, randomSpeedRange);

        if(!randomDoorSpeed)
            doorSpeed = 1;//reset back to original value

        yield return delay;//wait
    }
}

For the other question you asked, if you think about it you can't possibly use ping pong with dynamic speed based on Time.time. You need to change it like this:

bool isRising = true;
float fraq = 0;
void Update()
{
    if (isRising)
        fraq += Time.deltaTime * doorSpeed;
    else
        fraq -= Time.deltaTime * doorSpeed;

    if (fraq >= 1)
        isRising = false;
    if (fraq <= 0)
        isRising = true;

    fraq = Mathf.Clamp(fraq, 0, 1);


    door.position = Vector3.Lerp(originalDoorPosition,
        new Vector3(originalDoorPosition.x, originalDoorPosition.y, 64f),
        fraq);
}
Bizhan
  • 16,157
  • 9
  • 63
  • 101
  • It's working but I updated my question edited my question with two sub problems. When the speed is changing randomly the door is changing from it's current position to another position like the door is "jumping" to a new position. And I want the door to continue from it's current position but with a new speed. – Benzi Avrumi Jul 30 '18 at 21:44
  • @BenziAvrumi Ok. I updated my answer please take a look. – Bizhan Jul 30 '18 at 22:05
3

You can solve the original problem without coroutines:

public float timeBetweenChangeSpeed = 3f;
public float timer = 0;

void Update ()
{
    // Add the time since Update was last called to the timer.
    timer += Time.deltaTime;

    // If 3 seconds passed, time to change speed
    if(timer >= timeBetweenChangeSpeed)
    {
        timer = 0f;
        //Here you call the function to change the random Speed (or you can place the logic directly)
        ChangeRandomSpeed();
    }

}

And about opening and closing doors. Here is a script I used to control doors in a maze game I worked in:

You need to set empty gameObjects with to set the boundaries, that is until what point you want to move the door one opening or when closing. You place this empty gameobjects in your scene and link them to the scrip in the correct field. The script will take the transform.position component on it's own. There is also a trigger enter to activate the door when a character approaches. If you dont need that part, I can edit the code tomorrow.

You can use this script also to move platforms, enemies... in general anything which can move in a straight line.

using UnityEngine;
using System.Collections;

public class OpenDoor : MonoBehaviour {

    // define the possible states through an enumeration
    public enum motionDirections {Left,Right};
    // store the state
    public motionDirections motionState = motionDirections.Left;

    //Variables for State Machine
    bool mOpening = false;
    bool mClosing = false;

    //bool mOpened = false;

    //OpenRanges to open/close the door
    public int OpenRange = 5;
    public GameObject StopIn;   
    public GameObject StartIn;

    //Variables for Movement
    float SpeedDoor = 8f;
    float MoveTime = 0f;    

    int CounterDetections = 0;


    void Update () {
        // if beyond MoveTime, and triggered, perform movement
        if (mOpening || mClosing) {/*Time.time >= MoveTime && */
            Movement();
        }
    }

    void Movement()
    {
        if(mOpening)
        {
            transform.position = Vector3.MoveTowards(transform.position, StopIn.transform.position, SpeedDoor * Time.deltaTime);

            if(Vector3.Distance(transform.position, StopIn.transform.position) <= 0)
                mOpening = false;

        }else{ //This means it is closing

            transform.position = Vector3.MoveTowards(transform.position, StartIn.transform.position, SpeedDoor * Time.deltaTime);

            if(Vector3.Distance(transform.position, StartIn.transform.position) <= 0)
                mClosing = false;

        }
    }

    // To decide if door should be opened or be closed
    void OnTriggerEnter(Collider Other)
    {
        print("Tag: "+Other.gameObject.tag);

        if(Other.gameObject.tag == "Enemy" || Other.gameObject.tag == "Player" || Other.gameObject.tag == "Elevator")
        {
            CounterDetections++;
            if(!mOpening)
                Opening();
        }       
    }

    void OnTriggerStay(Collider Other)
    {
        if(Other.gameObject.tag == "Elevator")
        {
            if(!mOpening)
                Opening();
        }
    }

    void OnTriggerExit(Collider Other)
    {
        if(Other.gameObject.tag == "Enemy" || Other.gameObject.tag == "Player")
        {
            CounterDetections--;
            if(CounterDetections<1)
                Closing();
        }
    }

    void Opening()
    {
        mOpening = true;
        mClosing = false;
    }

    void Closing()
    {
        mClosing = true;
        mOpening = false;
    }
}
Ignacio Alorre
  • 7,307
  • 8
  • 57
  • 94
-1

Using timer and setting an interval. The delegate event will fire every time the interval is reached.

    var t = new Timer {Interval = 3000};
    t.Elapsed += (sender, args) => { /* code here */};
martinni39
  • 323
  • 3
  • 10