using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; /*************This class is the core of previewing the movement and placement on the grid**************/ [RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] public class GridSelector : MonoBehaviour { /*******Grid private variables********/ //Raycast variables Ray ray; RaycastHit hit; bool hitSomething; //Positions Vector3 gridSelectorPos; Vector3 currentPos; Vector3 previewObjOffset = new Vector3(); //Check positions for the placed objects Vector3 placementCheckPosition; Vector3[] gridCheckPositions; List GORemoveList; //Rotation variables int rotate = 0; bool rotating = false; GameObject rotateParent; Vector3 originalOffset; //GridSquare variables float selectedCellSize; GridSquare gridsquare; GridSquare currentGrid; //SelectObject variables GameObject selectedGameObjectToPlace; GameObject previewObj; Building.ObjectSize objectSize; int prefabId; string instanceId; int startingLayer; bool moveOnPoints; int buildTime; bool snap = false; //Materials and object manipulation variables MeshRenderer meshRenderer; Material selectorMat; Renderer[] children; Material[] previewObjOwnMats; //Cached components ObjectPlacer objectPlacer; ObjectSelector objectSelector; ObjectRemover objectRemover; GridObjectOptions gridObjectOptions; bool dragMove; bool dragBuild; bool dragAndDrop; bool dragging = false; /*****************Serialised inspector variables**********************/ //General settings [SerializeField] bool smoothMove; [SerializeField] float moveSpeed = 0.2f; [SerializeField] bool rotation; [SerializeField] bool smoothRotate; [SerializeField] float rotateSpeed = 600f; [SerializeField] float previewObjHoverDistance; [SerializeField] bool hideSelectorOnPreview; [SerializeField] bool previewObjFloorTiles; [SerializeField] GameObject previewObjFloorTilePrefab; [SerializeField] bool placeTilesWithObject; [SerializeField] bool usePreviewMatsForFloorTiles; [SerializeField] bool disableAnimationForPreviewObj = true; [SerializeField] float hoverDistance = 0.01f; //Invalid placement options [SerializeField] bool invalidPlacementFeedback = false; [SerializeField] bool showInvalidPreviewObj = false; [SerializeField] Material invalidPlacementMat; [SerializeField] Material objPreviewMat; [SerializeField] bool changeMatColorNotMat; //Accessors public GameObject SelectedGameObjectToPlace { get { return selectedGameObjectToPlace; } } public Vector3 PreviewObjOffset { get { return previewObjOffset; } } public GridSquare CurrentGrid { get { return currentGrid; } } public bool DragMove { get { return dragMove; } set { dragMove = value; } } public bool Dragging { get { return dragging; } set { dragging = value; } } public float HoverDistance { get { return hoverDistance; } } public float SelectedCellSize { get { return selectedCellSize; } } public GameObject PreviewObj { get { return previewObj; } } public bool Rotation { get { return rotation; } } public bool PlaceTilesWithObject { get { return placeTilesWithObject; } } public bool PreviewObjFloorTiles { get { return previewObjFloorTiles; } } public GameObject PreviewObjFloorTilePrefab { get { return previewObjFloorTilePrefab; } } private void Awake() { //Finds a gridSquare object in the scene to find your set cell size gridsquare = FindObjectOfType(); if (gridsquare) { //This assumes all of your grids use the same grid cell size selectedCellSize = gridsquare.CellSize; } else { Debug.Log("You have no GridSquare objects, to do anything with this component you need at least 1 gridSquare"); } selectorMat = GetComponent().sharedMaterial; //Removes all shadow options by default and hides the selector at runtime meshRenderer = GetComponent(); meshRenderer.enabled = false; meshRenderer.receiveShadows = false; meshRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; } // Start is called before the first frame update void Start() { //Finds and assigns cached objects if in the scene objectPlacer = GridBuilder2Manager.Instance.ObjectPlacer; objectSelector = GridBuilder2Manager.Instance.ObjectSelector; objectRemover = GridBuilder2Manager.Instance.ObjectRemover; gridObjectOptions = GridBuilder2Manager.Instance.GridObjectOptions; //Scales the transform of this component to match the found grids cell size Vector3 scale = transform.localScale; scale.x *= selectedCellSize; scale.z *= selectedCellSize; transform.localScale = scale; currentPos = gridSelectorPos; if (objectSelector && objectSelector.SelectorCanvas) { objectSelector.SelectorCanvas.transform.position = gridSelectorPos + new Vector3(0, hoverDistance, 0); } //Initiates an array to the amount of positions needed for objects if (FindObjectOfType()) { gridCheckPositions = new Vector3[FindObjectOfType().objSize.GetNum()]; } else { gridCheckPositions = new Vector3[25]; } } // Update is called once per frame void Update() { ray = Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out hit)) { //If the cursor is hovered over the GridSquare object if (hit.collider.GetComponent() && !EventSystem.current.IsPointerOverGameObject()) { //Hits something so tells other functions to perform hitSomething = true; //Get the center of each hovered cell GetCellPositions(); //If an object placer is present in the scene, inputs here control object placement if (objectPlacer) { //Moves the preview object to the centre of the grid cell if (previewObj != null) { //Smooth transition if (smoothMove && currentPos != Vector3.zero) { if (moveOnPoints) { previewObj.transform.position = Vector3.MoveTowards(previewObj.transform.position, (placementCheckPosition) - new Vector3((selectedCellSize * 0.5f), 0, (selectedCellSize * 0.5f)) + new Vector3(0, previewObjHoverDistance, 0), moveSpeed * Time.deltaTime); } else { previewObj.transform.position = Vector3.MoveTowards(previewObj.transform.position, (placementCheckPosition) + new Vector3(0, previewObjHoverDistance, 0), moveSpeed * Time.deltaTime); } } //Snap transition else { if(moveOnPoints) { previewObj.transform.position = placementCheckPosition - new Vector3(selectedCellSize * 0.5f, 0, selectedCellSize * 0.5f) + new Vector3(0, previewObjHoverDistance, 0); } else { previewObj.transform.position = placementCheckPosition + new Vector3(0, previewObjHoverDistance, 0); } } } //Drag and drop build if(Input.GetMouseButtonUp(0)) { if (!dragMove && objectPlacer.DragAndDrop && dragging) { //Ensures if drag and drop is selected, it will not interfere with moving if (previewObj && !previewObj.GetComponent()) { //Checks to see if an object has been passed to the grid selector to be placed if (selectedGameObjectToPlace != null) { currentGrid = hit.collider.GetComponent(); PlaceObjectIfEmpty(currentGrid); DeselectPreview(); } } } } //Single and multiple click build if (Input.GetMouseButtonDown(0)) { if (!dragMove) { //Checks to see if an object has been passed to the grid selector to be placed if (selectedGameObjectToPlace != null) { currentGrid = hit.collider.GetComponent(); PlaceObjectIfEmpty(currentGrid); } } if(objectPlacer && objectPlacer.PlaceOne) { DeselectPreview(); } } //Drag build if (objectPlacer.DragBuild) { if (!dragMove) { if (Input.GetMouseButton(0)) { if (selectedGameObjectToPlace != null) { currentGrid = hit.collider.GetComponent(); PlaceObjectIfEmpty(currentGrid); } } } } //Rotate preview object if(rotation && previewObj) { if(Input.GetKeyDown(KeyCode.R)) { previewObjOffset = Vector3.zero; if (rotate == 270) { rotate = 0; } else { rotate += 90; } //Smooth rotation if (smoothRotate) { rotating = true; } //Instant rotation else { previewObj.transform.rotation = Quaternion.Euler(0, rotate, 0); } ChangeCheckedPositions(); UpdatePreviewObjOffset(); ShowHideSelectorAndPreview(); if (objectSelector && objectSelector.SelectorCanvas) { objectSelector.GetObjectWidthHeight(GetObjectSize(objectSize)); objectSelector.AdjustCanvasImage(); } } //This performs the rotation after pressing 'R' if using smooth rotate if (rotating) { /*rotateParent.transform.rotation = Quaternion.RotateTowards (rotateParent.transform.rotation, Quaternion.Euler(0, rotate, 0), rotateSpeed * Time.deltaTime);*/ previewObj.transform.rotation = Quaternion.RotateTowards (previewObj.transform.rotation, Quaternion.Euler(0, rotate, 0), rotateSpeed * Time.deltaTime); if (previewObj.transform.eulerAngles.y == rotate) { rotating = false; } } } } //Assign the nearest position on the grid to the grid selector object if (transform.position != gridSelectorPos) { if (smoothMove) { //Snaps if hitting the grid for the first time so selector does not remember its old position if (!snap) { transform.position = gridSelectorPos; if (objectSelector) { if (previewObj) { //This will snap the object selector to the new position if (objectSelector && objectSelector.SelectorCanvas) { objectSelector.SelectorCanvas.transform.position = (placementCheckPosition + previewObjOffset) + new Vector3(0, objectSelector.SelectorCanvasHeight, 0); } } else { //This will snap the object selector to the new position if (objectSelector && objectSelector.SelectorCanvas) { objectSelector.SelectorCanvas.transform.position = placementCheckPosition + new Vector3(0, objectSelector.SelectorCanvasHeight, 0); } } } ShowHideSelectorAndPreview(); snap = true; } else { //This will move towards the new position at the given moveSpeed inspector value transform.position = Vector3.MoveTowards(transform.position, gridSelectorPos, moveSpeed * Time.deltaTime); } } else { //This will snap the grid selector to the new position transform.position = gridSelectorPos; } } //This makes sure the objectSelector canvas is always in the correct position if (objectSelector && objectSelector.SelectorCanvas) { if (transform.position.x != objectSelector.SelectorCanvas.transform.position.x || transform.position.z != objectSelector.SelectorCanvas.transform.position.z) { if (!Input.GetMouseButtonDown(1) || !Input.GetKeyDown(KeyCode.Escape)) { MoveSelectorCanvas(); } } } //Shows/Hides the selector tile depending if it is a placeable tile each time selector changes position //This only runs each time you are on a different cell if (gridSelectorPos != currentPos) { ShowHideSelectorAndPreview(); } } //Doesnt hit the GridSquare else { HideGridSelectorAndPreview(); snap = false; } //Edge cases to hide and show the selector canvas and the preview obj if (hit.collider.GetComponent() || hit.collider.GetComponent()) { if(previewObj && hit.collider.GetComponent()) { if(showInvalidPreviewObj) { previewObj.SetActive(true); } if (objectSelector && objectSelector.SelectorCanvas) { objectSelector.SelectorCanvas.gameObject.SetActive(false); } } } else { if (objectSelector && objectSelector.SelectorCanvas) { objectSelector.SelectorCanvas.gameObject.SetActive(false); } } } //Doesnt hit anything at all else { //Hits nothing so tells other functions to do nothing hitSomething = false; snap = false; //If the user isnt hovered on anything, hide the selector and any preview object HideGridSelectorAndPreview(); if (objectSelector && objectSelector.SelectorCanvas) { objectSelector.HideCanvas(); } } //Manually unselect and delete the preview if (Input.GetMouseButtonDown(1) || Input.GetKeyDown(KeyCode.Escape)) { if (previewObj && previewObj.GetComponent()) { if(gridObjectOptions && gridObjectOptions.Moving) { ReturnObjectToOriginalPlace(); } } else { DeselectPreview(); } if (objectSelector) { objectSelector.ShowCanvas(); } } } private void MoveSelectorCanvas() { if (smoothMove) { if (moveOnPoints) { //Moves selector canvas on the placementcheckpos + the previewobjoffset objectSelector.SelectorCanvas.transform.position = Vector3.MoveTowards(objectSelector.SelectorCanvas.transform.position, (placementCheckPosition + previewObjOffset) + new Vector3(0, objectSelector.SelectorCanvasHeight, 0), moveSpeed * Time.deltaTime); } else { if (previewObj) { //Moves selector canvas on the placementcheckpos + the previewobjoffset objectSelector.SelectorCanvas.transform.position = Vector3.MoveTowards(objectSelector.SelectorCanvas.transform.position, (placementCheckPosition + previewObjOffset) + new Vector3(0, objectSelector.SelectorCanvasHeight, 0), moveSpeed * Time.deltaTime); } else { //Moves on middle of cells without preview obj objectSelector.SelectorCanvas.transform.position = Vector3.MoveTowards(objectSelector.SelectorCanvas.transform.position, placementCheckPosition + new Vector3(0, objectSelector.SelectorCanvasHeight, 0), moveSpeed * Time.deltaTime); } } } else { if (gridSelectorPos != currentPos) { if (moveOnPoints) { //This will move towards the new position objectSelector.SelectorCanvas.transform.position = (placementCheckPosition + previewObjOffset) + new Vector3(0, objectSelector.SelectorCanvasHeight, 0); } else { if (previewObj) { //This will snap the object selector to the new position objectSelector.SelectorCanvas.transform.position = placementCheckPosition + previewObjOffset + new Vector3(0, objectSelector.SelectorCanvasHeight, 0); } else { //This will snap the object selector to the new position objectSelector.SelectorCanvas.transform.position = placementCheckPosition + new Vector3(0, objectSelector.SelectorCanvasHeight, 0); } } } } } //Hides the preview object and this objects mesh renderer //Also resets the currentPos to force a recheck on gridSquare entry private void HideGridSelectorAndPreview() { if (previewObj) { previewObj.SetActive(false); } meshRenderer.enabled = false; //Will hide the selector canvas if not hitting a grid object if(objectSelector && objectSelector.SelectorCanvas) { if (hit.collider != null && !hit.collider.GetComponent()) { if (gridObjectOptions && gridObjectOptions.MenuCanvas) { if(!gridObjectOptions.MenuCanvas.activeSelf) { objectSelector.HideCanvas(); } } else { objectSelector.HideCanvas(); } } } //Sets the current position to something else other than newPos forcing the Selector to recheck if valid cell currentPos = Vector3.zero; } //This function calculates an offset based on your SelectObject.ObjectSize 5x5 grid selection //to try to best position the object for rotation private void UpdatePreviewObjOffset() { float cellSizeOffset = selectedCellSize / 2; if (previewObj && objectSize != null) { //Inner ring //Top row if (objectSize.pos_m1_p1 || objectSize.pos_0_p1 || objectSize.pos_p1_p1) { previewObjOffset += new Vector3(0, 0, cellSizeOffset); } //Right column if (objectSize.pos_p1_p1 || objectSize.pos_p1_0 || objectSize.pos_p1_m1) { previewObjOffset += new Vector3(cellSizeOffset, 0, 0); } //Bottom row if (objectSize.pos_p1_m1 || objectSize.pos_0_m1 || objectSize.pos_p1_m1) { previewObjOffset += new Vector3(0, 0, -cellSizeOffset); } //Left column if (objectSize.pos_m1_m1 || objectSize.pos_m1_0 || objectSize.pos_m1_p1) { previewObjOffset += new Vector3(-cellSizeOffset, 0, 0); } //Outer ring //Top row if (objectSize.pos_m2_p2 || objectSize.pos_m1_p2 || objectSize.pos_0_p2 || objectSize.pos_p1_p2 || objectSize.pos_p2_p2) { previewObjOffset += new Vector3(0, 0, cellSizeOffset); } //Right column if (objectSize.pos_p2_p2 || objectSize.pos_p2_p1 || objectSize.pos_p2_0 || objectSize.pos_p2_m1 || objectSize.pos_p2_m2) { previewObjOffset += new Vector3(cellSizeOffset, 0, 0); } //Bottom row if (objectSize.pos_m2_m2 || objectSize.pos_m1_m2 || objectSize.pos_0_m2 || objectSize.pos_p1_m2 || objectSize.pos_p2_m2) { previewObjOffset += new Vector3(0, 0, -cellSizeOffset); } //Left column if (objectSize.pos_m2_p2 || objectSize.pos_m2_p1 || objectSize.pos_m2_0 || objectSize.pos_m2_m1 || objectSize.pos_m2_m2) { previewObjOffset += new Vector3(-cellSizeOffset, 0, 0); } } } //This will update the boolean values when using rotate, as different cells will now need to be checked private void ChangeCheckedPositions() { if (objectSize != null) { //Temp variables //Inner ring bool temptl = objectSize.pos_m1_p1; bool temptc = objectSize.pos_0_p1; bool temptr = objectSize.pos_p1_p1; bool tempcl = objectSize.pos_m1_0; bool tempcr = objectSize.pos_p1_0; bool tempbl = objectSize.pos_m1_m1; bool tempbc = objectSize.pos_0_m1; bool tempbr = objectSize.pos_p1_m1; //Outer ring bool tempm2_p2 = objectSize.pos_m2_p2; bool tempm1_p2 = objectSize.pos_m1_p2; bool temp0_p2 = objectSize.pos_0_p2; bool tempp1_p2 = objectSize.pos_p1_p2; bool tempp2_p2 = objectSize.pos_p2_p2; bool tempp2_p1 = objectSize.pos_p2_p1; bool tempp2_0 = objectSize.pos_p2_0; bool tempp2_m1 = objectSize.pos_p2_m1; bool tempp2_m2 = objectSize.pos_p2_m2; bool tempp1_m2 = objectSize.pos_p1_m2; bool temp0_m2 = objectSize.pos_0_m2; bool tempm1_m2 = objectSize.pos_m1_m2; bool tempm2_m2 = objectSize.pos_m2_m2; bool tempm2_m1 = objectSize.pos_m2_m1; bool tempm2_0 = objectSize.pos_m2_0; bool tempm2_p1 = objectSize.pos_m2_p1; if (!moveOnPoints) { //Reassign to rotate locations objectSize.pos_m1_p1 = tempbl; objectSize.pos_p1_p1 = temptl; objectSize.pos_p1_m1 = temptr; objectSize.pos_m1_m1 = tempbr; objectSize.pos_0_p1 = tempcl; objectSize.pos_p1_0 = temptc; objectSize.pos_0_m1 = tempcr; objectSize.pos_m1_0 = tempbc; //Reassign to rotate locations objectSize.pos_m2_p2 = tempm2_m2; objectSize.pos_m1_p2 = tempm2_m1; objectSize.pos_0_p2 = tempm2_0; objectSize.pos_p1_p2 = tempm2_p1; objectSize.pos_p2_p2 = tempm2_p2; objectSize.pos_p2_p1 = tempm1_p2; objectSize.pos_p2_0 = temp0_p2; objectSize.pos_p2_m1 = tempp1_p2; objectSize.pos_p2_m2 = tempp2_p2; objectSize.pos_p1_m2 = tempp2_p1; objectSize.pos_0_m2 = tempp2_0; objectSize.pos_m1_m2 = tempp2_m1; objectSize.pos_m2_m2 = tempp2_m2; objectSize.pos_m2_m1 = tempp1_m2; objectSize.pos_m2_0 = temp0_m2; objectSize.pos_m2_p1 = tempm1_m2; } else { //Reassign to rotate locations objectSize.pos_m1_p1 = tempm2_m1; objectSize.pos_p1_p1 = tempm2_p1; objectSize.pos_p1_m1 = temptc; objectSize.pos_m1_m1 = tempbc; objectSize.pos_0_p1 = tempm2_0; objectSize.pos_p1_0 = temptl; objectSize.pos_m1_0 = tempbl; objectSize.pos_m2_p2 = tempp2_p2; objectSize.pos_p2_p1 = tempm2_p2; objectSize.pos_p2_0 = tempm1_p2; objectSize.pos_p2_m1 = temp0_p2; objectSize.pos_p2_m2 = tempp2_p2; objectSize.pos_p1_m2 = temptr; objectSize.pos_0_m2 = tempcr; objectSize.pos_m1_m2 = tempbr; objectSize.pos_m2_m2 = tempp1_m2; objectSize.pos_m2_m1 = temp0_m2; objectSize.pos_m2_0 = tempm1_m2; objectSize.pos_m2_p1 = tempm2_m2; } } } //Calculates if the selector and the preview should be hidden or not and use of the invalid feedback options public void ShowHideSelectorAndPreview() { //Gets the grid that the selector is currently hovering over if(hitSomething && hit.collider.GetComponent()) { currentGrid = hit.collider.GetComponent(); } if(currentGrid == null) { return; } //If there is a grid if (currentGrid && currentGrid.Created) { //Can place in these cells //Performs multiple checks on each cell that requires space if (CheckCellsAreEmpty(currentGrid)) { //These cells are clear, so show the grid selector mesh and change its material to its assigned one ChangeObjMat(meshRenderer.gameObject, selectorMat); meshRenderer.enabled = true; if(objectSelector && objectSelector.SelectorCanvas) { objectSelector.ShowCanvas(); } if (previewObj) { if(hideSelectorOnPreview) { meshRenderer.enabled = false; } if (objectSelector && objectSelector.SelectorCanvas && objectSelector.HideCanvasOnPreview) { objectSelector.HideCanvas(); } //Changes the preview objects material to the one assigned, otherwise it will use its own material if (objPreviewMat) { ChangeObjMat(previewObj, objPreviewMat); } else { ReapplyOriginalMats(); } previewObj.SetActive(true); } } //Cannot place in these cells else { //Shows the selector and the preview obj if invalid and changes material if (invalidPlacementFeedback) { //Changes the grid selector tile ChangeObjMat(meshRenderer.gameObject, invalidPlacementMat); meshRenderer.enabled = true; if (objectSelector && objectSelector.SelectorCanvas && !objectSelector.HideCanvasOnPreview) { objectSelector.ShowCanvas(); } if (previewObj) { if (showInvalidPreviewObj) { if (invalidPlacementMat) { ChangeObjMat(previewObj, invalidPlacementMat); } else { ReapplyOriginalMats(); } } } } //Hides the selector and the preview obj if invalid else { meshRenderer.enabled = false; if (objectSelector && objectSelector.SelectorCanvas) { objectSelector.HideCanvas(); } } //Shows/hides the preview obj if invalid if (previewObj) { if (hideSelectorOnPreview) { meshRenderer.enabled = false; } if (invalidPlacementFeedback && showInvalidPreviewObj) { previewObj.SetActive(true); } else { previewObj.SetActive(false); if (objectSelector && objectSelector.SelectorCanvas && !objectSelector.HideCanvasOnPreview) { objectSelector.HideCanvas(); } } } } } //This makes sure this function gets called every time the cell position changes currentPos = gridSelectorPos; } //This checks every cell that is used by SelectObject.ObjectSize on the placeable objects private bool CheckCellsAreEmpty(GridSquare currentGrid) { //Get the rest of the check positions GetObjectSizeCheckPositions(); //Starts by saying that the cells are false/full bool allEmpty = false; //If overwrite is on, it does not matter if a cell is clear, only that it is on the grid if (objectPlacer && objectPlacer.Overwrite && !dragBuild) { if (selectedGameObjectToPlace) { //All position booleans added to a List List checkPosList = new List(); int j = 0; if (objectSize != null) { //Loop over each objectSize boolean foreach (System.Reflection.FieldInfo prop in objectSize.GetType().GetFields()) { //If true if ((bool)prop.GetValue(objectSize)) { //Add it to the list checkPosList.Add((bool)prop.GetValue(objectSize)); //Check the bool using the objectSizeCheckPos array created in function GetObjectSizePositions if (currentGrid.CheckIfOnGrid(gridCheckPositions[j])) { return allEmpty; } } j++; } } } //Checks the center position if (currentGrid.CheckIfOnGrid(placementCheckPosition)) { return allEmpty; } } else { if (selectedGameObjectToPlace) { //All position booleans added to a List List checkPosList = new List(); int j = 0; if (objectSize != null) { //Loop over each objectSize boolean foreach (System.Reflection.FieldInfo prop in objectSize.GetType().GetFields()) { //If true if ((bool)prop.GetValue(objectSize)) { //Add it to the list checkPosList.Add((bool)prop.GetValue(objectSize)); //Check the bool using the objectSizeCheckPos array created in function GetObjectSizePositions if (!currentGrid.CheckCellStatus(gridCheckPositions[j])) { return allEmpty; } } j++; } } } //Checks the center position if (!currentGrid.CheckCellStatus(placementCheckPosition)) { return allEmpty; } } //After it has checked every cell that needs to be checked, if none of them return out of this function, //then the cells must be clear, so this function returns true, and the object is placeable allEmpty = true; return allEmpty; } //Passes on data to the ObjectPlacer after checking the cell status public void PlaceObjectIfEmpty(GridSquare currentGrid) { //Checks the cells if (CheckCellsAreEmpty(currentGrid) && !EventSystem.current.IsPointerOverGameObject()) { GetObjectSizeCheckPositions(); if (objectPlacer) { //If you have overwrite enabled and dragbuild is not the placement type if(objectPlacer.Overwrite && !dragBuild) { //Init new list each time GORemoveList = new List(); int j = 0; if (objectSize != null) { //Loop over each objectSize boolean foreach (System.Reflection.FieldInfo prop in objectSize.GetType().GetFields()) { //If true if ((bool)prop.GetValue(objectSize)) { //Check the bool using the objectSizeCheckPos array created in function GetObjectSizePositions if (!currentGrid.CheckIfOnGrid(gridCheckPositions[j])) { //Get object on cell if it exits GameObject obj = currentGrid.GetGridObject(gridCheckPositions[j]); //Check to see if it is already in the list and the obj is not null if (obj != null && !GORemoveList.Contains(obj)) { //Add it to the list GORemoveList.Add(currentGrid.GetGridObject(gridCheckPositions[j])); } } } j++; } } //Checks the center position and does the same as above if (!currentGrid.CheckIfOnGrid(placementCheckPosition)) { GameObject obj = currentGrid.GetGridObject(placementCheckPosition); if (obj != null && !GORemoveList.Contains(obj)) { GORemoveList.Add(currentGrid.GetGridObject(placementCheckPosition)); } } //For every object in the list (in the way), remove it for (int i = 0; i < GORemoveList.Count; i++) { if(objectRemover) { objectRemover.RemoveObject(GORemoveList[i], GORemoveList[i].GetComponent().data); } } //Reselect the preview object if (objectSelector && objectSelector.SelectorCanvas) { SetupCanvasOnPreview(previewObj, objectSize); } } //These will be the blocked positions after placing the object List checkPositions = objectPlacer.GetSpecificCheckPositions(gridCheckPositions, objectSize); //Builds the GridObject to send and apply on the ObjectPlacer GridObject.Data gridObject = BuildGridObject(currentGrid, checkPositions); //Passes the GridObject plus the layer if (previewObj && previewObj.activeSelf) { GameObject objInstance; //Builds new object if (!previewObj.GetComponent()) { //Generate a new instance ID for the object gridObject.InstanceId = Guid.NewGuid().ToString(); //Creates a new object if (buildTime <= 0) { objInstance = objectPlacer.PlaceObject(selectedGameObjectToPlace, gridObject, startingLayer); objectPlacer.GetSpecificCheckPositions(gridCheckPositions, objectSize); } //Delay place an object else { objInstance = objectPlacer.DelayBuildStart(selectedGameObjectToPlace, gridObject, startingLayer, buildTime); objectPlacer.GetSpecificCheckPositions(gridCheckPositions, objectSize); } if(previewObjFloorTiles && placeTilesWithObject) { GameObject floorTiles = Instantiate(previewObj.transform.Find("FloorTileParent").gameObject); floorTiles.transform.position = objInstance.transform.position; floorTiles.transform.rotation = Quaternion.Euler(0, rotate, 0); floorTiles.transform.parent = objInstance.transform; floorTiles.name = "FloorTileParent"; } } //Moves an already placed object else { if(previewObjFloorTiles) { if (!placeTilesWithObject) { Destroy(previewObj.transform.Find("FloorTileParent").gameObject); } } objectPlacer.MoveObject(selectedGameObjectToPlace, gridObject, startingLayer); //Makes the instance the same object as the moved object as we are only moving and not creating objInstance = selectedGameObjectToPlace; //Reenable the animation on the moved object enableAnimation(); //Resets everything after moving an object previewObj = null; DeselectPreview(); } //Blocks the cells the objectSize takes up objectPlacer.BlockCells(checkPositions, objInstance, currentGrid); } else { if (dragMove) { ReturnObjectToOriginalPlace(); } } if (objectPlacer.HideCellsUnderPlacedObj) { //Calls the function to check if the cells need to be hidden under the object objectPlacer.HidePlacedObjCells(currentGrid, checkPositions); } } else { Debug.Log("To place objects you must have ObjectPlacer component"); } } else { if(dragMove) { ReturnObjectToOriginalPlace(); } } } //This builds a new GridObject with various variables for the objects data private GridObject.Data BuildGridObject(GridSquare currentGrid, List blockedCells) { GridObject.Data gridObject = new GridObject.Data(); gridObject.ObjName = selectedGameObjectToPlace.name; gridObject.PrefabId = prefabId; gridObject.InstanceId = instanceId; gridObject.Position = placementCheckPosition; gridObject.CheckPositions = blockedCells; gridObject.Offset = previewObjOffset; gridObject.OriginalOffset = originalOffset; gridObject.Rotation = rotate; gridObject.GridSquare = currentGrid; gridObject.ObjSize = objectSize; gridObject.MoveOnPoints = moveOnPoints; if (selectedGameObjectToPlace && selectedGameObjectToPlace.GetComponent()) { gridObject.UpgradeLevel = selectedGameObjectToPlace.GetComponent().CurrentUpgradeLevel; } else { gridObject.UpgradeLevel = 0; } return gridObject; } //If right clicking or pressing escape when moving an already placed object, //this will return the object to its previous position and rotation private void ReturnObjectToOriginalPlace() { List blockedCells = new List(); //Gets the objects GridObject data var objData = previewObj.GetComponent().data; blockedCells.Add(objData.Position); //Blocks the center position of the objects previous position objData.GridSquare.ChangeCellStatus(objData.Position, previewObj); //Loop through all blocked positions foreach(var block in objData.CheckPositions) { //Blocks all previously blocked positions except the center objData.GridSquare.ChangeCellStatus(block, previewObj); blockedCells.Add(block); } objectPlacer.MoveObject(selectedGameObjectToPlace, objData, startingLayer); //Resets previewObj.SetActive(true); rotate = (int)objData.Rotation; selectedGameObjectToPlace.GetComponent().data.ObjSize = selectedGameObjectToPlace.GetComponent().objSize; //Deselects the selected object when moved if(objectSelector) { objectSelector.DeselectObject(); if(objectSelector.SelectorCanvas) { objectSelector.ResetCanvas(); } } enableAnimation(); previewObj = null; selectedGameObjectToPlace = null; //Calls the function to check if the cells need to be hidden under the object if (objectPlacer) { if(objectPlacer.HideCellsUnderPlacedObj) { objectPlacer.HidePlacedObjCells(objData.GridSquare, blockedCells); } } //Resets moveOnPoints after replacing so the center of each cell is now selected if(moveOnPoints) { moveOnPoints = false; } previewObjOffset = Vector3.zero; } //Creates a visible preview of the object you want to place, moves with the cursor, and changes the material if applied private void CreatePreviewObject() { if(selectedGameObjectToPlace == null) { return; } //If the object has already been placed and is being moved if (selectedGameObjectToPlace.GetComponent()) { previewObj = SelectedGameObjectToPlace; IOnBuildingMoveStart[] onPlacedList = previewObj.GetComponents(); for (int i = 0; i < onPlacedList.Length; i++) { onPlacedList[i].OnMoveStart(previewObj); } //Set the new placementPosition as the position of the placed object placementCheckPosition = previewObj.GetComponent().data.Position; if (previewObjFloorTiles && !placeTilesWithObject) { CreatePreviewObjFloorTiles(); } } //If you are selecting a new object to place else { //Makes a new object at the grid selector position previewObj = Instantiate(selectedGameObjectToPlace, placementCheckPosition, Quaternion.identity); if (selectedGameObjectToPlace.GetComponent()) { UpgradeData upgradeData = selectedGameObjectToPlace.GetComponent(); //Only change preview mesh if it higher than level 1 and there is a prefab if (upgradeData.CurrentUpgradeLevel > 1 && upgradeData.UpgradePrefabs[upgradeData.CurrentUpgradeLevel - 2]) { //Changes mesh of prefab previewObj.GetComponent().sharedMesh = upgradeData.UpgradePrefabs[upgradeData.CurrentUpgradeLevel - 2].GetComponent().sharedMesh; previewObj.GetComponent().sharedMaterials = upgradeData.UpgradePrefabs[upgradeData.CurrentUpgradeLevel - 2].GetComponent().sharedMaterials; } } //Assigns this new building to object size objectSize = previewObj.GetComponent().objSize; if (previewObjFloorTiles) { CreatePreviewObjFloorTiles(); } previewObj.name = "PreviewObject"; //Resets the offset so UpdatePreviewObjOffset is off to a fresh start previewObjOffset = Vector3.zero; //If you are not using rotate, the prefabs rotation will be used if (!rotation) { previewObj.transform.rotation = Quaternion.Euler(0, selectedGameObjectToPlace.transform.rotation.eulerAngles.y, 0); } //Decides which offset to apply UpdatePreviewObjOffset(); //Keeps the original offset, to send to the object placer, to store as GridObject data originalOffset = previewObjOffset; } previewObj.transform.position = placementCheckPosition; if (!objPreviewMat || !invalidPlacementMat) { StoreOriginalMats(); } //Pauses animation on the preview object until placed if(disableAnimationForPreviewObj) { disableAnimation(); } //Preview objects have their layer set to ignoreraycast so the gridSelector still goes through to the grid collider ChangePreviewObjLayer(previewObj, 2); IOnSetPreview[] onPreviewList = previewObj.GetComponents(); for (int i = 0; i < onPreviewList.Length; i++) { onPreviewList[i].OnPreview(previewObj); } ShowHideSelectorAndPreview(); } private void CreatePreviewObjFloorTiles() { //Gets the initial positions for the object GetObjectSizeCheckPositions(); if(previewObj) { GameObject tileParent = new GameObject("FloorTileParent"); tileParent.transform.position = previewObj.transform.position; tileParent.transform.parent = previewObj.transform; List objCellPositions = GetObjectSize(objectSize); if(moveOnPoints) { foreach (Vector3 pos in objCellPositions) { GameObject tile = Instantiate(previewObjFloorTilePrefab, pos + new Vector3(selectedCellSize * 0.5f, hoverDistance, selectedCellSize * 0.5f), Quaternion.identity, tileParent.transform); tile.name = "previewFloorTile"; } } else { foreach (Vector3 pos in objCellPositions) { GameObject tile = Instantiate(previewObjFloorTilePrefab, pos + new Vector3(0, hoverDistance, 0), Quaternion.identity, tileParent.transform); tile.name = "previewFloorTile"; } } } } private void StoreOriginalMats() { //Assign children all child objects and parent of previewObj that contain a Renderer children = previewObj.GetComponentsInChildren(); //New object if (!previewObj.GetComponent()) { previewObjOwnMats = new Material[children.Length]; //Loops over and stores the original materials to reapply whenever needed for (int i = 0; i < children.Length; i++) { previewObjOwnMats[i] = children[i].sharedMaterial; } } //Moved object else { //Gets the original materials as they are stored on the gridObject class previewObjOwnMats = previewObj.GetComponent().OwnMats; } } //Reapply original materials to all child objects private void ReapplyOriginalMats() { for (int i = 0; i < children.Length; i++) { children[i].sharedMaterial = previewObjOwnMats[i]; } } //Stops the animation of a preview obj private void disableAnimation() { if(previewObj && previewObj.GetComponent()) { previewObj.GetComponent().enabled = false; } if(previewObj && previewObj.GetComponentInChildren()) { previewObj.GetComponentInChildren().gameObject.SetActive(false); } } //Resumes the aniamtion of a preview obj private void enableAnimation() { if (previewObj && previewObj.GetComponent()) { previewObj.GetComponent().enabled = true; } if (previewObj && previewObj.GetComponentInChildren(true)) { previewObj.GetComponentInChildren(true).gameObject.SetActive(true); } } //Cycles through the given obj and all of its children, changing every material on each one to the given newMat public void ChangeObjMat(GameObject obj, Material newMat) { if (!changeMatColorNotMat && newMat) { Renderer[] children; children = obj.GetComponentsInChildren(); if(usePreviewMatsForFloorTiles) { foreach (Renderer rend in children) { if (!rend.gameObject.GetComponent()) { Material[] mats = new Material[rend.materials.Length]; for (int i = 0; i < rend.materials.Length; i++) { mats[i] = newMat; } rend.materials = mats; } } } else { foreach (Renderer rend in children) { if (!rend.gameObject.GetComponent() && rend.gameObject.name != "previewFloorTile") { Material[] mats = new Material[rend.materials.Length]; for (int i = 0; i < rend.materials.Length; i++) { mats[i] = newMat; } rend.materials = mats; } } } } else { if (newMat) { Renderer[] matChildren; matChildren = obj.GetComponentsInChildren(); foreach (Renderer rend in matChildren) { Material[] mats = rend.materials; for (int i = 0; i < rend.materials.Length; i++) { mats[i].SetColor("_Color", newMat.GetColor("_Color")); //newMat; } rend.materials = mats; } } } } //Changes the given obj and all of its children to the given newLayer public void ChangePreviewObjLayer(GameObject obj, int newLayer) { obj.layer = newLayer; Transform[] objChildren = obj.GetComponentsInChildren(); foreach (Transform child in objChildren) { child.gameObject.layer = newLayer; } } //This is the core of the grid selector, this finds the center of each cell that the curser is inside //This also goes on to find the positions for each true boolean in SelectObject.ObjectSize such as topLeft etc private void GetCellPositions() { float newXPos; float newZPos; if(moveOnPoints) { //Finds the center of the cell you are inside, on each axis newXPos = (RoundTo((hit.point.x - hit.transform.position.x), selectedCellSize)); //float newYPos = (RoundTo((hit.point.y - hit.transform.position.y), selectedCellSize) - selectedCellSize / 2); newZPos = (RoundTo((hit.point.z - hit.transform.position.z), selectedCellSize)); placementCheckPosition = new Vector3(newXPos + (selectedCellSize * 0.5f), 0, newZPos + (selectedCellSize * 0.5f)) + hit.transform.position; } else { //Finds the center of the cell you are inside, on each axis newXPos = (RoundTo((hit.point.x - hit.transform.position.x), selectedCellSize) - selectedCellSize * 0.5f); //float newYPos = (RoundTo((hit.point.y - hit.transform.position.y), selectedCellSize) - selectedCellSize / 2); newZPos = (RoundTo((hit.point.z - hit.transform.position.z), selectedCellSize) - selectedCellSize * 0.5f); placementCheckPosition = new Vector3(newXPos, 0, newZPos) + hit.transform.position; } //Assigns the new position gridSelectorPos = new Vector3(newXPos, hoverDistance, newZPos) + hit.transform.position; //Trims the vector3 to 3 decimal places to fix rounding errors gridSelectorPos = TrimToThreeDP(gridSelectorPos); placementCheckPosition = TrimToThreeDP(placementCheckPosition); } //Works out all other positions from the gridSelectorPos //These are check positions so do not include the hoverDistance from the grid selector private void GetObjectSizeCheckPositions() { if (selectedGameObjectToPlace) { int i = 0; //1st row positions******************************************************** //pos_m2_p2 calcPos(-selectedCellSize * 2, +selectedCellSize * 2); //pos_m1_p2 calcPos(-selectedCellSize, +selectedCellSize * 2); //pos_0_p2 calcPos(0, +selectedCellSize * 2); //pos_p1_p2 calcPos(+selectedCellSize, +selectedCellSize * 2); //pos_p2_p2 calcPos(+selectedCellSize * 2, +selectedCellSize * 2); //2nd row positions******************************************************** //pos_m2_p1 calcPos(-selectedCellSize * 2, +selectedCellSize); //pos_m1_p1 calcPos(-selectedCellSize, +selectedCellSize); //pos_0_p1 calcPos(0, +selectedCellSize); //pos_p1_p1 calcPos(+selectedCellSize, +selectedCellSize); //pos_p2_p1 calcPos(+selectedCellSize * 2, +selectedCellSize); //3rd row positions******************************************************** //pos_m2_0 calcPos(-selectedCellSize * 2, 0); //pos_m1_0 calcPos(-selectedCellSize, 0); //Pos_0_0 gridCheckPositions[i] = placementCheckPosition; i++; //pos_p1_0 calcPos(+selectedCellSize, 0); //pos_p2_0 calcPos(+selectedCellSize * 2, 0); //4th row positions******************************************************** //pos_m2_m1 calcPos(-selectedCellSize * 2, -selectedCellSize); //pos_m1_m1 calcPos(-selectedCellSize, -selectedCellSize); //pos_0_m1 calcPos(0, -selectedCellSize); //pos_p1_m1 calcPos(+selectedCellSize, -selectedCellSize); //pos_p2_m1 calcPos(+selectedCellSize * 2, -selectedCellSize); //5th row positions******************************************************** //pos_m2_m2 calcPos(-selectedCellSize * 2, -selectedCellSize * 2); //pos_m1_m2 calcPos(-selectedCellSize, -selectedCellSize * 2); //pos_0_m2 calcPos(0, -selectedCellSize * 2); //pos_p1_m2 calcPos(+selectedCellSize, -selectedCellSize * 2); //pos_p2_m2 calcPos(+selectedCellSize * 2, -selectedCellSize * 2); Vector3 calcPos(float x, float z) { Vector3 pos; pos = placementCheckPosition + new Vector3(x, 0, z); gridCheckPositions[i] = TrimToThreeDP(pos); pos = gridCheckPositions[i]; i++; return pos; } } } //Takes a vector3 and then parses a string trimmed to 3 decimal places for x, y and z back into a vector3 private Vector3 TrimToThreeDP(Vector3 num) { num.x = float.Parse(num.x.ToString("F3")); num.y = float.Parse(num.y.ToString("F3")); num.z = float.Parse(num.z.ToString("F3")); return num; } //Finds the middle point of our value float RoundTo(float value, float multipleOf) { float roundedNumber; if (moveOnPoints) { roundedNumber = Mathf.Round(value / multipleOf) * multipleOf; } else { roundedNumber = Mathf.Ceil(value / multipleOf) * multipleOf; } //Prevents rounding up above grid width or height if (roundedNumber > gridsquare.GridHeight|| roundedNumber > gridsquare.GridWidth) { value -= 0.01f; } //Prevents the 0 rounding becoming negative number if(value == 0) { value += 0.01f; } if (moveOnPoints) { roundedNumber = Mathf.Round(value / multipleOf) * multipleOf; } else { roundedNumber = Mathf.Ceil(value / multipleOf) * multipleOf; } return roundedNumber; } //This starts the whole object placement procedure by assigning the selectedGameObjectToPlace a Building public void SetGameObjectToPlace(Building building) { //Check to see if to use objectSelector or not if(objectSelector && objectSelector.HideCanvasOnPreview) { objectSelector.HideCanvas(); } selectedGameObjectToPlace = building.gameObject; startingLayer = selectedGameObjectToPlace.layer; prefabId = building.PrefabId; if(building.gameObject.GetComponent()) { GridObject.Data gridObjData = building.gameObject.GetComponent().data; this.instanceId = gridObjData.InstanceId; building.objSize = CreateObjectSizeInstance(gridObjData); objectSize = gridObjData.ObjSize; } else { //objectSize = building.objSize; this.instanceId = null; } this.moveOnPoints = building.MoveOnPoints; this.buildTime = building.BuildTime; CreatePreviewObject(); //This is saying that the GameObject given does not contain a GridObject component, //therefor it is a new object and not an already placed one if (!building.gameObject.GetComponent()) { rotate = 0; } //This is an already placed object, we are just moving it else { UpdatePreviewObjOffset(); rotate = (int)building.gameObject.GetComponent().data.Rotation; originalOffset = building.gameObject.GetComponent().data.OriginalOffset; } if (objectSelector && objectSelector.SelectorCanvas) { if (building.gameObject.GetComponent()) { SetupCanvasOnPreview(building.gameObject, building.gameObject.GetComponent().data.ObjSize); } else { SetupCanvasOnPreview(building.gameObject, building.objSize); } } } //Creates a unique ObjectSize object with the correct booleans set to true public Building.ObjectSize CreateObjectSizeInstance(GridObject.Data gridObjectData) { Building.ObjectSize clonedObjSize = new Building.ObjectSize(); System.Reflection.FieldInfo[] clonedFields; clonedFields = clonedObjSize.GetType().GetFields(); int i = 0; if (gridObjectData.ObjSize != null) { foreach (System.Reflection.FieldInfo prop in gridObjectData.ObjSize.GetType().GetFields()) { clonedFields[i].SetValue(clonedObjSize, prop.GetValue(gridObjectData.ObjSize)); i++; } } return clonedObjSize; } private List GetObjectSize(Building.ObjectSize objSize) { List checkPositions = new List(); int i = 0; if (objSize != null) { foreach (System.Reflection.FieldInfo prop in objSize.GetType().GetFields()) { if ((bool)prop.GetValue(objSize)) { checkPositions.Add(gridCheckPositions[i]); } i++; } } return checkPositions; } //Removes any previewObj from the scene public void DeselectPreview() { selectedGameObjectToPlace = null; if(previewObj) { if (!previewObj.GetComponent()) { Destroy(previewObj); previewObj = null; } ShowHideSelectorAndPreview(); if (objectSelector && objectSelector.SelectorCanvas) { //This resets the selector position to the center of the cell objectSelector.SelectorCanvas.transform.position = placementCheckPosition + new Vector3(0, objectSelector.SelectorCanvasHeight, 0); snap = false; } } rotate = 0; moveOnPoints = false; previewObjOffset = Vector3.zero; if(objectSelector && objectSelector.SelectorCanvas) { objectSelector.ResetCanvas(); } } //This sets up the canvas to be on the preview object as soon as its created private void SetupCanvasOnPreview(GameObject obj, Building.ObjectSize objSize) { objectSelector.HoveredObject = obj; objectSelector.PreviewObj = true; objectSelector.GetObjectWidthHeight(GetObjectSize(objSize)); objectSelector.AdjustCanvasImage(); } //This may needed in future development private void DefaultPlacementPos() { placementCheckPosition = new Vector3(0 + (selectedCellSize * 0.5f), 0, 0 + (selectedCellSize * 0.5f)); placementCheckPosition = TrimToThreeDP(placementCheckPosition); GetObjectSizeCheckPositions(); } }