using UnityEngine; using UnityEditor; using System.Collections; [ExecuteInEditMode] public class TileManager { public static bool DEBUG_MSGS = false; static bool removeSides = false; static bool replacePrefabs = false; static string[] REP_PREFAB_NAMES = {"tile_Lab_light", "tile_Lab_dark", "tile_Purple"}; void Start() { } void Update() { } static bool KnownPrefab(GameObject nom) //Checks the array of replaceable prefab names above against the supplied string { for(int k = 0; k < REP_PREFAB_NAMES.Length; k++) { if(REP_PREFAB_NAMES[k].Contains(nom.name)) return true; } return false; } [MenuItem ("[Tile Manager]/Repopulate Tile Sides", false, 0)] //REPOPULATE TILE SIDES static void CompleteTileSides() { int platforms = 0; int destroyed = 0; if(DEBUG_MSGS) Debug.Log("TILE MANAGER: Discovering all tiles..."); //Populate an array of all active GameObjects GameObject[] scene = (GameObject[])Object.FindObjectsOfType(typeof(GameObject)); //Cycle through the array, tallying tile tops and removing existing sides/bottoms for(int i = 0; i < scene.Length; i++) { if(scene[i]) { if(scene[i].name.Contains("tileBasic_top")) { scene[i].name = "tileBasic_top"; platforms++; } else if(scene[i].name.Contains("tileBasic_") || scene[i].name == "New Game Object") { Object.DestroyImmediate(scene[i],false); destroyed++; } //Replace known default-sized tile prefabs with the new basic tile top layout else if(replacePrefabs && KnownPrefab(scene[i]) && scene[i].GetComponent() != null) { GameObject newPlat = (GameObject)AssetDatabase.LoadAssetAtPath("Assets/Platforms/tileBasicSides/tileBasic_top.prefab", typeof(GameObject)); newPlat = (GameObject)Object.Instantiate(newPlat,scene[i].transform.position,Quaternion.identity); if(newPlat.GetComponent() != null) newPlat.GetComponent().material = scene[i].GetComponent().sharedMaterial; newPlat.name = "tileBasic_top"; Object.DestroyImmediate(scene[i],false); platforms++; } } } if(DEBUG_MSGS) Debug.Log("TILE MANAGER: Found " + platforms + " tile tops.\nDestroyed " + destroyed + " old tile sides/bottoms."); if(removeSides) { removeSides = false; return; //Exit function now if only meant to remove the tile sides } //Perform the checks and create necessary tile tops for(int i = 0; i < scene.Length; i++) { if(scene[i]) { if(DEBUG_MSGS) Debug.Log("TILE MANAGER: Confirmed GameObject for iteration #" + i); if(AddTileSides(scene[i])) { if(DEBUG_MSGS) { Debug.Log("TILE MANAGER: Tile sides check complete for platform #" + platforms + " (of iteration #" + i + ")."); platforms++; } } else { if(DEBUG_MSGS) Debug.Log("TILE MANAGER: Iteration #" + i + " - GameObject not a valid platform. Moving on..."); } } } replacePrefabs = false; if(DEBUG_MSGS) Debug.Log("TILE MANAGER: Operation complete -- tile sides filled."); } static bool AddTileSides(GameObject target) //ALL THE TILE CHECKING STUFF EVER { Vector3 dirCheck = Vector3.zero; GameObject[] surTile = new GameObject[8]; //Array to store detected surrounding tiles int surCount = 0; //Integer from 0-4 used to represent the number of cardinal surrounding tiles int firstEmpty = -1; //An integer identifier, from 0-3, indicating the first empty space beside this tile int firstTile = -1; //An integer identifier, from 0-3, indicating the first other tile beside this tile bool gap = false; //Used to separate a corner-case surCount of 2 from a parallel-case if(target.name == "tileBasic_top" && target.GetComponent() != null) { if(DEBUG_MSGS) Debug.Log("TILE MANAGER: Confirmed valid platform target."); //platforms++; //Initialize the loop to check in all 8 directions around this tile //Populates surTile[] with resulting discovered tiles //(We are searching in 8 directions because we might need the diagonals' info for more //dynamic tiles, like grass, snow, and ice.) for(int j = 0; j < 8; j++) { float mult = 1.025f; //Multiply the directional vector just enough to detect a platform directly next to this one mult += j % 2 == 1 ? (Mathf.Sqrt(2)-1) : 0f; //Adjust the multiplier for 45-degree angles, or "corners" dirCheck = new Vector3(Mathf.Cos(j * (Mathf.PI/4)),0f,Mathf.Sin(j * (Mathf.PI/4))); dirCheck *= mult; dirCheck = new Vector3(dirCheck.x,0.26f,dirCheck.z); if(DEBUG_MSGS) Debug.Log("TILE MANAGER: Set offset vector for tile check iteration #" + j +"; vector is " + dirCheck + "."); //Check for another tile in the current target direction RaycastHit[] checkObs = Physics.RaycastAll(target.transform.position+dirCheck,Vector3.down,0.52f); for(int k = 0; k < checkObs.Length; k++) { if(checkObs[k].transform.gameObject.tag == "Platform" //&& Mathf.Approximately(checkObs[k].transform.position.y,target.transform.position.y)) && checkObs[k].collider.bounds.max.y >= target.GetComponent().bounds.max.y && checkObs[k].collider.bounds.min.y <= target.GetComponent().bounds.min.y) { //This currently filters out platforms that aren't at the exact y position of this one. //There will obviously be errors in unusual cases, but this should cover most basic cases and make things easier for us. surTile[j] = checkObs[k].transform.gameObject; if(DEBUG_MSGS) Debug.Log("TILE MANAGER: Confirmed nearby platform for circular sweeping iteration #" + j + "."); if(j % 2 == 0) { surCount++; if(DEBUG_MSGS) Debug.Log("TILE MANAGER: Platform counts as surrounding. surCount is now " + surCount + "."); if(firstTile == -1) { firstTile = j/2; if(DEBUG_MSGS) Debug.Log("TILE MANAGER: This platform is the first detected nearby."); } } break; } } if(j % 2 == 0 && firstEmpty == -1 && !surTile[j]) { firstEmpty = j/2; if(DEBUG_MSGS) Debug.Log("TILE MANAGER: First empty space for circular sweeping iteration #" + j +" found."); } } //surTile[] is populated! Now, let's use our corner-cutting variables to detect the best thing to do if(DEBUG_MSGS) Debug.Log("TILE MANAGER: Completed circular sweeping check for current platform target."); GameObject newSide = new GameObject(); //Used to finally reference the new tile side object after creation Object.DestroyImmediate(newSide,false); if((!surTile[0] && !surTile[4]) || (!surTile[2] && !surTile[6])) gap = true; //Because only possible patterns for gap is 1010 or 0101 if(surCount == 4) { if(DEBUG_MSGS) Debug.Log("TILE MANAGER: --- END --- Detected fully surrounded case."); //Surrounded -- no need to place tile sides. (THERE WAS A BREAK HERE WHAY? D'X) } //Below, place corresponding tile sides w/current tile's material else if(surCount == 3) { if(DEBUG_MSGS) Debug.Log("TILE MANAGER: --- END --- Detected need for singular tile side."); //Place down tileBasic_sidesOne & add 90deg*firstEmpty to 90deg rotation newSide = (GameObject)AssetDatabase.LoadAssetAtPath("Assets/Platforms/tileBasicSides/tileBasic_sidesOne_P.prefab", typeof(GameObject)); newSide = (GameObject)Object.Instantiate(newSide,target.transform.position,Quaternion.AngleAxis(-1*((90f*(float)firstEmpty) + 90f), Vector3.up)); if(DEBUG_MSGS) Debug.Log("TILE MANAGER: --- END --- Final rotation value: " + ((90f*(float)firstEmpty) + 90f)); } else if(surCount == 1) { if(DEBUG_MSGS) Debug.Log("TILE MANAGER: --- END --- Detected need for three tile sides."); //Place down tileBasic_sidesThree & add 90deg*firstTile to -180deg rotation newSide = (GameObject)AssetDatabase.LoadAssetAtPath("Assets/Platforms/tileBasicSides/tileBasic_sidesThree_P.prefab", typeof(GameObject)); newSide = (GameObject)Object.Instantiate(newSide,target.transform.position,Quaternion.AngleAxis(-1*((90f*(float)firstTile) - 180f), Vector3.up)); if(DEBUG_MSGS) Debug.Log("TILE MANAGER: --- END --- Final rotation value: " + ((90f*(float)firstTile) - 180f)); } else if(surCount == 2) { if(gap) //Parallel case { if(DEBUG_MSGS) Debug.Log("TILE MANAGER: --- END --- Detected need for 2 parallel tile sides."); //Place down tileBasic_sidesTwoParallel & add 90deg*firstTile to 0deg rotation newSide = (GameObject)AssetDatabase.LoadAssetAtPath("Assets/Platforms/tileBasicSides/tileBasic_sidesTwoParallel_P.prefab", typeof(GameObject)); newSide = (GameObject)Object.Instantiate(newSide,target.transform.position,Quaternion.AngleAxis(-1*(90f*(float)firstTile), Vector3.up)); if(DEBUG_MSGS) Debug.Log("TILE MANAGER: --- END --- Final rotation value: " + (90f*(float)firstTile)); } else //Corner case { if(DEBUG_MSGS) Debug.Log("TILE MANAGER: --- END --- Detected need for singular 2 tile sides in a corner."); //Place down tileBasic_sidesTwoCorner & rotate by 90deg * (firstTile+firstEmpty+Mathf.Sign(firstEmpty-1)) newSide = (GameObject)AssetDatabase.LoadAssetAtPath("Assets/Platforms/tileBasicSides/tileBasic_sidesTwoCorner_P.prefab", typeof(GameObject)); newSide = (GameObject)Object.Instantiate(newSide,target.transform.position,Quaternion.AngleAxis(-1*(90f * (float)(firstTile+firstEmpty+Mathf.Sign(firstEmpty-1))), Vector3.up)); if(DEBUG_MSGS) Debug.Log("TILE MANAGER: --- END --- Final rotation value: " + (90f * (float)(firstTile+firstEmpty+Mathf.Sign(firstEmpty-1)))); } } else if(surCount == 0)//No tiles around { if(DEBUG_MSGS) Debug.Log("TILE MANAGER: --- END --- Detected need for all four tile sides."); //Place down tileBasic_sidesFour newSide = (GameObject)AssetDatabase.LoadAssetAtPath("Assets/Platforms/tileBasicSides/tileBasic_sidesFour_P.prefab", typeof(GameObject)); newSide = (GameObject)Object.Instantiate(newSide,target.transform.position,Quaternion.identity); } //The new tile side has been placed and properly rotated! Now let's apply the correct material. if(newSide) { if(newSide.GetComponent() != null) { newSide.GetComponent().material = target.GetComponent().sharedMaterial; //Set the material for tile sides to same as this tile; newSide.transform.parent = target.transform; if(newSide.name.Contains("(Clone)")) //Remove (Clone) from the ends of the new object names newSide.name = newSide.name.Substring(0,newSide.name.Length - 7); } else Debug.Log("TILE MANAGER: ERROR - No renderer exists for prefab " + newSide.name + "."); if(DEBUG_MSGS) Debug.Log("TILE MANAGER: Placed tile side for current tile."); if(newSide.name == "New Game Object") Object.DestroyImmediate(newSide,false); } return true; } else return false; } [MenuItem ("[Tile Manager]/Tile Sides + Unify Prefabs", false, 1)] //TILE SIDES + UNIFY PREFABS static void ReplacePrefabs() { replacePrefabs = true; CompleteTileSides(); } [MenuItem ("[Tile Manager]/Only Remove Tile Sides", false, 2)] //ONLY REMOVE TILE SIDES static void RemoveTileSides() { removeSides = true; CompleteTileSides(); } [MenuItem ("[Tile Manager]/Form Sector From Trigger", false, 20)] //FORM SECTOR FROM TRIGGER (begin new menu section) static void FormSector() { Transform[] selection = Selection.transforms; int i; //Scan through currently selected objects, catch first with if(selection.Length > 0) { for(i = 0; i < selection.Length; i++) { if(selection[i].GetComponent(typeof(SectorTrigger)) != null) break; } if(i < selection.Length) //Means a trigger was found. Proceed with grouping. { Debug.Log("Found trigger: " + ((SectorTrigger)selection[i].GetComponent(typeof(SectorTrigger))).sectorName); //Populate an array of all active GameObjects GameObject[] scene = (GameObject[])Object.FindObjectsOfType(typeof(GameObject)); Transform secOb = selection[i]; //Prepare a transform to store the new or existing sector object in //First pass thru scene to catch any existing sector with same name for(int o = 0; o < scene.Length; o++) { if(scene[o]) { //Has no parent && tagged as a sector && same sector name if(scene[o].transform.parent == null && scene[o].tag == "Sector" && scene[o].name == ((SectorTrigger)selection[i].GetComponent(typeof(SectorTrigger))).sectorName) { secOb = scene[o].transform; //Found an existing sector with the same name. Use it to group objects inside the sector trigger. break; } } } if(secOb == selection[i]) //If no pre-existing sector with the same name was found, create a new one and reference its transform with secOb { GameObject sec = new GameObject(); sec.transform.position = Vector3.zero; sec.tag = "Sector"; sec.name = ((SectorTrigger)selection[i].GetComponent(typeof(SectorTrigger))).sectorName; secOb = sec.transform; } selection[i].parent = secOb; //Parent the new sector to the sector trigger //Second pass thru scene. For all objects within trigger, if sectorless or of a different sector, make child of this one for(int o = 0; o < scene.Length; o++) { if(scene[o]) { if(ForSector(scene[o].transform,selection[i])) scene[o].transform.parent = secOb; } } } else Debug.Log("No sector trigger found in selection."); } else Debug.Log("No selection detected."); } static bool ForSector(Transform test, Transform sec) { Bounds bounds = sec.GetComponent().bounds; if(test != null && sec != null) { //Can't test for the tag of a non-existing parent, so we have to use a bool and do this check first bool inSec = false; if(test.parent != null) { if(test.parent.tag == "Sector") inSec = true; } //If no parent and not a sector, or if parent is a sector, and if within bounds of //the sector trigger, and not a sector trigger (it's unlikely that triggers will //overlap, but we only need to put the currently selected on in the sector group) //(andotherstuff) if(((test.parent == null && test.tag != "Sector") || inSec) && test.position.x <= bounds.max.x && test.position.x > bounds.min.x && test.position.y <= bounds.max.y && test.position.y > bounds.min.y && test.position.z <= bounds.max.z && test.position.z > bounds.min.z && sec.GetComponent(typeof(SectorTrigger)) == null && test.name != "Player" && test.name != "Main Camera") { return true; } } return false; } }