Skip to content

Week 14: Interface and Application Programming

This week I went about creating a cool UI program in Unity to display an Arduino’s read input values. The only thing I needed to learn was writing to a file in Arduino.

Assignment #1: Write an Application that Interfaces a User with an Input &/or Output Device that you Made

Goal

I hoped by the end to have my soil moisture sensor be able to read values and have those values displayed in a Unity program.

Research

I already knew how to do all of this since I had tons of experience already in Unity, so I immedietly hopped in.

Initial Prototype

Writing to a File in Arduino

I looked into this and then realized something - the code is run on the arduino and not the computer itself, so there is no direct way to write to a text file. I then looked into serial port data readers, and found one here which I was going to try.

When I opened the program and plugged in the arduino, I coded this:

void setup() {
  Serial.begin(9600);
}

void loop() {
  delay(1000);
  Serial.print("Hello World");
}

I pushed this code to the Arduino and then in the serial reading application I clicked this button:

And hit “Start monitoring”. Note that no data appeared until I opened the Arduino’s Serial Monitor, and then the data started coming in:

From here, I changed my code to print in a way which I could parse later in unity using C# for the text file. I changed the code to this:

void setup() {
  Serial.begin(9600);
}

void loop() {
  delay(100);
  Serial.write("*0");
}

From here, I right clicked on the “Data (Chars)” category of data:

And hit “Redirect to file”

And made it send to a text file on my desktop. I knew it was working because each time I opened the text file, the amount of lines in the file increased.

I now had a way of reading serial data from an Arduino and writing it to a file.

Prepping the Sensor

I looked over my Input week documentation which can be viewed here on how I had hooked up my soil moisture sensor originally. I also used that code and got the sensor to read values again. Here is that code:

const int soilMoisturePin = 2;

float currentSoilMoisture = 0;

void setup() {
  Serial.begin(9600);
  pinMode(soilMoisturePin, INPUT);
}

void loop() {
  //Soil Moisture Data Calculation
  for (int i = 0; i <= 100; i++) 
  { 
    currentSoilMoisture = currentSoilMoisture + analogRead(soilMoisturePin); 
    delay(1); 
  } 

  currentSoilMoisture = currentSoilMoisture/100.0; 

  Serial.println("&" + currentSoilMoisture + "&");
}

And was able to get serial readings.

Improving the Serial-To-Txt

With my previous Serial to TXT, it only read a few bits at a time, which is not what I wanted. Some of the info was lost, which meant it would be impossible to parse. Instead, I followed a new tutorial here:

This video lead me to a program called CoolTerm, which can be downloaded FOR WINDOWS here.

And it WORKED and the reader was reading the serial data CORRECTLY. By clicking the options below, it was able to write to the text file:

And it sucsesfully put that text into a text file:

And with that I added special chars to the start of the data so I would be able to parse it later.

When programming the board again, you MUST disconnect CoolTerm’s accsess to the USB port or you will be denied acsess to push code to your board.

const int soilMoisturePin = 2;

float currentSoilMoisture = 0;

void setup() {
  Serial.begin(9600);
  pinMode(soilMoisturePin, INPUT);
}

void loop() {
  //Soil Moisture Data Calculation
  for (int i = 0; i <= 100; i++) 
  { 
    currentSoilMoisture = currentSoilMoisture + analogRead(soilMoisturePin); 
    delay(1); 
  } 

  currentSoilMoisture = currentSoilMoisture/100.0;

  Serial.println("&" + String(currentSoilMoisture));
}

With that, my data was now parsable.

Using Unity

For Unity, I began by creating a new Unity project.

Once in Unity, I added a Text object under the UI category which automatically created a canvas.

Note that I used the Text Mesh Pro Text object and imported the TMPro library, which isn’t essential, but it gives me more font and text options as well as improving the clarity of the text.

I then added a script to that Text object which would change the text to the loaded file. I looked over the C# Microsoft documentation on opening files to refresh myself. Ultimately, it turned out like this:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using System;
using System.IO;
using System.Text;

