Unity3d - Get GameObject height for a rotating object - c#

I want to get a GameObject's height. I have tried with:
this.GetComponent<MeshRenderer>().bounds.size.y
But the problem with bounds is that it works with static objects only. When you have a moving object and if the rotation of the object is not perfectly aligned, the bounds (height) is not accurate anymore because it returns the height of the bounds that is a square and if you tilt an object like a plate you get the bounds height which is not accurate to the height of the object.
It is an Axis-Aligned Bounding Box (also known as an "AABB").
Please check the image I attached, there you can see the problem with moving objects and if you rotate them how the height is not accurate anymore.
Did anyone else have this kind of problem?
Any advice on how to get the object's height accurately?

One option is to use the Game Object's Collider.
You can relatively easily find the height and width of a collider so this is one way you can measure the dimensions of a game object. The collider, for example a box collider on the game object, will have to fully encompasses the object. You can do this be resizing the collider until it snugly wraps around your game object. Then when you want to find a certain dimension of the object instead find that dimension on the collider and multiply by the Game Object's transform.scale.
Here are some examples I have tested.
Example 1:
CapsuleCollider m_Collider = GetComponent<CapsuleCollider>();
var height = m_Collider.height * transform.localScale.y;
https://docs.unity3d.com/ScriptReference/CapsuleCollider-height.html
or
Example 2:
BoxCollider m_Collider = GetComponent<BoxCollider>();
var height = m_Collider.size.y * transform.localScale.y;
var width = m_Collider.size.x * transform.localScale.x;
var breadth = m_Collider.size.z * transform.localScale.z;
https://docs.unity3d.com/ScriptReference/BoxCollider-size.html
Hopefully one of these will work for you.
If not, then a second inconvenient way you can find the height is to create 2 proxy objects as children at the top and bottom of your object. Afterwards find the scalar distance between them.

Solution 1: Use Mesh.bounds
You can get the height using Mesh.bounds. Unlike MeshRenderer.bounds (or Renderer.bounds), this is the axis-aligned bounding box of the mesh in its local space (i.e. not affected by the transform).
You still need to account for the scale of the GameObject using transform.lossyScale, as follows:
public class Example : MonoBehaviour
{
private Mesh _mesh;
private void Awake()
{
_mesh = GetComponent<MeshFilter>().mesh;
}
private void Update()
{
float height = _mesh.bounds.size.y * transform.lossyScale.y;
Debug.Log(height);
}
}
(Note when using transform.lossyScale, the value can be slightly inaccurate. This is something to note if you need extreme accuracy. See the documentation on transform.lossyScale:)
Please note that if you have a parent transform with scale and a child
that is arbitrarily rotated, the scale will be skewed. Thus scale can
not be represented correctly in a 3 component vector but only a 3x3
matrix. Such a representation is quite inconvenient to work with
however. lossyScale is a convenience property that attempts to match
the actual world scale as much as it can. If your objects are not
skewed the value will be completely correct and most likely the value
will not be very different if it contains skew too.
Solution 2 (Workaround): Cache the unrotated MeshRenderer bounds
Alternatively, if in your context you are able to instantiate the GameObject without a rotation and you only need to rotate the GameObject after initialization, one workaround solution is to cache the bounds from MeshRenderer in the Awake method, as follows:
public class Example : MonoBehaviour
{
private Bounds _initialUnrotatedBounds;
private void Awake()
{
InitializeUnrotatedBounds();
}
private void InitializeUnrotatedBounds()
{
Assert.IsTrue(transform.rotation == Quaternion.identity);
_initialUnrotatedBounds = GetComponent<MeshRenderer>().bounds;
}
private void Update()
{
// Use the cached bounds. Now, even for moving objects,
// it doesn't matter if the rotation changes
float height = _initialUnrotatedBounds.size.y;
Debug.Log(height);
}
}

Related

how to use slider to rotate the joint in unity

i use the slider to change the joint angle, here is my code.
void Update()
{
a = sliderx.GetComponent<slider>().value;
b = slidery.GetComponent<slider>().value;
headjoint.transform.Rotate(a, b, 0);
}
and after I slide the bar, the joint rotates around and won't stop. like
a += value
and not
a = value
, the slider not give a definite number?
why? do I need to use eularangle?
when I use
headjoint.transform.rotation = Quaternion.Euler(a, b, 0);
it's change the rotation angle of my object ,to 0,0,0 when running
anyideas?
Rotate as the name suggests rotates the object about a given amount. If you call this constantly with the same value your object is rotated constantly about the same amount.
You rather want to set a rotation.
Your attempt
headjoint.transform.rotation = Quaternion.Euler(sliderX.value, sliderY.value, 0);
is actually close, yes. But by default Rotate is applied in local space. So if you want that same behavior you'd rather use
// First of all use the correct type and don't use GetComponent repeatedly
public Slider sliderX;
public Slider sliderY;
void Update()
{
headjoint.transform.localRotation = Quaternion.Euler(sliderX.value, sliderY.value, 0);
}

