Search Unity

[SOLVED] (C#) Rotate axis to a surface normal

Discussion in 'Scripting' started by Gunging, Nov 5, 2017.

  1. Gunging

    Gunging

    Joined:
    Sep 6, 2016
    Posts:
    139
    I have this "Sticky Bomb", and I want it to rotate its Z axis to be the same as the surface normal. The normal is obtained with RaycastHit.normal.

    Here is an image of the Sticky Bomb (It is supposed to be otherwordly so it might not be your average estereotype sticky bomb) showing the Z axis (Blue Arrow)


    And of course, I want it to rotate to stick on walls and roofs:


    The code Im using right now is exploting the fact that my game is mostly voxels and walls' and roofs' normals can only be directly facing ±y, ±x or ±z, but it is very limited as you can see:

    Code (CSharp):
    1.  
    2.     void Start() {
    3.         Debug.Log(normal);
    4.         if (normal.y == 1) { cookedNormal = new Vector3(-90, 0, 0); }
    5.         if (normal.y == -1) { cookedNormal = new Vector3(90, 0, 0); }
    6.         if (normal.x == 1) { cookedNormal = new Vector3(0, 90, 0); }
    7.         if (normal.x == -1) { cookedNormal = new Vector3(0, -90, 0); }
    8.         if (normal.z == 1) { cookedNormal = new Vector3(0, 0, 0); }
    9.         if (normal.z == -1) { cookedNormal = new Vector3(0, 180, 0); }
    10.         transform.eulerAngles = cookedNormal;
    11.         //It is "cooked" because it is no longer raw :P
    12.     }
    However the enemies though being voxels can actually rotate in any way they want, so whenever a bomb falls on them and sticks, it is always facing the default rotation it is instantiated by:


    Thanks in advance.
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    Code (csharp):
    1. transform.rotation = Quaternion.FromToRotation(Vector3.forward, normal);
    This should probably do the job.

    Basically we get the amount of rotation from forward to the normal, and then set it to that. This should result in the z-axis of your object pointing in the direction of normal.
     
  3. Gunging

    Gunging

    Joined:
    Sep 6, 2016
    Posts:
    139
    It doesnt work :c.

    What I wonder most is what is the RaycastHit.normal is, because it says nothing about what it is in the documentation and the values are from -1 to +1 in each axis. You'd expect it to be a Quaternion or some EulerAngles but nope. I mean, it is not completely dumb because (0, 1, 0) is directly upwards and (0, -1, 0) is straight downwards.
     
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    a normal vector is a vector pointing in the direction of the surface face

    The vector is a unit vector (of length 1), you will see this in the 'magnitude' property of the Vector3.

    Basically for any given point on a surface, the blue arrows here would be the vector for the surface normal at that point:

    https://en.wikipedia.org/wiki/Normal_(geometry)

    As for the example code... yes, it should work. Unless your normal is wrong, or you don't want the z-axis, or something else. Can you show me your actual implementation?
     
    Gunging likes this.
  5. Gunging

    Gunging

    Joined:
    Sep 6, 2016
    Posts:
    139
    Ok it does work now, I see what I did. Thank you.

    For anyone who needs the code:

    Code (CSharp):
    1. public class StickyBombScript : MonoBehaviour {
    2.     public Vector3 target, normal; //The "target" is the raycasted point
    3.  
    4.     void Start() {
    5.         transform.rotation = Quaternion.FromToRotation(Vector3.forward, normal);
    6.     }
    7.  
    8.     void Update () {
    9.         if (GetComponent<Transform>().position != target) {
    10.             float step = 30 * Time.deltaTime; //Moves to the raycasted point
    11.             GetComponent<Transform>().position = Vector3.MoveTowards(transform.position, target, step);
    12.         }
    13.         if (GetComponent<Transform>().position == target && !GetComponent<Collider>().enabled) {
    14.             GetComponent<Collider>().enabled = true;
    15.             //In my case this is a trigger aura for the OnTriggerEnter()
    16.         }
    17.     }
    18.  
    19.     void Blow() {
    20.         //[INSERT EXPLOSION CODE HERE]
    21.         //Debug.Log("Boom!");
    22.         //Destroy(gameObject);
    23.     }
    24.  
    25.     void OnTriggerEnter(Collider coll){
    26.         if (coll.gameObject.tag == "Player") { Blow(); }
    27.     }
    28. }
    29.  
    And how the normal is obtained (in the player script or whatever you set bombs with):

    Code (CSharp):
    1.  
    2.         if (Input.GetKey(KeyCode.Mouse0)) {
    3.             RaycastHit sampleRaycast;
    4.             Physics.Raycast(rS.transform.position, rD.transform.position - rS.transform.position, out sampleRaycast, 1000);
    5.             //rS stands for "Raycast Start" and is an emtpy gameObject assigned somewhere else.
    6.             //rD stands for "Raycast Direction" and is an empty gameObject assigned somewhere else.
    7.             if (sampleRaycast.collider != null && fireRate < Time.time) {
    8.                  GameObject proyectile = (GameObject)Instantiate(stickyBombPrefab, firePoint.position, firePoint.rotation);
    9.                  proyectile.GetComponent<StickyBombScript>().target = sampleRaycast.point;
    10.                  proyectile.GetComponent<StickyBombScript>().normal = sampleRaycast.normal;
    11.                  fireRate = Time.time + 1f; //fireRate is a float
    12.                  //firePoint is an emtpy gameObject's transform that is where you want the sticky bomb to be spawned (I got it on the tip of the sticky bomb cannon Im using).
    13.  
    14.                  //Note that my sticky bombs are NOT affected by gravity
    15.             }
    16.         }
     
    Last edited: Nov 8, 2017
  6. Stevers

    Stevers

    Joined:
    Aug 11, 2019
    Posts:
    1
    Not sure why but this is the code i needed for my specific situation:


    Code (CSharp):
    1.  
    2.         GameObject myTargetReticle = <insert code to create cylinder, resize, etc....>
    3.  
    4.         Ray mouseRay = Camera.main.ScreenPointToRay(mousePosition);
    5.         RaycastHit hit;
    6.         if(Physics.Raycast(mouseRay, out hit, Mathf.Infinity))
    7.         {
    8.          myTargetReticle .transform.rotation = Quaternion.FromToRotation(Vector3.up, hit.normal);
    9.         }
    10.  
    11.  
    I was drawing a circle (flat cylinder, it was 3D with a height of .0.25) on the ground whereever I pointed my mouse. Essentially so i can form the basis of a targetting system.

    If i pointed my mouse at a cube, i wanted the circle to be flat aginst the side of the cube, like you'd expect. and if i put the mouse under the feet of a "player" model, it should show the circle underneath their feet.