public class SensorText : MonoBehaviour
{
    public TextMeshProUGUI sensorText;
    public const string filePath = "C:/Users/nicho/OneDrive/Desktop/CoolTerm Capture 2022-05-05 15-55-02.txt";

    void Start()
    {
        sensorText = this.gameObject.GetComponent<TextMeshProUGUI>();
    }

    void Update()
    {
        UpdateSensorText();
    }

    public void UpdateSensorText()
    {
        if (File.Exists(filePath))
        {
            float sensorVal = 0.0f;
            string fileData = System.IO.File.ReadAllText(filePath);
            string[] data = fileData.Split('&');
            sensorVal = float.Parse(data[data.Length - 1]);
            sensorText.text = sensorText.ToString();
        }
        else
        {
            Debug.Log("File not found...");
        }
    }
}

This however didn’t work and consistently gave me this error:

Which I knew from previous experience meant I was trying to open an already opened file. I needed to modify my code to get around this. I followed an example here and came up with this code:

Note that I got around the sharing violation by creating a copy of that file and opening the copy as opposed to the original, then after getting the data, I deleted the copy.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using System;
using System.IO;
using System.Text;

public class SensorText : MonoBehaviour
{
    public TextMeshProUGUI sensorText;
    public const string filePath = "C:/Users/nicho/OneDrive/Desktop/CoolTerm Capture 2022-05-05 15-55-02";

    void Start()
    {
        sensorText = this.gameObject.GetComponent<TextMeshProUGUI>();
    }

    void Update()
    {
        UpdateSensorText();
    }

    public void UpdateSensorText()
    {
        if (File.Exists(filePath + ".txt"))
        {
            float sensorVal = 0.0f;

            string sourceFile = filePath + ".txt";
            string destinationFile = filePath + "1.txt";
            try
            {
                File.Copy(sourceFile, destinationFile, true);
                string fileData = System.IO.File.ReadAllText(destinationFile);
                string[] data = fileData.Split('&');
                print(float.Parse(data[data.Length - 1]));
                sensorVal = float.Parse(data[data.Length - 1]);
                sensorVal = Mathf.Abs(100 - (((sensorVal - 212) / 241) * 100));
                sensorVal = ((Mathf.RoundToInt(sensorVal * 100)) / 100);

                if (sensorVal < 0)
                {
                    sensorVal = 0;
                }
                else if (sensorVal > 100)
                {
                    sensorVal = 100;
                }

                sensorText.text = "Current Soil Moisture: " + sensorVal.ToString() + "%";

                File.Delete(destinationFile);
            }
            catch (IOException iox)
            {

            }
        }
        else
        {
            Debug.Log("File not found...");
        }
    }
}

And it WORKED! I was getting Arduino sensor values in Unity! However, there was one major flaw. My sensor value didn’t change when dipped into water. After some review of my code, I realied I had foolishly plugged the dataline into a digital pin and not an analog. I fixed this and here is that updated Arduino code:

float currentSoilMoisture = 0;

void setup() {
  Serial.begin(9600);
  pinMode(A0, INPUT);
}

void loop() {
  //Soil Moisture Data Calculation
  for (int i = 0; i <= 100; i++) 
  { 
    currentSoilMoisture = currentSoilMoisture + analogRead(A0); 
    delay(1); 
  } 

  currentSoilMoisture = currentSoilMoisture/100.0;

  Serial.println("&" + String(currentSoilMoisture));
}

And lastly added some math to make the values understandable and this is the final result:

This video here is a longer explanation, the second video below this is a shorter demo if you don’t want the voiceover!

Adding to the Unity World - Rain based off of the Moisture Sensor

I wanted to add rain which would change in heaviness based off the reading of the sensor. I began by creating a base rain particle effect using the particle system:

And changed the shape of the emmision to rectangle:

I set the scale of the emmison to 10x10 and rotated the particle system by 180 degrees to be facing down. I then created a new material which would be assigned to the rain in the Asset area:

And changed the 3D start size as seen below:

And set the speed to 25. I then correctly aligned the camera object:

I then created a prefab out of the rain gameobject so that I could spawn in more. I also added a gameobject for the origin point of the rain where I could later instantiate rains.

I then modified my sensor reading code to instantiate rain objects dependent on how moist it was. Here is the function I added to my code:

    public void UpdateRain(float reading)
    {
        if (reading <= 20)
        {
            //Have 0 rains

            if (rains.Count > 0)
            {
                List<ParticleSystem> toDestroy = new List<ParticleSystem>();

                foreach (ParticleSystem rain in rains)
                {
                    toDestroy.Add(rain);
                }

                for (int i = 0; i < toDestroy.Count; i++)
                {
                    toDestroy[i].Stop();
                    rains.Remove(toDestroy[i]);
                }
            }
        }
        else if (reading <= 40 && reading > 20)
        {
            //Have 1 rain

            List<ParticleSystem> toDestroy = new List<ParticleSystem>();

            foreach (ParticleSystem rain in rains)
            {
                if (rain != rains[0])
                {
                    toDestroy.Add(rain);
                }
            }

            for (int i = 0; i < toDestroy.Count; i++)
            {
                toDestroy[i].Stop();
                rains.Remove(toDestroy[i]);
            }

            if (rains.Count == 0)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
        }
        else if (reading <= 60 && reading > 40)
        {
            //Have 2 rains

            List<ParticleSystem> toDestroy = new List<ParticleSystem>();

            foreach (ParticleSystem rain in rains)
            {
                if (rain != rains[0] && rain != rains[1])
                {
                    toDestroy.Add(rain);
                }
            }

            for (int i = 0; i < toDestroy.Count; i++)
            {
                toDestroy[i].Stop();
                rains.Remove(toDestroy[i]);
            }

            if (rains.Count == 0)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
            else if (rains.Count == 1)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
        }
        else if (reading <= 80 && reading > 60)
        {
            //Have 3 rains

            List<ParticleSystem> toDestroy = new List<ParticleSystem>();

            foreach (ParticleSystem rain in rains)
            {
                if (rain != rains[0] && rain != rains[1] && rain != rains[2])
                {
                    toDestroy.Add(rain);
                }
            }

            for (int i = 0; i < toDestroy.Count; i++)
            {
                toDestroy[i].Stop();
                rains.Remove(toDestroy[i]);
            }

            if (rains.Count == 0)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
            else if (rains.Count == 1)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
            else if (rains.Count == 2)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
        }
        else if (reading <= 100 && reading > 80)
        {
            //Have 4 rains

            List<ParticleSystem> toDestroy = new List<ParticleSystem>();

            foreach (ParticleSystem rain in rains)
            {
                if (rain != rains[0] && rain != rains[1] && rain != rains[2] && rain != rains[3])
                {
                    toDestroy.Add(rain);
                }
            }

            for (int i = 0; i < toDestroy.Count; i++)
            {
                toDestroy[i].Stop();
                rains.Remove(toDestroy[i]);
            }

            if (rains.Count == 0)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
            else if (rains.Count == 1)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
            else if (rains.Count == 2)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
            else if (rains.Count == 3)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
        }
    }

This worked after a few fixes and it now rained depending on how much moisture the sensor detected:

Official Build

I had a few things I wanted to do and redo from my initial prototype to finish up the week:

  1. Beutify my UI and scene in Unity
  2. Creating a custom board for my sensor to read through and not read through a regular arduino

So I began by designing a new custom board which I would read from.

Board Design

I wanted to create a board which had all the tiny outs as headers so I could potentially use it in later weeks. I began by creating a fresh KiCad project and laying out all the components I would need in the schematic view:

Then I wired them accordingly:

This allowed me to have connection to all chip inputs/outputs as well as 2 additional VCCs and Grounds for sensor use. I quickly was met with a tangled mess of paths when going to the PCB editor, so I went back to the schematic editor to fix up the lines:

And while this did create quite the mess on the schematic view, it was essential to a sucsessful PCB:

So I went back to the PCB editor and finished it up:

And it looked pretty cool!

Spicing up the Unity Scene

