• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
Question by devstudents · Nov 15, 2014 at 03:22 AM · rotationraycastinstantiateterrainslope

Make an instantiated object match the slope of the terrain

I'm trying to get my instantiated building to not clip through the terrain when I build on a slight slope. I've managed to get the building to drop to the terrain y coordinate but the x and z rotations of the building are not consistent with the gradient of the slope and so the building cuts through the terrain.

I've been researc$$anonymous$$ng t$$anonymous$$s for about two hours and I'm finding it really hard to understand the posts about raycast normal. Can someone please explain how to do t$$anonymous$$s in very simple terms? Here is the script:

 void Update()
 {
     Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
     RaycastHit $$anonymous$$tInfo; 
 
     if(currentlySelectedBuilding != null)
     {
         if(Physics.Raycast(ray, out $$anonymous$$tInfo, Mathf.Infinity))
         {
             if($$anonymous$$tInfo.collider.tag != null && $$anonymous$$tInfo.collider.tag == "Ground")
             {
                 Vector3 groundPosition = $$anonymous$$tInfo.point;
 
                 //I'm rounding the coordinates so it's easier to place buildings in perfectly straight lines etc. 
                 int x = Mathf.FloorToInt($$anonymous$$tInfo.point.x);
                 int z = Mathf.FloorToInt($$anonymous$$tInfo.point.z); 
                 
                 roundedMousePosition.x = x;
                 roundedMousePosition.z = z; 
 
                 //t$$anonymous$$s allows me to place buildings on the same level as the slope
                 roundedMousePosition.y = groundPosition.y; 
 
                 //I need some code here to change the rotation of currentlySelectedBuidling so matches the slope:
                 
                 //??????????????????????????????????????????// 
 
                 //make the gameObject equal to the mouse position so I can move it around the map
                 currentlySelectedBuilding.transform.position = roundedMousePosition; 
 
                 //t$$anonymous$$s script detects when the building is hovering over another object that should be built on. 
                 oCD = currentlySelectedBuilding.GetComponent<ObjectCollisionDetecter>(); 
                 
                 //rotate building w$$anonymous$$le it's hovering
                 if(Input.GetKeyUp("r"))
                 {
                     currentlySelectedBuilding.transform.Rotate(0,-90,0); 
                 }
 
                 if(Input.GetMouseButtonDown(0) && oCD.objectBelow == false )
                 {
                     //sets the building at the location it was at when mouse0 was $$anonymous$$t. 
                     currentlySelectedBuilding.transform.position = roundedMousePosition; 
 
                     //I had to make the prefab ignore raycast $$anonymous$$t w$$anonymous$$le it was moving otherwise it interacts with the raycast and creates weird artefacts. 
                     //since I now want the ray to detect it, I changed it back to obstacle. 
                     currentlySelectedBuilding.layer = LayerMask.NameToLayer("Obstacle");
 
                     //t$$anonymous$$s gets around the building(clone) problem. 
                     currentlySelectedBuilding.name = buildingName;
 
                     //update graph so units avoid building 
                     AstarPath.active.UpdateGraphs(currentlySelectedBuilding.collider.bounds); 
 
                     //make the building and ocd script null. 
                     currentlySelectedBuilding = null; 
                     oCD = null; 
                 }
                 else if (Input.GetMouseButtonDown(0) && oCD.objectBelow == true)
                 {
                     Debug.Log("You must build somewhere else!"); 
                 }
             }
         }
     }
 }

  
 
Comment
_protagonist

People who like this

1 Show 0
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
Best Answer

Answer by robertbu · Nov 15, 2014 at 03:24 AM

You can do somet$$anonymous$$ng like t$$anonymous$$s:

 function SpawnObject(prefab : GameObject, worldPos : Vector3) {
     var $$anonymous$$t : RaycastHit;
     if (Physics.Raycast(worldPos, Vector3.down, $$anonymous$$t)) {
         var go = Instantiate(prefab, $$anonymous$$t.point, Quaternion.identity);
         go.transform.rotation = Quaternion.FromToRotation(transform.up, $$anonymous$$t.normal) * go.transform.rotation;
     }
 }

