2
\$\begingroup\$

I am using a sensor as the input to my game.

When a certain level of force is exerted on the sensor, for a certain amount of time, I want the next level to be reached.

How would I do this?

I have attached both the timer code and the game code, but I don't know how to use them together.

Timer code:

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class Timer : MonoBehaviour
{
    public int timeLeft = 5;
    public Text countdownText;

    // Use this for initialization
    void Start()
    {
        StartCoroutine("LoseTime");
    }

    // Update is called once per frame
    void Update()
    {
        countdownText.text = ("Time Left = " + timeLeft);

        if (timeLeft <= 0)
        {
            StopCoroutine("LoseTime");
            countdownText.text = "Times Up!";
        }
        //Invoke("ChangeLevel", 0.1f);
    }

    IEnumerator LoseTime()
    {
        while (true)
        {
            yield return new WaitForSeconds(1);
            timeLeft--;
        }
    }
    void ChangeLevel()
    {

        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
    }
}

Game code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO.Ports; 

public class arduino : MonoBehaviour {

    SerialPort sp = new SerialPort("\\\\.\\COM6", 9600);// Com port and the baud rate of the arduino
    Material m_Material;
    GameObject Sphere;



    void Awake(){
        Sphere = GameObject.FindWithTag ("Player");
        m_Material = GameObject.FindWithTag ("Player").GetComponent<Renderer > ().material ;
    }
    void Start ()
    {
        if (!sp.IsOpen) 
        { // If the serial port is not open 
            sp.Open(); // Open 
        }
        sp.ReadTimeout = 250; // Timeout for reading 
     }
    // Update is called once per frame
    void Update () {
        if (sp.IsOpen) { // Check to see if the serial port is open 
            try
            {

                string portreading = sp.ReadLine(); // get the string output of the serial port 
                float amount = int.Parse(portreading);



                if ((amount > 10f) && (amount < 100f))
                {
                    m_Material.color = Color.grey;
                   // Sphere.transform.position = (new Vector3(0f, amount * 0.1f, 0f));


                }

                if ((amount > 101f) && (amount < 150f))
                {
                    m_Material.color = Color.blue;
                    //Sphere.transform.position = (new Vector3(0f, amount * 0.1f, 0f));
                }

                if ((amount > 151f) && (amount < 201f))
                {
                    m_Material.color = Color.yellow;
                    //Sphere.transform.position = (new Vector3(0f, amount * 0.1f, 0f));
                }
                if ((amount > 201f))          
                {
                    m_Material.color = Color.red;
                   // Sphere.transform.position = (new Vector3(0f, amount * 0.1f, 0f));


                }
            }
            catch (System.Exception)
            {

            }
        }
    }
}
\$\endgroup\$
3
\$\begingroup\$

Myself, I'd recommend separating the jobs of reading from the sensor, running your timer, and coordinating your scene transitions. This way each script can do one simple job, so you don't end up with an uberscript that's trying to do everything in your scene.

We can use Unity Events to communicate between the scripts. This way, if you decide that instead of immediately loading the next scene, your timer should set off some fireworks and play some music, you can do that just by rewiring a few events, rather than gutting logic that's dependent on scene transitions out of your timer script.

This first script is responsible for reading from your sensor & reporting its values. Since you already have the sensor reading parts working, I'll elide those details here and just show the parts needed to communicate with other scripts:

public class SensorReader : MonoBehaviour {
    // Define a pattern for any methods that want to listen to the sensor:
    [System.Serializable]
    public class SensorEvent : UnityEvent<float> {}

    // Expose an event in the Inspector that you can use to wire up events.
    public SensorEvent onValueChanged;

    // Track the sensor value so it's easy to check on demand,
    // and cache a previous value so you know when it's changed.    
    public float sensorValue;
    float _previousSensorValue;