I wanted to do a few things with the unity project, including:

  1. Building a small garden scene
  2. Majorly improving the UI
  3. Importing my actual garden models from CAD Week

I began by importing an asset pack which I had purchased a couple years back, which can be found if you want to use it for yourself here.

The asset pack I use for my project, the Low Poly Ultimate Pack can be found right here!

To import the asset, I began by going to the package manager:

Then I navigated to “My Assets” as seen below:

And I was able to find all of the assets I had purchased prior, to which I navigated to the Low Poly Ultimate Pack, which I installed.

Using the hundereds of premade precolored models this pack provided, I began building a small outdoor scene in which I would late place my own garden designed modules.

As you can see, there are quite a lot of props that I could now use to design a small garden scene, so I got to work and came up with this:

Additionally, I changed the scene camera to “orthographic” to give the level an isometric look, and also changed the skybox through the camera to a solid blue color for the sky:

I also designed a desert scene over top of the green scene for when the sensor reads <20% moisture as I thought it’d be funny:

Additionally, I seperated the two ‘sceneries’ as children of two different parents so that I could toggle either through code and disable the entire scene easiy:

Now, I coded the scene to change dependig on the moisture level, and I also added a variable to toggle moisture as I didn’t have my sensor with me and I could use it to test.

This boolean allowed me to enter a debug mode in which the float below it would act as the inputed sensor value so that I could test the effects without having the actual sensor up and running. It worked well.

From here, I wanted to overhaul the UI majorly. I wanted to have a bard on the left side of the screen with a title and the bar fluctuating. I also wanted a title and my name in the center, so I got to work.

I also used a small tool called Aseprite in order to draw up the bar that I was using.

And this was due to the fact that the default Unity button images were low resolution, as you can see:

And this new image allowed me to use the “Filled” image type (which would allow me to scale the bar based off the moisture) with an actually high resolution image.

I then hopped into the code again and worked on implimenting the bar fill.

I added this line using UnityEngine.UI; in order to acsess the image class, also public Image bar; for the bar object, and lastly bar.fillAmount = reading / 100; to adjust the bar. I also went to 1001 Fonts which provided free comercial use fonts that I could use for this project. I selected this font to use for the moisture text.

After implimenting this asset into my project, I used the TMPro font asset tool in Unity to make the font file into a TextMeshPro usable font. I then assigned it to the text.

After all of this, here are my final results:

I was very happy with how this looked. I added my name in the bottom corner:

And lastly I downloaded my Fusion models from CAD Week which are my final project, and I put them into the scene!

It was amazing to see my final project in the virtual environment. I then imported the rest of the parts needed and colored the units to their red color:

I was super happy with the visual outcome of my interfacing program, and next I was to rework Neil’s python code for reading serial to fit my needs, then its down to testing!

Reading Serial through a Custom Board

I began by speaking with Aaron Logan who was very knowledgable on using the programmer as a serial monitor. He lead me to how to program the SamD back into a serial monitor, then I spoke with Alaric Pan who lead me in the right direction for wiring. I also spoke with Adam Harris about this matter. I ended up adding the Mattair Tech JSON to my board manager, then switching my board target to “Generl D11C14A”. I pushed the following code to convert the programmer into a serial monitor:

void setup() {

   SerialUSB.begin(0);
   //Serial1.begin(57600, SERIAL_8E2); //Adjust baud rate and use SERIAL_8N1 for regular serial port usage
   Serial1.begin(9600); //Adjust baud rate and use SERIAL_8N1 for regular serial port usage
}

void loop() {
   if (SerialUSB.available()) {
      Serial1.write((char) SerialUSB.read()); //Send stuff from Serial port to USB
   }
   if (Serial1.available()) {
      SerialUSB.write((char) Serial1.read()); //Send stuff from USB to serial port
   }
} //end loop

Once doing so, I sucsesfully read through serial using the following wiring:

But my child board’s sensor analog read was programmed to the Rx pin, so I needed to go back and re-program my child board to read from a different pin. I recoded my SamD by pushing this code:

