How to compress iterative code, perhaps using a list and/or loops?
Hi! I looked around and I tried a few of the answers that looked similar (that I could understand!) but alas I was unable to find what I want; my problem is extremely simple and the solution is most likely simple too! But as a newbie to programming and Unity I can't seem to find the most efficient or elegant way to get to it; I have a flat level with five (for now) floor tiles that trigger when a sphere GameObject is placed on them.
using UnityEngine;
using System.Collections;
public class FloorTrigger : MonoBehaviour {
public bool triggered;
void OnTriggerEnter(Collider col) {
if (col.tag == "TriggerObj")
triggered = true;
}
void OnTriggerExit(Collider col) {
if (col.tag == "TriggerObj")
triggered = false;
}
}
When all floor tiles have spheres on them, the current level ends and the next level is unlocked!
using UnityEngine;
using System.Collections;
public class Level02Manager : MonoBehaviour {
FloorTrigger floorTrigger;
bool trigger1, trigger2, trigger3, trigger4, trigger5;
public bool nextlevel;
void Start () {
floorTrigger1 = GameObject.Find("Trigger1").GetComponent<FloorTrigger>();
floorTrigger2 = GameObject.Find("Trigger2").GetComponent<FloorTrigger>();
floorTrigger3 = GameObject.Find("Trigger3").GetComponent<FloorTrigger>();
floorTrigger4 = GameObject.Find("Trigger4").GetComponent<FloorTrigger>();
floorTrigger5 = GameObject.Find("Trigger5").GetComponent<FloorTrigger>();
}
void Update () {
trigger1 = floorTrigger1.triggered;
trigger2 = floorTrigger2.triggered;
trigger3 = floorTrigger3.triggered;
trigger4 = floorTrigger4.triggered;
trigger5 = floorTrigger5.triggered;
if (trigger1 && trigger2 && trigger3 && trigger4 && trigger 5 && nextlevel == false) {
nextlevel = true;
Debug.Log("Win State");
}
}
}
As one can see, the iterative list of FloorTrigger's and trigger's can get quite expansive and will bloat rapidly with the complexity or size of the level! Also, the if statement will grow to a silly length as well.
What would be a clean solution to either or both problems, that will ultimately reduce the length of the script?
Answer by Landern · Nov 04, 2016 at 03:54 PM
Have a single game manager with a List of FloorTrigger type (List).
using System.Linq;
using System.Collections.Generic;
// http://wiki.unity3d.com/index.php/Singleton
public class GameManager : MonoBehavior
{
public List< FloorTrigger > CurrentFloorTriggers = new List< FloorTrigger >();
}
In the Start/Awake function of each component script of type FloorTrigger have it add itself to the List of FloorTriggers that are in play(between levels always Clear() the List.
void Start()
{
GameManager.GetInstance().CurrentFloorTrigger.Add(gameObject.GetComponent<FloorTrigger>());
}
Use a linq (like the All extension) statement (Your complexity is already O(n) meaning you're going over all the objects of FloorTrigger anyways) and check for the bool property.
if (GameManager.GetInstance().CurrentFloorTriggers.All(floorTrigger => floorTrigger.triggered == true) && !nextlevel)
{
nextlevel = true;
Debug.Log("Win State");
}
The cool thing about "registering" the reference to the new FloorTriggers in a GameManager script is you already have the instances and don't have to find the Component on the fly all the time.
Well, being "new to program$$anonymous$$g"--these words being frequently lost or unparsable on many such websites-- I didn't understand 90% of your response but I managed to infer enough to ask some CS-savvy friends to explain it to me that, I need to create a list, which I did like you suggested:
using System.Collections.Generic;
using UnityEngine;
public class FT$$anonymous$$anagertest : $$anonymous$$onoBehaviour {
public static List<FloorTriggertest> CurrentFloorTriggers = new List<FloorTriggertest>();
and then I made sure that my FloorTriggertest scripts all had that line with the "Add" extension (function? method?) that you suggested:
using UnityEngine;
public class FloorTriggertest : $$anonymous$$onoBehaviour {
public bool triggered;
void Start() {
FT$$anonymous$$anagertest.CurrentFloorTriggers.Add(gameObject.GetComponent<FloorTriggertest>());
void OnTriggerEnter(Collider col) {
if (col.tag == "TriggerObj")
triggered = true;
}
void OnTriggerExit(Collider col) {
if (col.tag == "TriggerObj")
triggered = false;
}
}
Then finally, in the script that checks the conditions to be met or not:
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class Newclasstest : $$anonymous$$onoBehaviour {
public static bool nextlevel;
void Update() {
if (FT$$anonymous$$anagertest.CurrentFloorTriggers.All(FloorTriggertest => FloorTriggertest.triggered) && !nextlevel) {
nextlevel = true;
Debug.Log("Win State");
}
}
}
Thanks.
P.S. Oh yeah, as suggested, either use a Singleton pattern (way beyond my level of understanding at the moment) or just remember to clear the List between levels by placing this somewhere smart:
FT$$anonymous$$anagertest.CurrentFloorTriggers.Clear();
I'm not very creative so I just put it in the line immediately above
Scene$$anonymous$$anager.LoadScene(n);
Also, since my requirement is only one-dimensional info (number of switches pressed) and the order or permutation of the switches pressed don't matter in this example, I switched the entire code to simply using a counter:
public class FT$$anonymous$$anagertest : $$anonymous$$onoBehaviour {
public static int CurrentFloorTriggers = 0; }
Which is then incremented in each FloorTrigger script ins$$anonymous$$d of setting a boolean:
void OnTriggerEnter(Collider col) {
if (col.tag == "TrigSphere") {
FT$$anonymous$$anagertest.CurrentFloorTriggers ++;
}
}
void OnTriggerExit(Collider col) {
if (col.tag == "TrigSphere") {
FT$$anonymous$$anagertest.CurrentFloorTriggers --;
}
}
And ultimately the checking condition no longer needs to use All to check (O(n)) but simply:
if (CurrentFloorTriggers == 5 && !nextlevel)
Thanks. Is there a hook I should know about that can let me run code when CurrentFloorTriggers is changed, to run my if statements? That way I won't even have to put it in Update() to cool my CPU even more!