How can I get screen position of a UI element?

I'm trying to get the global position of a UI element.
I've tried so many different ways to get the position but none of them seems to work. The problem comes with the anchors, as i'm moving them and not the UI element position itself (for resolution purposes), the position of the UI showing in the inspector is always 0,0,0. I also tried to get the anchoredPosition and also the Corners to make some calculations but it still not working, I always get (0,0,0) or some incoherent numbers.
Vector3 pointToTravel = Camera.main.WorldToScreenPoint(objectRectTransform.anchoredPosition);
This 'pointToTravel' variable should hold the screen position in pixels of the ui element.
anchoredPosition is the
position of the pivot of this RectTransform relative to the anchor reference point.
Since RectTransform inherits from Transform you can simply use
Vector3 pointToTravel = Camera.main.WorldToScreenPoint(objectRectTransform.position);
in order to get the absolute world position.
The position you see in the in the Inspector is always the
localPosition for Transform - relative to the parent GameObject.
or
anchoredPosition for RectTransform depending on the anchor settings - relative to the anchor reference point.
However in the case your Canvas is a Screen Space - Overlay all the contained UI (or better RectTransforms) already uses the screenspace coordinates in pixels so the WorldToScreenPoint is unnecessary or better said returns incorrect values.
So in that case you could instead simply use
Vector3 pointToTravel = objectRectTransform.position;
In order to get any information you could need about the dimensions and position of a UI Element from its RectTransform, I wrote this little utility function to convert a RectTransform to a Screen Space - Rect(For Canvas mode Screen Space - Camera).
public static Rect RectTransformToScreenSpace(RectTransform transform, Camera cam, bool cutDecimals = false)
{
var worldCorners = new Vector3[4];
var screenCorners = new Vector3[4];
transform.GetWorldCorners(worldCorners);
for (int i = 0; i < 4; i++)
{
screenCorners[i] = cam.WorldToScreenPoint(worldCorners[i]);
if (cutDecimals)
{
screenCorners[i].x = (int)screenCorners[i].x;
screenCorners[i].y = (int)screenCorners[i].y;
}
}
return new Rect(screenCorners[0].x,
screenCorners[0].y,
screenCorners[2].x - screenCorners[0].x,
screenCorners[2].y - screenCorners[0].y);
}
From this rect you can easily get all the information that the RectTransform does not give you that easily, such as Screen-Space-position and non-anchored size.

is there a way to make an object follow another object plus always points/looks towards the camera

i am working on a project and need help making an object follow another object but at the same time one end should point towards the camera.I have tried the lookAT method but it is not working properly.
LookAt works perfectly for 3D objects.
These are the possibilities that LookAt may fail to work as you expect:
You want a 2D object to look at a point.
The visually-considered front-side of the object is not its x+ side. mainly because:
The 3D model is carelessly fabricated using a third party software.
The object is inside another object and is rotated in its place.
You are manipulating the rotation of the object elsewhere.
Pointing the other object is really simple. You only need to use the LookAt, like this:
GameObject target;
transform.LookAt(target.transform);
For following the target, I see multiple apporaches. You can for example make the object also use the script with LookAt and when you move the main oject, move also the follower.
Another more complex way would be set a maximum separation distance where the object will move towards its target. This one would also use LookAt.
If you chose the last one, you can do something like this:
const float MaxDistance = 5.0f // The maximum distance the target can go away before following it.
float speed = 2.0f;
if(Vector3.Distance(transform.position, target.transform.position) > MaxDistance)
{
transform.LookAt(target.transform);
transform.position += Vector3.forward * speed * Time.deltaTime;
}
You should attach this to the Update or a Couroutine.
A specific object goes straight to the target object as follows:
//Declare a variable that points to a specific object and a target obj variable
public GameObject obj1;
public GameObject obj2;
GameObject targetObj;
// Update is called once per frame
void Update()
{
if (Input.GetKeyUp(KeyCode.Alpha1))
OnChangedTarget(obj1);
if (Input.GetKeyUp(KeyCode.Alpha2))
OnChangedTarget(obj2);
UpdateObjPos(Camera.main.gameObject);
}
//Keep distance from target obj
void UpdateObjPos(GameObject followObj, float width = 10f, float height = 10f)
{
if (targetObj == null)
return;
followObj.transform.LookAt(targetObj.transform);
float posX = targetObj.transform.position.x + width;
float posY = targetObj.transform.position.y + width;
float posZ = targetObj.transform.position.z + height;
followObj.transform.position = new Vector3(posX, posY, posZ);
}
//Declare function to select target obj
void OnChangedTarget(GameObject obj)
{
Debug.Log("Target Changed : " + obj.name);
targetObj = obj;
}
Solution
Just inherit your desired object to the main game object withe the main game object which is your player.I never used any c# code script for this purpose. if you want to follow two objects to follow each other just inherit main game object . In that way the second game object will follow the required main player . The camera will be inherit as well if you want camera to follow both of them . As far as the points the camera is concerned you need to play with unity tool and adjust the x,y and z axis according to the position of camera . I would suggest to put camera in front of both game objects and just turn the face of camera toward the game objects .
picture