void setup() {

   SerialUSB.begin(0);
   Serial1.begin(57600, SERIAL_8E2); //Adjust baud rate and use SERIAL_8N1 for regular serial port usage
}

void loop() {
   if (SerialUSB.available()) {
      Serial1.write((char) SerialUSB.read()); //Send stuff from Serial port to USB
   }
   if (Serial1.available()) {
      SerialUSB.write((char) Serial1.read()); //Send stuff from USB to serial port
   }
} //end loop

And I reprogrammged my child board with this:

float currentSoilMoisture = 0;

void setup() {
  Serial.begin(9600);
  pinMode(2, INPUT);
}

void loop() {
  currentSoilMoisture = analogRead(2);
  Serial.println("&");
  Serial.println(currentSoilMoisture);
}

And it worked:

I found 316 to be the most moist and 675 to be the max so I redid some of my Unity math: sensorVal = Mathf.Abs(100 - (((sensorVal - 316) / 359) * 100));. This worked but I needed to average the last 100 results as to avoid the random jumps in value. I did so in Unity, but it lagged heavily due to the calculations being run. I needed to write in the sampling into the Arduino code and run the math in Unity as I had done before which worked, so I modified my arduino code to the following:

float currentSoilMoisture = 0;

void setup() {
  Serial.begin(9600);
  pinMode(2, INPUT);
}

void loop() {
  currentSoilMoisture = analogRead(2);

  //Soil Moisture Data Calculation
  for (int i = 0; i <= 100; i++) 
  { 
    currentSoilMoisture = currentSoilMoisture + analogRead(2); 
    delay(1);
  } 

  currentSoilMoisture = currentSoilMoisture/100.0;

  Serial.println("&");
  Serial.println(currentSoilMoisture);
}

And altered my Unity code.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using System;
using System.IO;
using System.Text;
using UnityEngine.UI;

public class SensorText : MonoBehaviour
{
    public TextMeshProUGUI sensorText;
    public const string filePath = "C:/Users/nicho/OneDrive/Desktop/CoolTerm Capture 2022-05-05 15-55-02";

    public Transform rainOrigin;
    public GameObject rainObject;

    private List<ParticleSystem> rains = new List<ParticleSystem>();

    public bool inDebug = true;
    public float mositure;

    public GameObject greenScene;
    public GameObject desertScene;

    public Image bar;

    void Start()
    {
        sensorText = this.gameObject.GetComponent<TextMeshProUGUI>();
    }

    void Update()
    {
        UpdateSensorText();
    }

    public void UpdateSensorText()
    {
        if (inDebug)
        {
            UpdateRain(mositure);
            return;
        }

        if (File.Exists(filePath + ".txt"))
        {
            float sensorVal = 0.0f;

            string sourceFile = filePath + ".txt";
            string destinationFile = filePath + "1.txt";
            try
            {
                File.Copy(sourceFile, destinationFile, true);
                string fileData = System.IO.File.ReadAllText(destinationFile);
                string[] data = fileData.Split('&');
                //print(float.Parse(data[data.Length - 1]));

                print(data[data.Length - 1]);
                try
                {
                    sensorVal = float.Parse(data[data.Length - 1]);
                }
                catch (FormatException iox)
                {
                    return;
                }
                print(data[data.Length - 1]);
                sensorVal = Mathf.Abs(100 - (((sensorVal - 316) / 359) * 100));
                sensorVal = ((Mathf.RoundToInt(sensorVal * 100)) / 100);

                if (sensorVal < 0)
                {
                    sensorVal = 0;
                }
                else if (sensorVal > 100)
                {
                    return;
                    sensorVal = 100;
                }

                UpdateRain(sensorVal);
                sensorText.text = "Soil Moisture: " + sensorVal.ToString() + "%";

                File.Delete(destinationFile);
            }
            catch (IOException iox)
            {

            }
        }
        else
        {
            Debug.Log("File not found...");
        }
    }

