how to limit drag and drop to parent object - unity3d

I have rawimage inside a game object and want to move it by drag and drop but must not exceed parent gameobject
I can move the raw image but It can go anywhere in the canvas
for that I calculate the border points and allow it if it stays in border but mouses eventdata.position gives world position.
how can I transform mouse position to position in game object?

You can translate the mouse position to world position then world position to local position
Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector3 objectRelative = object.transfrom.InverseTransformPoint(mousePos);

Another approach is to use collider bounds, these aren't always super accurate however, depending on the shape of your object
In your child object's update method, make sure you have [ExecuteInEditMode] at the top of your script
if(!Application.isPlaying){
Vector3 minBounds, maxBounds;
Collider parentCollider = transform.parent.gameObject.GetComponent<Collider>();
minBounds = parentCollider.bounds.min;
maxBounds = parentCollider.bounds.max;
float xPos = Mathf.Clamp(transform.position.x, minBounds.x, maxBounds.x);
float yPos = Mathf.Clamp(transform.position.y, minBounds.y, maxBounds.y);
float zPos = Mathf.Clamp(transform.position.z, minBounds.z, maxBounds.z);
transform.position = new Vector3(xPos,yPos,zPos);
}

Related

How to make a 2D arrow on the screen that always point to a 3d object inside the AR Scene

I want to display a 2D arrow on the screen that always moves to point to a 3D object in the AR scene.
The issue is how to measure the angle that should the arrow rotate to point the desired 3D object.
Thanks in advance.
One strategy is to project the position of your object into screen space.
Then calculate the vector between the position of your arrow and that projected position. You can use this vector to calculate an angle of rotation from, for example, a vertical direction, using the Dot Product, followed by an acos.
Finally, you'd need to do a little cross-product calculation to decide whether the above rotation is clockwise or anticlockwise.
Here is some sample code:
public GameObject Target;
RectTransform rt;
void Start()
{
rt = GetComponent<RectTransform>();
}
void Update()
{
// Get the position of the object in screen space
Vector3 objScreenPos = Camera.main.WorldToScreenPoint(Target.transform.position);
// Get the directional vector between your arrow and the object
Vector3 dir = (objScreenPos - rt.position).normalized;
// Calculate the angle
// We assume the default arrow position at 0° is "up"
float angle = Mathf.Rad2Deg * Mathf.Acos(Vector3.Dot(dir, Vector3.up));
// Use the cross product to determine if the angle is clockwise
// or anticlockwise
Vector3 cross = Vector3.Cross(dir, Vector3.up);
angle = -Mathf.Sign(cross.z) * angle;
// Update the rotation of your arrow
rt.localEulerAngles = new Vector3(rt.localEulerAngles.x, rt.localEulerAngles.y, angle);
}
For the above code, I suppose that:
You are only using one main camera, you may need to change this
Your arrow is on the Canvas, by default pointing upwards (when its rotation is (0, 0, 0))
You are using a Canvas in Render Mode: Screen Space - Overlay. The above code would be different if the Canvas were in World Space.
As a high-level overview:
Find direction from UI/view-plane centre to 3D object
Project direction onto UI/view-plane (using forward as normal vector), and normalize
Point 2D arrow toward projected direction
Thank You all Guys, I got an Answer for two Situations :
First One: When the two objects are in 3D
public GameObject flesh;
public GameObject target;
// Start is called before the first frame update
void Start()
{
flesh.transform.position = Camera.main.ScreenToWorldPoint(new Vector3( Screen.width/2, Screen.height/2,1));
}
// Update is called once per frame
void Update()
{
var dir = Camera.main.WorldToScreenPoint(target.transform.position) -
Camera.main.WorldToScreenPoint(flesh.transform.position);
var angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
flesh.transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
}
Second: When the Flesh is an Image that Has RectTransform,
, this solution Inspirational from #kevernicus
public GameObject Target;
RectTransform rt;
void Start()
{
rt = GetComponent<RectTransform>();
}
void Update()
{
// Get the position of the object in screen space
Vector3 objScreenPos = Camera.main.WorldToScreenPoint(Target.transform.position);
// Get the directional vector between your arrow and the object
Vector3 dir = (objScreenPos - rt.position).normalized;
float angle = Mathf.Rad2Deg * Mathf.Atan2(dir.y, dir.x);
rt.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
}
Use Unity's built in Transform.LookAt function

How to lock position of physic body in z axis in Unity 3D