Vector 3 Position on different Screen Size

so this is what happens when Im using vector 3 on my game I want my vector 3 to be on specific position for different screen sizes? is that possible? here is my codes
public virtual void ShuffleButton()
{
Vector3 buttonFirst = gameButtons[0].transform.position;
buttonFirst.x += 297f;
gameButtons[0].transform.position = buttonFirst;
Vector3 buttonSecond = gameButtons[1].transform.position;
buttonSecond.x -= 74.25f;
gameButtons[1].transform.position = buttonSecond;
Vector3 buttonThird = gameButtons[2].transform.position;
buttonThird.x += 74.25f;
gameButtons[2].transform.position = buttonThird;
Vector3 buttonFourth = gameButtons[3].transform.position;
buttonFourth.x -= 148.5f;
gameButtons[3].transform.position = buttonFourth;
Vector3 buttonFifth = gameButtons[4].transform.position;
buttonFifth.x -= 148.5f;
gameButtons[4].transform.position = buttonFifth;
}
You are looking for the position conversion functions like Camera.ScreenToWorldPoint(). These can be found in the Unity Camera class documentation here: https://docs.unity3d.com/ScriptReference/Camera.html
If, for example, you want to place a Sprite in the top-left corner, regardless of screen size, you would use the screen or viewport space. The position of the sprite will have to be translated from this screen/viewport space to world space. You could use Camera.ScreenToWorldPoint() for this.
However, Unity uses three viewspaces: Screen, World and Viewport. You should read up on all three as your problem stems from the fact that you are trying to use world coordinates (transform.position) to set the position of UI elements (which use either the screen or world space; this is dependent on the parent Canvas settings)

Keep a particlesystems transform and cone radius relative to its parent

I have a "Lift". While being in the game, you walk into the particle system and get moved up in the air (on y).
So the particle system is a child of the cube / the lift. So when scaling the cube, I don't want to change the settings of the particle system. It should scale itself on its own.
When the cube got the y position on 5 and a height / scaling on y of 10, the particle system should place itself down at the bottom.
As you can see, I want it being full automatic.
So, when heading into the code I got this
[SerializeField]
ParticleSystem liftParticles;
private void Start()
{
Vector3 objectScale = transform.localScale; // cube scaling
Vector3 particlePos = liftParticles.transform.position; // temp position
particlePos.y = (particlePos.y - objectScale.y) / 2; // move down on y
liftParticles.transform.position = particlePos; // set particle position
float transformScalingX = objectScale.x; // x scaling of the cube
float transformScalingZ = objectScale.z; // z scaling of the cube
var shape = liftParticles.shape; // set the cone radius now
shape.radius = transformScalingX > transformScalingZ ? transformScalingX : transformScalingZ;
liftParticles.shape = shape;
}
I want to go with the following example as mentioned above..
The cube got a scaling of (3,10,3) and its position is (0,5,0)
my current calculation particlePos.y returns a value of -0,75 but it has to be -0,5.
So do I have an error in my code? (yes obviously I do ...)
The second problem is, how do I change the radius of the particlesystem? When trying to reference the radius of the cone, it says I can't change it, it is readonly.
Is it? I hope I can change this somehow ...
Edit:
Obviously, the particlesystem just has to be always on -0,5f on y when having a scaling of (1,1,1). No need for a calculation anymore.
But I still need to change the radius of the shape and set the lifetime of the particles relative to the height of the lift. Means
private void Start()
{
Vector3 liftScale = transform.localScale; // Liftscaling
var shape = liftParticles.shape; // temp shape
shape.radius = liftScale.x > liftScale.z ? liftScale.x : liftScale.z; // set radius
liftParticles.shape = shape; // assign the temp shape to the real shape
liftParticles.main.startLifetime = ; // set the liftetime of the particles relative to its parent height on y
}
As I understand you made the particle system child of the lift (a cube) so it can move together. In case you just want that both of them move together, but they scale independently, you should consider to use an Empty GameObject as a parent.
You can placed this Empty GameObject in the middle of the Cube (your lift) and then make the lift and the particle filter children of that Empty GameObject. Then move the Empty GameObject instead of the lift, and the children will move as well.
About modifying the radio, try this script
GameObject myParticleGenerator;
ParticleSystem.ShapeModule pShape;
pShape = yParticleGenerator.GetComponent<ParticleSystem>().shape;
pShape.radius = 4.0f;

Categories

Resources