/**
     * Stops the object's movement.
     */
    public void StopObjMov(ManageObjs.Obj curObj, int i)
    {
        float endTime = (Time.time - trials[curTrial - 1].trialStart);

        ttcActualSim = (endTime - hideTime);
        if (config.debugging)
        {
            Debug.Log("TTC (simulator): " + ttcActualSim + " Valid when end is 0,0,0");
        }


        // Hide the object if it hasn't been hidden already
        if (curObj.objVisible)
        {
            HideObj(movingObjs[i]);
            curObj.objVisible = false;
            if (config.feedbackType == 1)
            {
                pmVisible = false;
            }
        }

        // Set the object to inactive and decrement numObjs
        curObj.objActive = false;
        numObjs          = numObjs - 1;
    }
        public float rotationSpeedZ;    // the speed at which the object should rotate around Z axis

        /**
         * The constructor for the ObjData object.
         */
        public ObjData(ManageObjs.Obj obj)
        {
            this.objNum         = obj.objNum;
            this.objType        = obj.objType;
            this.objScale       = obj.objScale;
            this.startPos       = obj.startPos;
            this.endPos         = obj.endPos;
            this.distTraveled   = obj.dist;
            this.velocity       = obj.velocity;
            this.timeVisible    = obj.timeVisible;
            this.rotationSpeedX = obj.rotationSpeedX;
            this.rotationSpeedX = obj.rotationSpeedY;
            this.rotationSpeedX = obj.rotationSpeedZ;
        }
    /**
     * Moves the object in a linear path.
     */
    public void LinearMotionMov(ManageObjs.Obj curObj, int i)
    {
        float fracTraveled = curObj.stepCounter / curObj.finalStep;

        movingObjs[i].position = Vector3.Lerp(startPosArr[i], endPosArr[i], fracTraveled);
        movingObjs[i].Rotate(curObj.rotationSpeedX, curObj.rotationSpeedY, curObj.rotationSpeedZ);
        curObj.stepCounter++;

        // If the object has traveled the entire distance, it should no longer be moving
        if (fracTraveled >= 1)
        {
            StopObjMov(curObj, i);
        }
    }
    /**
     * Move each object in the current trial based on the object's parameters.
     */
    void MoveObjsByStep()
    {
        ManageObjs.Obj[] objs = trials[curTrial - 1].objects;

        for (int i = 0; i < objs.Length; i++)
        {
            ManageObjs.Obj curObj = objs[i];

            // Once an object has become inactive we no longer need to move it
            if (curObj.objActive)
            {
                // Hide the object once it has been visible for its defined timeVisible
                if (curObj.stepCounter > curObj.stepHidden && curObj.objVisible)
                {
                    hideTime = (Time.time - trials[curTrial - 1].trialStart);
                    if (config.debugging)
                    {
                        Debug.Log("Time Hidden: " + hideTime);
                    }
                    HideObj(movingObjs[i]);
                    curObj.objVisible = false;
                    if (config.feedbackType == 1)
                    {
                        pmVisible = false;
                    }
                }

                // Move the object forward another step
                if (curObj.customMot)
                {
                    CustomMotionMov(curObj, i, cusMotArray[i], rotations[i]);
                }                                                                                   // Custom motion movement
                else
                {
                    LinearMotionMov(curObj, i);
                }                                    // Linear motion movement
            }
        }

        // Once numObjs reaches 0, all objects have finished moving and the repeating method can be canceled
        if (numObjs == 0)
        {
            CancelInvoke("MoveObjsByStep");
        }
    }
    /**
     * Moves the object in a custom motion path.
     */
    public void CustomMotionMov(ManageObjs.Obj curObj, int i, List <Vector3> coordinateArray, List <Vector3> rotationArray)
    {
        float duration = curObj.customDur;
        // Be careful, cusMotArrayIndex refers to the positions array index. i refers to the trial object.
        float totalFrames    = duration * rate;
        float framesPerPoint = totalFrames / (numCustomCoordinates[i] - 1);
        float fracTraveled   = curObj.stepCounter / framesPerPoint;

        if (fracTraveled >= 1) // Move onto the next position in the array.
        {
            cusMotArrayIndex[i]++;
            curObj.stepCounter = 0; // Reset the counter to 0 for the next segment.
            fracTraveled       = 0;
        }

        // Once we hit the second to last element of the array, it should no longer be moving
        if (cusMotArrayIndex[i] + 2 > numCustomCoordinates[i])
        {
            StopObjMov(curObj, i);
        }

        else // Move the object forward another step
        {
            movingObjs[i].position = Vector3.Lerp(coordinateArray[cusMotArrayIndex[i]], coordinateArray[cusMotArrayIndex[i] + 1], fracTraveled);

            if (cusMotArrayIndex[i] + 1 < numCustomCoordinates[i])
            {
                // Use the next rotation for current position's rotation.
                Vector3 currentAngle = new Vector3(
                    Mathf.LerpAngle(rotationArray[cusMotArrayIndex[i]].x, rotationArray[cusMotArrayIndex[i] + 1].x, fracTraveled),
                    Mathf.LerpAngle(rotationArray[cusMotArrayIndex[i]].y, rotationArray[cusMotArrayIndex[i] + 1].y, fracTraveled),
                    Mathf.LerpAngle(rotationArray[cusMotArrayIndex[i]].z, rotationArray[cusMotArrayIndex[i] + 1].z, fracTraveled));

                movingObjs[i].localEulerAngles = currentAngle;
            }
            curObj.stepCounter++;
        }
    }
    /**
     * Initialize the current trial.
     */
    public void InitializeTrial()
    {
        // Check that we still have more trials to run
        if (curTrial < this.trials.Length)
        {
            // Stop displaying the feedback text
            uiManager.ResetFeedbackMsg();

            Debug.Log("Trial " + trials[curTrial].trialNum + ": " + trials[curTrial].trialName + " started");

            // Get the current trial from the data array
            ManageTrials.Trial trial = trials[curTrial];


            // Initialize all of the arrays for the objects in the trial
            numObjs              = trial.objects.Length;
            objs                 = new Transform[numObjs];
            startPosArr          = new Vector3[numObjs];
            endPosArr            = new Vector3[numObjs];
            movingObjs           = new Transform[numObjs];
            cusMotArray          = new List <Vector3> [numObjs];
            rotations            = new List <Vector3> [numObjs];
            cusMotArrayIndex     = new int[numObjs];
            numCustomCoordinates = new int[numObjs];

            if (trial.playSound)
            {
                // Play sounds for the trial
                List <AudioClip> trialClips     = audioClips[curTrial];
                List <float>     trialAudioTime = audioDelays[curTrial];

                for (int tcIndex = 0; tcIndex < trialClips.Count; tcIndex++)
                {
                    AudioSource audioSource = audioSourceObject.AddComponent <AudioSource>();
                    Debug.Log("current trial index: " + curTrial);
                    audioSource.clip = trialClips[tcIndex];
                    StartCoroutine(PlaySoundAfterDelay(audioSource, trialAudioTime[tcIndex]));
                }
            }

            for (int i = 0; i < numObjs; i++)
            {
                ManageObjs.Obj curObj = trial.objects[i];

                // Set current frame to 0.
                curFrame = 0;

                // Set custom motion array to empty.
                cusMotArray[i] = new List <Vector3>();

                // Set custom rotations array to empty.
                rotations[i] = new List <Vector3>();

                if (curObj.customMot) // Check for custom motion configurations.
                {
                    // Set custom motion array index to 0.
                    cusMotArrayIndex[i] = 0;
                    // Set custom motion array (called positions) and custom rotations array (called rotations).
                    ReadCustomPositions("Assets/Trials/Custom_Motion_Positions/" + curObj.customFile, i);
                }

                Debug.Log("custom motion array for index " + i + ": ");
                for (int y = 0; y < numCustomCoordinates[i]; y++)
                {
                    Debug.Log(y + ": " + cusMotArray[i][y]);
                }

                // Set the object prefab that will be displayed
                GameObject newObj = Resources.Load("Objects\\" + curObj.objType) as GameObject;
                objs[i] = newObj.transform;

                // Set the scale of the object
                objs[i].localScale = new Vector3(curObj.objScale[0], curObj.objScale[1], curObj.objScale[2]);

                // Set the object rotation.
                if (curObj.customMot && (objs[i].localEulerAngles != rotations[i][0]))
                {
                    Debug.Log("WARNING! Object rotation is not equal to initial rotation in custom motion file. Using the rotation of first frame in " + curObj.customFile);
                    objs[i].localEulerAngles = rotations[i][0];
                }

                if (!curObj.customMot)
                {
                    objs[i].localEulerAngles = new Vector3(curObj.objRot[0], curObj.objRot[1], curObj.objRot[2]);
                }

                if (config.debugging)
                {
                    Debug.Log("rotation: " + objs[i].localEulerAngles.x + " " + objs[i].localEulerAngles.y + " " + objs[i].localEulerAngles.z);
                }

                /**
                 * Check that offsets are merely directions and don't have a magnitude. If so, correct them.
                 */

                if (Math.Abs(curObj.offsetX) > 1)
                {
                    Debug.Log("WARNING! Magnitude of x offset direction should not be greater than 1!");
                    curObj.offsetX = curObj.offsetX / Math.Abs(curObj.offsetX);
                }
                if (Math.Abs(curObj.offsetY) > 1)
                {
                    Debug.Log("WARNING! Magnitude of y offset direction should not be greater than 1!");
                    curObj.offsetY = curObj.offsetY / Math.Abs(curObj.offsetY);
                }
                if (Math.Abs(curObj.offsetZ) > 1)
                {
                    Debug.Log("WARNING! Magnitude of z offset direction should not be greater than 1!");
                    curObj.offsetZ = curObj.offsetZ / Math.Abs(curObj.offsetZ);
                }

                // Set offset directions.
                Vector3 offsetDirections = new Vector3(curObj.offsetX, curObj.offsetY, curObj.offsetZ);

                // Set initial start and end vectors.
                Vector3 startVector = new Vector3(curObj.startPos[0], curObj.startPos[1], curObj.startPos[2]);
                Vector3 endVector   = new Vector3(curObj.endPos[0], curObj.endPos[1], curObj.endPos[2]);

                if (curObj.customMot && (startVector != cusMotArray[i][0] || endVector != cusMotArray[i][numCustomCoordinates[i] - 1]))
                {
                    Debug.Log("WARNING! Custom motion initial and final positions in " + curObj.customFile +
                              " are not equal to startPos and endPos. Using initial and final positions in " + curObj.customFile);
                }

                // Get size of model
                Renderer render  = objs[i].GetComponent <Renderer>();
                Vector3  objSize = render.bounds.size;

                if (config.cameraLock)
                {
                    startPosArr[i] = new Vector3(startVector.x, startVector.y, startVector.z); // Got rid of adding camera height because was doing it twice.
                    endPosArr[i]   = new Vector3(endVector.x, endVector.y, endVector.z);

                    // Calculate camera lock offsets
                    if (curObj.customMot)
                    {
                        CameraLockOffset(cusMotArray[i], objSize, offsetDirections);
                    }

                    else
                    {
                        List <Vector3> startEndList = new List <Vector3>();
                        startEndList.Add(startPosArr[i]);
                        startEndList.Add(endPosArr[i]);
                        List <Vector3> positions = CameraLockOffset(startEndList, objSize, offsetDirections);
                        startPosArr[i] = positions[0];
                        endPosArr[i]   = positions[1];
                    }
                }
                else
                { // Add offsets for non camera-locked trials.
                    // Initialize startPosArr and endPosArr with a copy of the object's current start and end positions, respectively.
                    startPosArr[i] = startVector;
                    endPosArr[i]   = endVector;

                    // Calculate offsets
                    if (curObj.customMot)
                    {
                        Offset(cusMotArray[i], objSize, offsetDirections);
                    }

                    else
                    {
                        List <Vector3> startEndList = new List <Vector3>();
                        startEndList.Add(startPosArr[i]);
                        startEndList.Add(endPosArr[i]);
                        List <Vector3> positions = Offset(startEndList, objSize, offsetDirections);
                        startPosArr[i] = positions[0];
                        endPosArr[i]   = positions[1];
                    }
                }


                // Calculate the distance that the object must travel
                curObj.dist = Vector3.Distance((Vector3)startPosArr[i], (Vector3)endPosArr[i]);

                if (config.debugging)
                {
                    Debug.Log("Start Pos: " + startPosArr[i].x + " " + startPosArr[i].y + " " + startPosArr[i].z);
                }
                if (config.debugging)
                {
                    Debug.Log("End Pos: " + endPosArr[i].x + " " + endPosArr[i].y + " " + endPosArr[i].z);
                }

                // Instantiate the object so that it's visible
                if (curObj.customMot)
                {
                    movingObjs[i] = Instantiate(objs[i], cusMotArray[i][0], objs[i].localRotation); // Important to make sure these are correct variables.
                }

                else
                {
                    movingObjs[i] = Instantiate(objs[i], startPosArr[i], objs[i].localRotation); // Important to make sure these are correct variables.
                }
                curObj.objVisible = true;
                curObj.objActive  = true;
                if (config.feedbackType == 1)
                {
                    pmVisible = true;
                }

                // Set the variables that need to be used in the repeating method to move the objects
                curObj.step      = curObj.velocity * stepSize;
                curObj.finalStep = ((curObj.dist / curObj.velocity) * rate);

                //calculate theoretical ttcActual
                ttcActual = ((curObj.startPos[2] - (curObj.timeVisible * curObj.velocity)) / curObj.velocity);
                if (config.debugging)
                {
                    Debug.Log("TTC: " + ttcActual);
                }

                // If timeVisible is negative, the object should never disappear
                if (curObj.timeVisible < 0)
                {
                    curObj.stepHidden = -1;     // set stepHidden to be a step value that can never occur so that HideObject will never be called
                }
                else
                {
                    curObj.stepHidden = curObj.timeVisible * rate;
                }
            }
            // Set the start time of this trial so that it can be recorded by the data manager
            trial.trialStart = Time.time;
            curTrial++;
            // Call the repeating methods to move the objects and track head position
            float delay = (1.0f / rate);
            InvokeRepeating("MoveObjsByStep", 0.0f, delay);
            InvokeRepeating("Tracking", 0.0f, delay);

            // Set the trial as running
            isRunning = true;
        }
        else
        {
            // Do this check and then increment the trial num so data is only saved once
            if (curTrial == trials.Length)
            {
                expComplete = true;
                if (config.debugging)
                {
                    Debug.Log("Experiment complete");
                }
                uiManager.ShowMessage("Experiment complete");
                dataManager.Save(false);
                curTrial++;
            }
        }
    }