1

In my Unity Project, I have list of 4 signals. In the code, when CheckPress() is called, a random index, what I call the firststimIndex is selected from a list of signals and that index is sent to MAX/MSP via UDP to play some audio. When PlaySignal() is called, the audio associated with this index is played. This works fine. Then, when LoadNextSignal() is called, this previous index is removed and a new random index is loaded and played and is removed. LoadNextSignal() needs to be called 3 times as the remaining number of signals are three. This process will continue until all signals from the list are removed.

My problem lies when LoadNextSignal()is executed when called second time. How do I fix this part of my code? Also, The variable stimIndex is required as in my project when I write the data, it is written as signals[stimIndex].

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Text;
using System.Net;
using System.Net.Sockets;

public class CheckButtonBehaviour : MonoBehaviour
{
public Button Check;
public Button Play;
public Button NextSignal;

public List<string> signals = new List<string> {"Speech1", "Speech2", "Speech3", "Speech4"};

public int stimIndex;
public int index = 1;
public int counter = 3;
public int remainingSignals;

private static int localPort;
private string IP;  
public int port = 8050;
IPEndPoint remoteEndPoint;
UdpClient client;

void Start()
{
    IP = "127.0.0.1";
    port = 8050;
    remoteEndPoint = new IPEndPoint(IPAddress.Parse(IP), port);
    client = new UdpClient();
    Play.gameObject.SetActive(false);
    NextSignal.gameObject.SetActive(false);
}

public void CheckPress()    
{
    var random = new System.Random();
    int firststimIndex = random.Next(signals.Count);

    ///Sends to MAX MSP 
    string text = firststimIndex.ToString();
    byte[] data5 = Encoding.ASCII.GetBytes(text);
    client.Send(data5, data5.Length, remoteEndPoint);
    Debug.Log("<color=red>First signal is : </color>"+ signals[firststimIndex]);
    stimIndex = firststimIndex;
    
    Check.gameObject.SetActive(true);
    Check.interactable = false;
    Check.enabled = false;

    Play.gameObject.SetActive(true);
    NextSignal.gameObject.SetActive(true);
}

public void LoadNextSignal()
{
   if(signals.Count == 0)
      return;
   signals.RemoveAt(stimIndex); //Removes the previous signal
   remainingSignals = signals.Count;

   Debug.Log("<color=red>Remaining Signals are : </color>" + remainingSignals);
   int randomIndex = UnityEngine.Random.Range(0, remainingSignals);
   string text1 = randomIndex.ToString(); 
   byte[] data6 = Encoding.ASCII.GetBytes(text1);
   stimIndex = randomIndex;
   client.Send(data6, data6.Length, remoteEndPoint);
   
   
   Debug.Log("Loaded Signal" + stimIndex + "; remaining signals before removal equals" + remainingSignals);
   index++;

    if (counter >= index)
    {
        Debug.Log("<color=orange>Yay! Keep Listening!</color>");
    }
    else
    {
        Debug.Log("<color=green>All Trials finished</color>");
        Play.gameObject.SetActive(false);
        NextSignal.gameObject.SetActive(false);
    }
}

void PlaySignal()
{
    byte[] data2 = Encoding.ASCII.GetBytes("A"); //B
    client.Send(data2, data2.Length, remoteEndPoint);
    Debug.Log("<color=red>Signal is Played</color>");
}
}
Daniel Widdis
  • 8,424
  • 13
  • 41
  • 63
  • Isn't it called just so many times that there are no items in the list anymore? You have no code that would cover that situation. – Tatranskymedved Nov 23 '20 at 11:01
  • If you have for example 4 remainingSignals you only have the index values from 0-3 in your signals list but your random code produces indexes from 0 to 4. You should change the upper bound there to remainingSignals-1. – Ralf Nov 23 '20 at 11:03
  • @Ralf I don't think so. For int parameters the second argument is **exclusive** so the values will actually be `0` to `remainingSignals - 1` – derHugo Nov 23 '20 at 11:52
  • And yes what if `remainingSignals == 0` ? You should check for that case like `if(remainingSignals == 0) { return; }` or something similar ... Have you tried debugging your code like by line and see at which point it behaves not as expected? I guess you could find the issue on your own this way .. otherwise one of us would just have to do the same in our mind which currently I have no huge ambitions for ;) – derHugo Nov 23 '20 at 11:54
  • @Tatranskymedved, I did not understand "By so many times". `LoadNextSignal()` will only be called thrice as three signals are supposed to be remaining in the list. – Tanmayee Pathre Nov 23 '20 at 15:56
  • @Ralf, `remaining signals` is 3. If I put `remaining signals -1`, then `Random.Range` will randomise from between 0 and 1 as the second argument is exclusive. – Tanmayee Pathre Nov 23 '20 at 16:14
  • @derHugo, I tried debugging the code line by line. The problem lies in the line `signals.RemoveAt(stimIndex)` and `signals.RemoveAt(randomIndex)`. If I call, `signals.RemoveAt(stimIndex)` under `CheckPress()`, I get Argument Out of Range exception error. I removed `signals.RemoveAt(randomIndex)`. – Tanmayee Pathre Nov 23 '20 at 16:49
  • Is there any other way to fix this issue, by that I mean using a separate script? – Tanmayee Pathre Nov 23 '20 at 16:51
  • @TanmayeePathre I can't see `LoadNextSignal()` called anywhere. That means it can be executed more times then expected. In that case the function should be covered to run only when there are proper arguments/values (thus you will cover situation with out-of-range exception). – Tatranskymedved Nov 23 '20 at 17:00
  • @Tatranskymedved, I did not understand `LoadNextSignal()` being called. It is called in the same manner as `CheckPress()` and `PlaySignal()`.Could you please elaborate? I am still a beginner in programming. – Tanmayee Pathre Nov 24 '20 at 11:27

1 Answers1

1

There are several "problems" with the code, but they can be fixed:

  1. Using UnityEngine.Random.Range(0, remainingSignals) for Int value.

This function always return float as stated on Unity page for this function . You want to have result as int and I would follow that new System.Random().Next(signals.Count) only.


  1. Access those list items when it can be empty

In development such program/functions, which are called from different places, developer is usually trying to prevent any problems & exceptions when it is possible. As an example we can take your method public void LoadNextSignal() and add following lines at the beginning:

public void LoadNextSignal()
{
   if(signals.Count == 0) return;

   //... the rest of the code
}

This will prevent stepping out of the index & casting this exception. Main idea behind is that you can never be sure, how many times it was executed.

Tatranskymedved
  • 4,194
  • 3
  • 21
  • 47
  • I tried the steps 1 and 2 that you suggested. The Argument Out of Range error is solved. However, there is one signal that is repeated and one of them never played. In a nutshell, I hear 4 signals but they are not different from each other. For example, if Speech1 was played as the first signal, it is either repeated as third or fourth signal again. – Tanmayee Pathre Nov 24 '20 at 16:25
  • I checked in the Inspector the stimIndex variable, what I observed that its value never goes above 2. It takes values 0, 1, and 2. Because of which, I never hear Speech4. – Tanmayee Pathre Nov 24 '20 at 16:32
  • Can anyone suggest something what I should do? – Tanmayee Pathre Dec 02 '20 at 19:38
  • I suggest you to try to draw a chart of flow. For each method - when it is called, when it reduces items in the list and when it takes out the value. Try to debug each step and see what the app do. This way you will understand why it doesn't work as you expect and find how to solve it. – Tatranskymedved Dec 03 '20 at 09:33