'worldPos' should be a coordinate that has the right x and z positions, and a 'y' position guaranteed to be above the surface of the terrain. The raycast returns a normal (perpendicular) vector to the surface. The Quaternion.FromToRotation() will rotate the prefab to match the angle.

P.S. For future questions, please post your script. It gives us information like what language you are coding in, and it allows us to tailor the answer to your code.

Comment
_protagonist

People who like this

1 Show 5 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image devstudents · Nov 15, 2014 at 08:20 PM 0
Share

I'm sorry about that. I've edited the question so it has the original script.

avatar image robertbu · Nov 15, 2014 at 08:59 PM 0
Share

Since there is no Instantiate() in this code, that you are dealing with the section starting on line 40. Here is a bit of a untested rewrite of your code to add alignment:

     if(Input.GetMouseButtonDown(0) && oCD.objectBelow == false )
     {

         Transform tr = currentlySelectedBuilding.transform;
         RaycastHit hit;

         // Aligns the building with the surface
         if (Physics.Raycast (roundedMousePosition + 100 * Vector3.up, Vector3.down, out hit)) {
             tr.rotation = Quaternion.FromToRotation(trrm.up, hit.normal) * tr.rotation;
         }

         //sets the building at the location it was at when mouse0 was hit.
         currentlySelectedBuilding.transform.position = roundedMousePosition; 
         
         //I had to make the prefab ignore raycast hit while it was moving otherwise it interacts with the raycast and creates weird artefacts. 
         //since I now want the ray to detect it, I changed it back to obstacle. 
         currentlySelectedBuilding.layer = LayerMask.NameToLayer("Obstacle");
         
         //this gets around the building(clone) problem. 
         currentlySelectedBuilding.name = buildingName;
         
         //update graph so units avoid building 
         AstarPath.active.UpdateGraphs(currentlySelectedBuilding.collider.bounds); 
         
         //make the building and ocd script null. 
         currentlySelectedBuilding = null; 
         oCD = null; 
     }

Adjust the 100 as needed. If no other objects but the 'terrain' can be in teh way, you can use the more efficient Collider.Raycast().

avatar image devstudents · Nov 15, 2014 at 10:56 PM 0
Share

Thanks a lot! There is no instantiate in the update because the object has already been instantiate via another method governed by another script. It's instantiated the entire time it follows the mouse around. However your code works wonderfully!

The only problem is that sometimes the y is a bit off (it will either be half submerged or completely under the terrain). I tried deleting my y code on line 22 but then it would instantiate on the zero coordinate when I build on terrain that drops below zero. Maybe my mistake is having terrain that goes below the zero coordinate?

avatar image robertbu · Nov 16, 2014 at 04:12 AM 0
Share

Not sure why your positioning is wrong, but an easy fix is to use the hit.point from the Raycast to do the positioning. If the pivot for your object is not at the bottom, you might have to the 'y'.

avatar image devstudents · Nov 16, 2014 at 04:28 AM 0
Share

Line 12 and 22 make the y equivalent to the hitpoint y. So it's fine when I build on flat surfaces. When I build on slopes, the angles are great thanks to your code, but the y is sometimes not great. I'm guessing it's because the slopes are uneven so if I click a point that is lower than the surrounding points then the object will instantiate below the surface. If I find a way to get all the y points under the object and then instantiate on a y equal to the average of all those points, that might solve it. I just have no idea how I'd do that.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

1 Person is following this question.

avatar image

Related Questions

Making a particle effect parallel to the slope of a terrain 1 Answer

Use raycast to see if something else than terrain is under 1 Answer

Unity automatically rotate camera on slopes up/down 0 Answers

Instantiate not shooting in right direction 1 Answer

rotating gameobject according to terrain before instantiate 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges