Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Making a random item hover

Discussion in 'Scripting' started by TiggyFairy, Jun 18, 2023.

  1. TiggyFairy

    TiggyFairy

    Joined:
    Dec 22, 2019
    Posts:
    413
    I have a situation in my game where I want the player to be able to select items that hover above the floor. My issue is that they clip through the ground 99% of the time. What I would like to do is to get the longest distance from the item's pivot and then offset the item upwards from a target point by that amount. I've been trying to do this for a couple of days, but I'm not having much luck. I don't know how to find the pivot point, or how to put it into this code. Worse: even when the pivot is totally equal, this code doesn't really do it. Things still clip. Any ideas how to fix it without shooting smaller items into space?

    Code (CSharp):
    1. Vector3 scale = item.transform.localScale;
    2.             Vector3 offset = item.GetComponent<Collider>().bounds.extents;
    3.             loc[count] += Vector3.up * Mathf.Max(offset.x * scale.x, Mathf.Max(offset.y * scale.y, offset.z * scale.z));
     
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    You need to remember that a vector 3 can represent different things. Such as a position, a direction, euler angles, or scale, the first and second of which might also be world or local positions/directions.

    In this instance you're using extends wrong, as its a vector 3 that represents scale, and not a direction or position. However the
    extends.magnitude
    will represent the length of this scale. As in, the distance from the center of the bounds to its furthest point (roughly speaking).

    So it should be enough to just do:
    Code (CSharp):
    1. Bounds bounds;
    2.  
    3. if (item.TryGetComponent(out Collider collider)
    4. {
    5.     bounds = collider.bounds;
    6. }
    7. else if (item.TryGetComponent(out MeshRenderer renderer)
    8. {
    9.     bounds = renderer.bounds;
    10. }
    11. else
    12. {
    13.     bounds = new Bounds(Vector3.zero, Vector3.one);
    14. }
    15.  
    16. float heightOffset = bounds.extents.magnitude;
    17. Vector3 offset = Vector3.up * (heightOffset + extraOffset);
    Wrote it so you don't brazenly potentially walk into a null-ref if the object doesn't have a collider or mesh renderer (however unlikely).

    The calculation could be more sophisticated, but this should be straight forward enough.
     
    Last edited: Jun 18, 2023
    TiggyFairy likes this.
  3. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    510
    As Spiney has shown, you don't need localScale as the bounds is already scaled.
     
    spiney199 likes this.
  4. TiggyFairy

    TiggyFairy

    Joined:
    Dec 22, 2019
    Posts:
    413
    Thanks Spiny. I'm having issues though. Seems there's an error:

    By the way, I'm still having hellish problems rotating it. All I want to do is let the player press a button and have the object rotate 45 degrees in a set direction - without gimbal lock. I get a lot of warnings about using Euler, but every tutorial uses
    Quaternion.Euler()
    and that's not working for me. I can only rotate on one axis using that method. Here's my current code:

    Code (CSharp):
    1. public void RotateRight()
    2.         {
    3.             //Debug.Log($"Turn Right");
    4.             startRotation = targetRotation;
    5.             targetRotation += Vector3.up * 45f;
    6.         }
    And, in fixed update:

    Code (CSharp):
    1. rigidbody.MoveRotation(Quaternion.Slerp(transform.localRotation, Quaternion.Euler(targetRotation), speed * Time.deltaTime));
    The code above works - but things like up and down do not. I'm not sure how to make them function as I think all the other axis's would need to work together to point that way. So I don't think this is going to cut it:

    Code (CSharp):
    1. targetRotation += Vector3.right * 45f;
    Rotation code is just not something I have a good time with tbh, even though I know what a gimbal is.
     
    Last edited: Jun 18, 2023
  5. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    I wrote it in notepad++ so there was probably going to be a few typos. Please just use the docs when something isn't right: https://docs.unity3d.com/ScriptReference/Bounds.html

    You should know by now you never needs to post about compiler errors.

    First rule of rotating things: don't use euler angles. You never need to. Use quaternions.

    If you want to rotate something on an axis, use
    Quaternion.AngleAxis
    the rotation on that axis. If you want a rotation from one direction to another, use
    Quaternion.FromToRotation
    to make a rotation that rotates towards that direction.

    Then you combine rotations by multiplying them together. Then you can rotate towards that target rotation overtime using
    Quaternion.RotateTowards
    .
     
    Kurt-Dekker likes this.
  6. TiggyFairy

    TiggyFairy

    Joined:
    Dec 22, 2019
    Posts:
    413
    Thank you. I still have an object it won't work for, but it seems to be working otherwise. I think there's maybe something wrong with that object.

    I think the problem is that I don't know what the 'to' rotation should be. I get that I shouldn't use Euler, but I still have to put 'rotate by 45f' into the code somewhere and I don't know where or what it would look like as every tutorial is in Euler and they say "don't input Quaternions unless you understand them totally". So I'm just basically stumped here.
     
  7. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    If you absolutely need 45 degrees, here are five different ways to make 45 degrees on the Y, with increasing difficulty.
    Code (csharp):
    1. var rot = Quaternion.Euler(0f, 45f, 0f);
    Code (csharp):
    1. var rot = Quaternion.AngleAxis(45f, Vector3.up);
    Code (csharp):
    1. var rot = Quaternion.Slerp(new Quaternion(0f, 0f, 0f, 1f), new Quaternion(0f, 1f, 0f, 0f), 0.25f);
    Code (csharp):
    1. var rot = Quaternion.FromToRotation(Vector3.forward, new Vector3(1f, 0f, 1f) * (Mathf.Sqrt(2f) / 2f));
    Code (csharp):
    1. var y = Mathf.Sin(45f / 2f * Mathf.Deg2Rad); // => 45 * pi / 360 => 0.125 * pi
    2. var rot = new Quaternion(0f, y, 0f, Mathf.Sqrt(1f - y * y));
    Spiney's "first rule" is about automating movement, nobody said you'd lose hands if you touch anything Euler.

    Unity docs "recommendation" is about people hurting their brains, nobody said you should climb mount Everest using only eulerAngles.

    Edit: Verified all five
     
    Last edited: Jun 18, 2023
  8. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    I hope you can see from the previous examples that quaternions basically shapeshift between 4 cardinal states: X, Y, Z, and W, such that X^2+Y^2+Z^2+W^2 = 1 where each component represents an axis of rotation, going from 0 to 1, where 1 is practically 180 degrees, and W is a dump accumulator of "no touchy".

    Therefore the identity quaternion (full "no touchy" behavior) is (0,0,0,1) and as you add something to X/Y/Z the W gets smaller. The amount by which X/Y/Z grow is not linear but trigonometric so you cannot set 0.5 and hope it'll do 90 degrees, but you can do sqrt(0.5) and this is correct because it's the same thing as sin(90 / 2), which is how quaternion are formulated, as half angle sines (except W).

    But also you now need to take something from W and by this formula X^2+Y^2+Z^2+W^2 you can see that whatever you add must contribute and so A^2 + W^2 = 1, A^2 = 1 - W^2 therefore W = sqrt(1 - A^2)

    It's a miracle, but W is actually just a half angle cosine. W = cos(a / 2) = sqrt(1 - sin^2(a / 2))
    This is among the standard trigonometric identities.

     
  9. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    So in essence, quaternions simply bake-in the trigonometric identities required for the rotation matrices. This particular flavor of a quaternion is called a versor, and contrary to popular belief, is not a 4-dimensional object. It's very well grounded in the 3d reality, which is why we use it for rotations.

    Nothing really special about them, just another notation for the very same angles, only it's not degrees but radians and there are certain rules you have to follow. This is because they serve as ready-made objects to be plugged-in further down the line, and as such are compactly used to perfectly capture all kinds of rotations in the engine. Think of them as "ideas", not "values".
     
  10. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    There's a lot of great programming and Vector and Quaternion stuff here but I have to ask...

    Why are you writing code to animate something?!!!!!!

    Unity has a fantastic animation and animator system that is purpose built to make collectable items bob up and down like this. Nobody writes code for this stuff if they can get away with it!!

    And if you absolutely MUST write code, at least use a free tweener such as LeanTween or DOTween. Why write the same tweening code again and again and again, each time with fresh bugs you have to slog through.
     
  11. TiggyFairy

    TiggyFairy

    Joined:
    Dec 22, 2019
    Posts:
    413
    Sorry. It's just that I want to learn how to rotate things properly without it being totally jury-rigged together. This is also stuff I use in player and camera movement. Also I've yet to find something so simple the animation system can't mess it up. Not even animating a gate to go up and down. I always end up with an object that's increasingly deviating from where it's meant to be. I asked about it, but got no replies so I just took to animating everything with code. Code just works better. Don't know if I'll try again when I add monsters, but the rotations in this game are something that has to be absolutely exact.

    Thanks a lot for the really in-depth response. I'm going to try and go through it until I understand it.
     
    Last edited: Jun 18, 2023
  12. TiggyFairy

    TiggyFairy

    Joined:
    Dec 22, 2019
    Posts:
    413
    Could you clarify what that means? Should I not touch w, or does it stops something else touching it?
     
  13. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    You should not touch x, y, z, or w. These are the internal fields of the Quaternion. Consider them magic.

    You should also never touch eulerAngles.x, eulerAngles.y, eulerAngles.z for different reasons. See link below.

    NOTE: Those last three (x,y,z) have ABSOLUTELY NOTHING to do with the first four (x,y,z,w).

    They have NOTHING to do with each other. Really. Honest! Nothing.

    If you doubt that assertion, print them out while wiggling (rotating) an object. You'll see.

    All about Euler angles and rotations, by StarManta:

    https://starmanta.gitbooks.io/unitytipsredux/content/second-question.html

    Code (csharp):
    1. using UnityEngine;
    2. // @kurtdekker - put this on a GameObject, rotate it and observe the outputs
    3. public class DumpQuat : MonoBehaviour
    4. {
    5.     void Update ()
    6.     {
    7.         Quaternion q = transform.rotation;
    8.  
    9.         Debug.Log( "x=" + q.x.ToString("0.00") + ", " +
    10.             "y=" + q.y.ToString("0.00") + ", " +
    11.             "z=" + q.z.ToString("0.00") + ", " +
    12.             "w=" + q.w.ToString("0.00") + ", " +
    13.             "eu.x=" + q.eulerAngles.x.ToString("0.00") + ", " +
    14.             "eu.y=" + q.eulerAngles.y.ToString("0.00") + ", " +
    15.             "eu.z=" + q.eulerAngles.z.ToString("0.00")
    16.         );
    17.     }
    18. }
    Screen Shot 2023-06-18 at 8.23.35 AM.png
     
    Last edited: Jun 18, 2023
  14. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    If you can't understand a word of what I'm saying, that means you don't touch any of it, like Kurt said. That's why Unity recommends to not touch them as well. You simply need to know more, much more, about games, life, and programming. Then some things tend to open up like it's nothing. Try to be diligent, learn more, practice more, and you'll get there, one step at a time.

    Also try reading post #9 until you can understand it.
     
    Kurt-Dekker likes this.
  15. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    510
    It's okay to use euler angles if you're staying within certain limits. The Quake engine which many of today's engines are inspired by used only euler angles for the characters, monsters etc. This is because they're mostly upright and only lean/tilt slightly from their up axis. But if you're doing a space game that allows the ships to rotate freely on any axis by any amount then you're probably going to have problems if using euler angles.

    It's easy to forget that Unity was intended for amateur programmers who understandably find it easier to use euler angles.
     
  16. TiggyFairy

    TiggyFairy

    Joined:
    Dec 22, 2019
    Posts:
    413
    This is so frustrating. It has to freely rotate for the game to work. I've been struggling with this since day one. Every other part of my code works eventually, but every time I try to rotate something it ends in stress and stress and no workable answers and then I'm still where I was six months ago.
     
  17. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    I have no idea what you are doing. Our entire product line uses thousands of animations every day.

    Enclosed is a bobbing / weaving powerup. You can drop whatever sensor / trigger scripts on it you like.

    Pull it apart. It's very simple. Object hierarchy, one Cube, one Animator, one Animation.

    Make a blank scene, press PLAY. Now start dropping these things into the scene while the game is running.

    Using code to move stuff is the textbook definition of jury-rigging. Use animations for animation!
     

    Attached Files:

  18. TiggyFairy

    TiggyFairy

    Joined:
    Dec 22, 2019
    Posts:
    413
    I make a cube, set the animation to move the object for real, hit record. I then move the block up two units. Stop. I then reverse the animation by setting speed to -1. Gate now goes up and down, but then slowly goes too far by little bits until it's totally under the floor.
    I will when I find out why the animation system can't reverse mirror animations accurately. Until then, code is waaaaay easier. Mostly I just tween things, but sometimes I just can't get away with that.
     
  19. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    What is that... Speed?!!

    Just finish the animation... set it at y = +0 at time==0, move it up at time==one half, then copy the time==0 keyframe ahead to your final time keyframe.

    That's all I did above. Then I inserted a few random "Programmer Animator" wiggles in it and sent it off.

    If you need to adjust the speed of an animation and choose to use the speed control... congratulations! Now you have to sprinkle these horrible ugly special "speed = 1.23f" kinds of warts all over your codebase.

    Instead, select all the animation keyframes at once (Ctrl-A), then scale them smaller or larger laterally in the animation window.
     
  20. TiggyFairy

    TiggyFairy

    Joined:
    Dec 22, 2019
    Posts:
    413
    Oh I didn't know speed - 1 was so bad because everyone says to use it. I did ask ages ago about this, but nobody mentioned the speed setting.

    I mean, what I'm doing with a coroutine ends up the same but without the weird stuff and uncertainty.

    But anyway, it's not like I can use animations for what I want to use them. They would end up with the same sorts of issues minus the ability to collide.
     
    Last edited: Jun 18, 2023