I am developing a 2.5D game. In that game I want my character (which has Rigidbody component attached to) to just move on x and y axises. So I use this code snippet:
private void LockZAxis () {
Vector3 currentPosition = _rigidbody.position;
currentPosition.z = 0;
_rigidbody.position = currentPosition;
}
I call this LockZAxis method in the end of both Update, FixedUpdate and LateUpdate. But it doesn't work. When my character run forward for a while, its z position is still changed.
For additional information, in my code, there are two times I manipulate the position of RegidBody. The first is when my character jump, that time I use this:
jumpVelocityVector = Vector3.up * jumpForceUp + transform.forward * jumpForceForward;
_rigidbody.velocity = jumpVelocityVector;
And each frame when I want my character to move a bit faster, so in the update method, I have this:
void Update () {
Vector3 newPosition = transform.position + transform.forward * speed * Time.deltaTime;
newPosition.z = 0;
_rigidbody.MovePosition (newPosition);
LockZAxis ();
}
A rigidbody is used to simulate physics, by setting the rigidbody's position every frame you're essentially teleporting the character every frame. You can restrict movement in z-axis, this will prevent it to move in z-axis when physics is applied, which is what a rigidbody typically is used for.
Here is how to restrict rigidbody positional change:
If you run your LockZAxis() after you've changed the position it should teleport the object to the z-position of 0 every frame. Please make sure that the z-axis is the correct axis. You can debug this by pausing a running game and manipulating the Transform values to see how each axis moves your Object.
Here is how you can do it with C# Script:
Freeze All Positions
rigidbody.constraints = RigidbodyConstraints.FreezePosition;
Freeze Specific Positions:
rigidbody.constraints = RigidbodyConstraints.FreezePositionY | RigidbodyConstraints.FreezePositionZ;
Unity Documentation
Is physics gravity set to only affect the Y position ?
Physics.gravity = new Vector3(0, -1.0F, 0);
And set these also
rigidbody.angularVelocity = Vector3.zero;
rigidbody.velocity.z=0;
make sure your rigidbody is set to kinematic since you are using Rigidbody.moveposition() and using moveposition() will directly effect velocity internally on a kinematic rigidbody
Try using moveposition() for you jump instead of velocity

Unity: Instantiate GameObject at top of screen plus it's own height

I have this code that instantiates GameObjects at the top of the screen and then they fall down.
float RandX = GetRandomXPos();
float RandY = screenSize.y;
Vector3 ballPos = new Vector3(RandX,RandY,0);
GameObject clone = Instantiate(BallPrefab, ballPos, transform.rotation) as GameObject;
This works fine but it spawn them at the top of the screen so they just blink into existence. I want to spawn them at the top of the screen plus the height of the prefab so that it can appear out of view and then fall down into view.
What is the best way to get that height that I need to offset by?
To get the size of the prefab you need to get the information of the Renderer that the prefab has.
To do this you have to get the component in its hierarchy, you can do this by using GetComponentInChildren<Renderer>()
Once you got the Renderer, you can access the bounds size with renderer.bounds.size
This is a Vector3 which has the dimensions of the object, height being the Y component.
You may have more than one Renderer in a prefab so you will need to get them all with GetComponentsInChildren<Renderer>(). and calculate the sum of all the bounds using bounds.Encapsulate
var renderer = target.GetComponentInChildren<Renderer>();
var height = renderer.bounds.size.y;

Move 3d object to mouse click position in unity

I create a scene in unity. I add camera and 3d unity cube object to the scene. To move object to mouse click position, I add raycast and it works.
But I want to use my model. I have an obj and mtl file. I copy them to Assets folder. I use below code. The object moves but not exact my click position.
Plane plane = new Plane(Camera.main.transform.forward, transform.position);
pos = Input.mousePosition;
Ray ray = Camera.main.ScreenPointToRay(pos);
float dist;
if (plane.Raycast(ray, out dist))
{
Vector3 v = ray.GetPoint(dist);
objectPos = v;
}
transform.position = objectPos;
Origin of model is not (0,0,0). I cut this object from a big 3d model.
I try to move to (Screen.width/2, Screen.Height/2, 0.0f) and result is below
EDIT
EDIT 2
I add box collider to object. But the collider position is not same as the object.
You have to move the null point of the object or save a variable which is representing the vector by which it has to be moved and add it to the objectPos befor setting the transform.position

Instantiate prefab around a object

I have a scene with a body maked with makehuman, and I need to add a simple prefab (a torus) around the arm of the body when the user touch the arm.
I tried:
Instantiate the prefab in the point where the user touch, but the prefab apear in the border of the arm.
Instantiate the prefab in the center of the arm, with this code:
float radio = hit.transform.collider.radius; // the arm has a capsuleCollider
Ray r = Camera.main.ScreenPointToRay(Input.GetTouch(0));
Vector3 origin = r.origin;
float distance = (origin - hit.point).magnitude;
RaycastHit ou;
Vector3 position = hit.point;
Ray r2 = new Ray(r.GetPoint(distance + 10f), -r.direction);
if (cc.Raycast(r2, out ou, distance + 10f))
position = (hit.point + ou.point) / 2;
Instantiate(Prefab, position, Quaternion.identity);
This try to Select the center of the arm and initialite a torus.
The second option works in some cases, but the general impression is that is the wrong way to do it.
How can I add a prefab around a collider? or, how can I modify the mesh to add a visual indicator?
This should work a lot better as well as look a lot cleaner:
Vector3 center = hit.transform.collider.bounds.center;
Instantiate(Prefab, center, Quaternion.identity);
hit.transform.collider is a vital part of this process and you got that part. collider.bounds is the bounding box that surrounds the collider (http://docs.unity3d.com/ScriptReference/Collider-bounds.html), and bounds.center is the center of the bounding box (http://docs.unity3d.com/ScriptReference/Bounds-center.html). The Vector3 that bounds.center returns is where you want to spawn your prefab.
From there, you should be able to rotate the prefab to the desired angle and perform any number of operations you want.