- Home /
Making Building Placement Buildings rotateable
Hey! So I have this script where you can place buildings and stuff but im not sure how to make it so when your trying to place it you can rotate it on a 90-degree scale so from 90 to 180 to 270 to 360. How can I do this? Here is my code: PS This is my BuildingManager script. If you need my BuildingList, BuildingColision or even the ButtonGUI just tell me :)
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// Build manager.
///
/// This Script is attached to a empty GameObject
/// </summary>
[System.Serializable]
public class Snapping
{
public bool snappingEnabled = true;
public float snapRadius = 2.0f;
public float snapMagin = 0.0f;
public SnapSides snapSides;
}
[System.Serializable]
public struct SnapSides
{
// based on objects facing direction
public bool left, right, top, bottom, front, back;
}
public class BuildManager: MonoBehaviour
{
public int SelectedBuilding;
private int LastSelectedBuilding;
public GameObject[] Building;
public List<BuildingList> buildings = new List<BuildingList>();
public string TerrainCollisionTag;
private bool ghostOn = false;
private GameObject ghost;
private BuildingCollision ghostCollision;
private bool isFlat;
public float maxSlopeHigh = 5f;
public bool isBuildingEnabled { get; private set; }
// Unused until fully implemented
public Snapping snapping = new Snapping();
void Start()
{
LastSelectedBuilding = SelectedBuilding;
isBuildingEnabled = false;
}
public void ActivateBuildingmode()
{
isBuildingEnabled = true;
}
public void DeactivateBuildingmode()
{
isBuildingEnabled = false;
}
public void SelectBuilding(int id)
{
if (id < Building.Length && id >= 0)
{
LastSelectedBuilding = SelectedBuilding;
SelectedBuilding = id;
}
}
void Update()
{
if (!isBuildingEnabled)
{
if (ghost != null)
{
Destroy(ghost);
ghostOn = false;
}
return;
}
Ray ray;
RaycastHit[] hit;
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
hit = Physics.RaycastAll(ray, Mathf.Infinity);
for (int i = 0; i < hit.Length; i++)
{
if (hit[i].collider.tag == TerrainCollisionTag)
{
if (SelectedBuilding != LastSelectedBuilding && ghost != null)
{
Destroy(ghost);
ghostOn = false;
LastSelectedBuilding = SelectedBuilding;
break;
}
if (!ghostOn)
{
ghost = (GameObject)Instantiate(Building[SelectedBuilding],
new Vector3(hit[i].point.x,
hit[i].point.y,
hit[i].point.z),
Quaternion.identity);
ghost.name = ghost.name.Replace("(Clone)", "(Ghost)");
ghost.layer = 2; //ignore raycast layer
ghostCollision = ghost.AddComponent<BuildingCollision>();
ghostOn = true;
}
if (ghost != null)
{
if (Input.GetMouseButtonDown(0) && !ghostCollision.Collided() && isFlat)
{
BuildingList bl = new BuildingList();
DestroyImmediate(ghost);
bl.buildingGameObject = (GameObject)Instantiate(Building[SelectedBuilding],
new Vector3(hit[i].point.x,
hit[i].point.y + Building[SelectedBuilding].GetComponent<Collider>().transform.localScale.y / 2,
hit[i].point.z),
Quaternion.identity);
string s = bl.buildingGameObject.name.Replace("(Clone)", "");
bl.buildingGameObject.name = s;
bl.buildingName = s;
bl.buildingGameObject.AddComponent<BuildingCollision>();
buildings.Add(bl);
ghostOn = false;
DeactivateBuildingmode();
break;
}
Vector3 ghostTargetPos = new Vector3(
hit[i].point.x,
hit[i].point.y + Building[SelectedBuilding].GetComponent<Collider>().transform.localScale.y / 2,
hit[i].point.z);
ghost.transform.position = ghostTargetPos;
isFlat = GroundFlat();
if (ghostCollision.Collided() || !isFlat)
{
ghost.GetComponent<Renderer>().material.CopyPropertiesFromMaterial(Building[SelectedBuilding].GetComponent<Renderer>().sharedMaterial);
ghost.GetComponent<Renderer>().material.color = new Color(
1f,
0f,
0f,
0.6f);
}
else if (!ghostCollision.Collided() && isFlat)
{
ghost.GetComponent<Renderer>().material.CopyPropertiesFromMaterial(Building[SelectedBuilding].GetComponent<Renderer>().sharedMaterial);
ghost.GetComponent<Renderer>().material.color = new Color(
0f,
1f,
0f,
0.6f);
}
}
}
}
}
private bool GroundFlat()
{
RaycastHit[] buildingSlopeHitULAll;
RaycastHit[] buildingSlopeHitURAll;
RaycastHit[] buildingSlopeHitDLAll;
RaycastHit[] buildingSlopeHitDRAll;
RaycastHit[] buildingSlopeHitMAll;
buildingSlopeHitULAll = Physics.RaycastAll(new Vector3(
ghost.GetComponent<Collider>().transform.position.x - ghost.transform.localScale.x / 2,
ghost.GetComponent<Collider>().transform.position.y + Building[SelectedBuilding].GetComponent<Collider>().transform.localScale.y / 2,
ghost.GetComponent<Collider>().transform.position.z - ghost.transform.localScale.z / 2),
Vector3.down, Mathf.Infinity);
buildingSlopeHitURAll = Physics.RaycastAll(new Vector3(
ghost.GetComponent<Collider>().transform.position.x + ghost.transform.localScale.x / 2,
ghost.GetComponent<Collider>().transform.position.y + Building[SelectedBuilding].GetComponent<Collider>().transform.localScale.y / 2,
ghost.GetComponent<Collider>().transform.position.z - ghost.transform.localScale.z / 2),
Vector3.down, Mathf.Infinity);
buildingSlopeHitDLAll = Physics.RaycastAll(new Vector3(
ghost.GetComponent<Collider>().transform.position.x - ghost.transform.localScale.x / 2,
ghost.GetComponent<Collider>().transform.position.y + Building[SelectedBuilding].GetComponent<Collider>().transform.localScale.y / 2,
ghost.GetComponent<Collider>().transform.position.z + ghost.transform.localScale.z / 2),
Vector3.down, Mathf.Infinity);
buildingSlopeHitDRAll = Physics.RaycastAll(new Vector3(
ghost.GetComponent<Collider>().transform.position.x + ghost.transform.localScale.x / 2,
ghost.GetComponent<Collider>().transform.position.y + Building[SelectedBuilding].GetComponent<Collider>().transform.localScale.y / 2,
ghost.GetComponent<Collider>().transform.position.z + ghost.transform.localScale.z / 2),
Vector3.down, Mathf.Infinity);
buildingSlopeHitMAll = Physics.RaycastAll(new Vector3(
ghost.GetComponent<Collider>().transform.position.x,
ghost.GetComponent<Collider>().transform.position.y + Building[SelectedBuilding].GetComponent<Collider>().transform.localScale.y / 2,
ghost.GetComponent<Collider>().transform.position.z),
Vector3.down, Mathf.Infinity);
RaycastHit hitUL = new RaycastHit(), hitUR = new RaycastHit(), hitDL = new RaycastHit(), hitDR = new RaycastHit(), hitM = new RaycastHit();
foreach (RaycastHit hit in buildingSlopeHitULAll)
{
if (hit.collider.tag == TerrainCollisionTag)
{
hitUL = hit;
break;
}
}
foreach (RaycastHit hit in buildingSlopeHitURAll)
{
if (hit.collider.tag == TerrainCollisionTag)
{
hitUR = hit;
break;
}
}
foreach (RaycastHit hit in buildingSlopeHitDLAll)
{
if (hit.collider.tag == TerrainCollisionTag)
{
hitDL = hit;
break;
}
}
foreach (RaycastHit hit in buildingSlopeHitDRAll)
{
if (hit.collider.tag == TerrainCollisionTag)
{
hitDR = hit;
break;
}
}
foreach (RaycastHit hit in buildingSlopeHitMAll)
{
if (hit.collider.tag == TerrainCollisionTag)
{
hitM = hit;
break;
}
}
if ((buildingSlopeHitULAll.Length > 0 ? hitUL.collider != null : false) &&
(buildingSlopeHitURAll.Length > 0 ? hitUR.collider != null : false) &&
(buildingSlopeHitDLAll.Length > 0 ? hitDL.collider != null : false) &&
(buildingSlopeHitDRAll.Length > 0 ? hitDR.collider != null : false) &&
(buildingSlopeHitMAll.Length > 0 ? hitM.collider != null : false))
{
if (HitDistanceSmallerEqual(hitUL, maxSlopeHigh) &&
HitDistanceSmallerEqual(hitUR, maxSlopeHigh) &&
HitDistanceSmallerEqual(hitDL, maxSlopeHigh) &&
HitDistanceSmallerEqual(hitDR, maxSlopeHigh) &&
HitDistanceSmallerEqual(hitM, maxSlopeHigh))
{
return true;
}
else
return false;
}
else
return false;
}
private bool ContainsTag(RaycastHit[] hitArr, string tag)
{
foreach (RaycastHit h in hitArr)
if (h.collider.tag == tag)
return true;
return false;
}
private bool ContainsTag(RaycastHit[] hitArr, string tag, out RaycastHit correctHit)
{
foreach (RaycastHit h in hitArr)
if (h.collider.tag == tag)
{
correctHit = h;
return true;
}
correctHit = new RaycastHit();
return false;
}
private bool HitDistanceSmallerEqual(RaycastHit hit, float val)
{
if (hit.distance - (ghost.transform.localScale.y) <= val)
return true;
return false;
}
static void ClearConsole()
{
// This simply does "LogEntries.Clear()" the long way:
var logEntries = System.Type.GetType("UnityEditorInternal.LogEntries,UnityEditor.dll");
var clearMethod = logEntries.GetMethod("Clear", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
clearMethod.Invoke(null, null);
}
}
Answer by MostNimbus · Mar 18, 2017 at 01:45 PM
Well, why not add Quaternion.Euler with params of transform.rotation. + 90? Then just make check for if(angle is 360){ set rotation to 0 } and assign it?
Answer by AnOrdinarySandwich · Mar 18, 2017 at 05:47 PM
Hi! Leerow is on the right track. Your instantiate code:
ghost = (GameObject)Instantiate(Building[SelectedBuilding],
new Vector3(hit[i].point.x,
hit[i].point.y,
hit[i].point.z),
Quaternion.identity);
Instead of using Quaternion.identity, use the correct rotational Quaternion :)
Hey! I am not quite sure what you mean as I am still learning scripting. Can you please explain just a bit better :) Thanks
Hey Skyfall, are you familiar with the term Quaternion? It's a rotational matrix, deter$$anonymous$$ing how the objects is rotated. Here's the Unity5 tutorial on Quaternions (it's only like 5 $$anonymous$$utes long to watch). Also, the code you can look at for an example is under LookAtScript, showing an example of how Quaternions can be used. And here's the scripting reference for Quaternions.
The ghost object mentioned above, you know where that's located in your code?
The call here Instantiate is the method used to create a game object copy of a prefab in code. In this case, it's accepting three arguments:
The item to spawn (in this case Building[SelectedBuilding])
The locational Vector3 where it spawns
The rotatational Quaternion
Quaternion.identity is a buildin rotation, defaulting to "no rotation" (as the unity docs call it). And as Leerow suggested, Quaterion.Euler() is a method that takes three arguments, rotation of x, y, and z (in degrees, not radians).
So, once you know which 90 degree angle snap location you want the building to have, ins$$anonymous$$d of using Quaternion.identity, simply replace it with Quaterion.Euler(yourXRotation, yourYRotation, yourZRotation).
Hey! I checked it out but thats in Javascript and it wouldnt connect with my code. Did i do something wrong?
Post your code you tried. Also, the unity site has javascript and c# code available :)
if (!ghostOn)
{
ghost = (GameObject)Instantiate(Building[SelectedBuilding],
new Vector3(hit[i].point.x,
hit[i].point.y,
hit[i].point.z),
var rotation = Quaternion.Euler(0, 30, 0);
Hey @AnOrdinarySandwich! (I notififed you as for some reason I couldnt reply to your comment.) It almost worked! I added it to my code but when i place it it had rotate the 90 degrees and then goes back to the way it was before. I cant change it either ): But still thank you so much for helping me and also I agree what your saying about scripting so starting tomorrow I am going to start learning! Thanks!
Hi Skyfall, just a tad more! As user r_ge rightly pointed out, this "ghost" is a preview object, to show you how it will look once the building itself is actually Instantiated. If you'll take a look in the original code in your first post, around like line 133, you'll notice another Instantiate call. This is the actual building placement. And do you see the Quaternion.identity here too? That's why it's not rotated upon placement. Hopefully that's enough to help you plug in the rotation from the ghost!
It kindove works if i dd it right. I changed the code that you said to the same code as you suggested. Im not dure if thats right but thats another problem. I also cant change it with a key. Im so sorry for continuously bugging you and I will give you some credit for your hard work :)
Can you post the code. showing all changes that you've made so far? Then we can see about getting that working
if (!ghostOn)
{
GameObject buildingToCreate = Building[SelectedBuilding];
Vector3 buildingLocation = new Vector3(hit[i].point.x, hit[i].point.y, hit[i].point.z);
Quaternion buildingRotation = Quaternion.Euler(0, 90, 0);
// ghost has already been declared, so we don't need to declare it again
ghost = (GameObject)Instantiate(buildingToCreate, buildingLocation, buildingRotation);
// snipped to conserve space
}
ghost.name = ghost.name.Replace("(Clone)", "(Ghost)");
ghost.layer = 2; //ignore raycast layer
ghostCollision = ghost.AddComponent<BuildingCollision>();
ghostOn = true;
if (ghost != null)
{
if (Input.Get$$anonymous$$ouseButtonDown(0) && !ghostCollision.Collided() && isFlat)
{
BuildingList bl = new BuildingList();
DestroyImmediate(ghost);
GameObject buildingToCreate = Building[SelectedBuilding];
Vector3 buildingLocation = new Vector3(hit[i].point.x, hit[i].point.y, hit[i].point.z);
Quaternion buildingRotation = Quaternion.Euler(0, 90, 0);
// ghost has already been declared, so we don't need to declare it again
ghost = (GameObject)Instantiate(buildingToCreate, buildingLocation, buildingRotation);
// snipped to conserve space
I had to get rid of the [...] as it caused errors in my console
Answer by rbeldessi · Mar 19, 2017 at 06:41 PM
Store a quaternion previewRotation for your building preview (I'm assuming that's what ghost is), when the user hits whatever input for rotation:
if(input)
previewRotation = Quaternion.Euler(previewRotation.eulerAngles + new Vector3(0, 90, 0));
Then when you instantiate the final version of the building instead of using Quaternion.identity use previewRotation; then I assume you wouldn't want the preview to persist through different building placements so reset previewRotation to Quaternion.identity.
Answer by RGS-Aaron · Mar 19, 2017 at 04:43 PM
This is quite easy, the solution I would use is something like this:
float objectRotation;
void update(){
if(Input.getButtonDown("MyRotateClockwisebutton")){
objectRotation = objectRotation - 90;
}
}
Instantiate code
Instantiate(MyPrefab, myRotation, Quaternion.Euler(new Vector3(0, objectRotation, 0)));
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
How to make drawable Paths 0 Answers
Making Building Placment rotatable 0 Answers
Raycasting not working? 1 Answer