    void Update() {
        // I'll elide the details of reading from the sensor, since you've got that.
        sensorValue = ReadSensorValue();

        // You can add a tolerance range here to notify only on large changes,
        // if you find you're getting to much spam from small flickers.
        if(sensorValue != _previousSensorValue && onValueChanged != null) {
            onValueChanged.Invoke(sensorValue);
            _previousSensorValue = sensorValue;
        }
    }
}

This second script listens to a value source (hook up its OnValueChanged method to your sensor's onValueChanged event), and when the value is within a particular range, it runs a timer. When that timer runs out, it fires an event of its own.

public class RangeCountdown : MonoBehaviour {
    public float duration = 5;

    public float minValue = 50;
    public float maxValue = 100;

    // Setting this > 0 gives a little wiggle room once the timer starts,
    // so it doesn't flicker on-off-on-off when the value hovers near the threshold.
    public float hysteresis = 0.1f;

    // Expose an event in the Inspector you can use to wire up events.
    public UnityEvent onTimerComplete;

    public Text timerText;

    Coroutine _countdown;

    public void OnValueChanged(float value) {
        // If the countdown isn't active, check if we're in range to start counting.
        if (_countdown == null) {
            if (value < minValue || value > maxValue)
                return;
            _countdown = StartCoroutine(Countdown(duration));
        // Otherwise, check if we've gone out of range & need to reset.
        } else {
            if (value < maxValue + hysteresis && value > minValue - hysteresis)
                return;
            StopCoroutine(_countdown);
            _countdown = null;
            timerText.gameObject.SetActive(false);
        }
    }

    IEnumerator Countdown(float remainingDuration) {
        timerText.gameObject.SetActive(true);

        while (remainingDuration > 0f) {
            timerText.text = "Time Left = " + Mathf.CeilToInt(remainingDuration);

            // Wait one frame.
            yield return null;
            remainingDuration -= Time.deltaTime;
        }

        // Let everyone who cares know that the timer completed successfully.    
        if (onTimerComplete != null)
            onTimerComplete.Invoke();

        // End the timer & hide the timer text.
        timerText.gameObject.SetActive(false);
        _countdown = null;
    }
}

Lastly, you can have a scene loader script, that you wire up to listen to the timer's completion event. (Or, as you build on your project, you might have a little success fanfare first, and then the success fanfare later triggers the scene load - the events let you mix this up as needed)

public class SceneLoader : MonoBehaviour {

    public void LoadNextScene() {
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
    }
}
\$\endgroup\$
1
\$\begingroup\$

If i understood you correctly, after force or amount > 0 we need to subtract some number from TimeLeft, and after 5 seconds pass, we level up, so move this sphere up. Correct me if i didn't get your question. Also I simplified your IF statements using 2 arrays.

Pseudo Code

So, no need for IEnumerators. I would Use deltaTime.

//ps: This is inside same Update void where we check for colors.

 public float totalTimePressed = 0;
 public float TimeToLevel = 5;

 void Update(){
   float amount = int.Parse(portreading);

   if(amount > 0) totalTimePressed += Time.deltaTime;
   else totalTimePressed = 0; //reset

   for(int i = 0; i < Limits.Length-1; i++){
       if(totalTimePressed >= TimeToLevel){
          int index = Limits.Find(Closer(amount, Limits[i], Limits[i+1]));
          Color color = Colors[index];
          m_Material.color = color;
          Sphere.transform.position = (new Vector3(0f, amount * 0.1f, 0f));

          break;
       }
   }

 }
    //returns value that is closest to compareValue
    public static int Closer(int compareValue, int a, int b)
    {

       long calcA = Math.Abs((long)a - compareValue);
       long calcB = Math.Abs((long)b - compareValue);

       if (calcA == calcB) return 0;
       if (calcA < calcB) return a;

       return b;
    }

Docs Time.deltaTime: https://docs.unity3d.com/ScriptReference/Time-deltaTime.html

PS: reply to this answer if you really will need non-pseudo code from me :p

\$\endgroup\$
  • \$\begingroup\$ Thank you for your reply. I will try this out and update accordingly \$\endgroup\$ – user19964 Aug 7 '18 at 16:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.