    public void UpdateRain(float reading)
    {
        if (reading > 100)
        {
            reading = 100;
        }
        else if (reading < 0)
        {
            reading = 0;
        }

        bar.fillAmount = reading / 100;
        sensorText.text = "Soil Moisture: " + reading.ToString() + "%";

        if (reading <= 20)
        {
            //Have 0 rains

            desertScene.SetActive(true);
            greenScene.SetActive(false);

            if (rains.Count > 0)
            {
                List<ParticleSystem> toDestroy = new List<ParticleSystem>();

                foreach (ParticleSystem rain in rains)
                {
                    toDestroy.Add(rain);
                }

                for (int i = 0; i < toDestroy.Count; i++)
                {
                    toDestroy[i].Stop();
                    rains.Remove(toDestroy[i]);
                }
            }
        }
        else if (reading <= 40 && reading > 20)
        {
            //Have 1 rain

            desertScene.SetActive(false);
            greenScene.SetActive(true);

            List<ParticleSystem> toDestroy = new List<ParticleSystem>();

            foreach (ParticleSystem rain in rains)
            {
                if (rain != rains[0])
                {
                    toDestroy.Add(rain);
                }
            }

            for (int i = 0; i < toDestroy.Count; i++)
            {
                toDestroy[i].Stop();
                rains.Remove(toDestroy[i]);
            }

            if (rains.Count == 0)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
        }
        else if (reading <= 60 && reading > 40)
        {
            //Have 2 rains

            desertScene.SetActive(false);
            greenScene.SetActive(true);

            List<ParticleSystem> toDestroy = new List<ParticleSystem>();

            foreach (ParticleSystem rain in rains)
            {
                if (rain != rains[0] && rain != rains[1])
                {
                    toDestroy.Add(rain);
                }
            }

            for (int i = 0; i < toDestroy.Count; i++)
            {
                toDestroy[i].Stop();
                rains.Remove(toDestroy[i]);
            }

            if (rains.Count == 0)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
            else if (rains.Count == 1)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
        }
        else if (reading <= 80 && reading > 60)
        {
            //Have 3 rains

            desertScene.SetActive(false);
            greenScene.SetActive(true);

            List<ParticleSystem> toDestroy = new List<ParticleSystem>();

            foreach (ParticleSystem rain in rains)
            {
                if (rain != rains[0] && rain != rains[1] && rain != rains[2])
                {
                    toDestroy.Add(rain);
                }
            }

            for (int i = 0; i < toDestroy.Count; i++)
            {
                toDestroy[i].Stop();
                rains.Remove(toDestroy[i]);
            }

            if (rains.Count == 0)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
            else if (rains.Count == 1)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
            else if (rains.Count == 2)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
        }
        else if (reading <= 100 && reading > 80)
        {
            //Have 4 rains

            desertScene.SetActive(false);
            greenScene.SetActive(true);

            List<ParticleSystem> toDestroy = new List<ParticleSystem>();

            foreach (ParticleSystem rain in rains)
            {
                if (rain != rains[0] && rain != rains[1] && rain != rains[2] && rain != rains[3])
                {
                    toDestroy.Add(rain);
                }
            }

            for (int i = 0; i < toDestroy.Count; i++)
            {
                toDestroy[i].Stop();
                rains.Remove(toDestroy[i]);
            }

            if (rains.Count == 0)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
            else if (rains.Count == 1)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
            else if (rains.Count == 2)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
            else if (rains.Count == 3)
            {
                rains.Add(Instantiate(rainObject, rainOrigin.transform.position, rainOrigin.transform.rotation, rainOrigin.transform).GetComponent<ParticleSystem>());
            }
        }
    }

}

And this worked perfectly!

Dowloads

WARNING: My Unity project files are NOT included in this zip, as their size is around 190 Mb!

Weeks Files

Assignment 2: Group Work (Send a Message Between Two Projects)

All group work is documented on your group site, found here.

I contributed by comparing the Unity Engine as a whole including examples of reading serial/sensor data from a custom board into the engine as a tool.

Week Summary

Overall this week was huge for me, I finally got to show off my Unity skills. It also heavily influenced a new direction for my final project, one which I am much happier with and excited to do. I learned how to read data from a Tiny to my serial ports as well, which is very significant.


Last update: May 11, 2022