how to snap two objects in runtime in unity? - c#

this is the 3d model i wanted to connect another model like this to its silver connectors on top side and also another model to right side(so do help me to snap it)I want to know how to snap two 3D objects together in runtime. i.e during "play" the user must be able to dragup, down, left, right to snap one object with the other object .for example like "lego", ie. one 3D object should snap to another 3D object. How would I achieve this?
This is the code I use for dragging:
using System.Collections;
using UnityEngine;
public class drag : MonoBehaviour {
Vector3 dist;
float posX;
float PosY;
void OnMouseDown()
{
dist = Camera.main.WorldToScreenPoint(transform.position);
posX = Input.mousePosition.x - dist.x;
PosY = Input.mousePosition.y - dist.y;
}
void OnMouseDrag()
{
Vector3 curPos = new Vector3(Input.mousePosition.x - posX, Input.mousePosition.y - PosY, dist.z);
Vector3 worldPos = Camera.main.ScreenToWorldPoint(curPos);
transform.position = worldPos;
}
}

There are many many ways to accomplish this. Here is the first method I came up with. If we break down what might comprise a snap, we can muster up a script to attach to each snappable child:
1) Assign tag "parentblock" to the object you are dragging around.
2) Attach a trigger collider to both the parent object and the snappable child object.
3) When the the dragged object enters the collision area, snap it to the parent.
4) Store the offset from the parent to maintain its position once snapped.
bool snapped = false;
GameObject snapparent; // the gameobject this transform will be snapped to
Vector3 offset; // the offset of this object's position from the parent
Update()
{
if (snapped == true)
{
//retain this objects position in relation to the parent
transform.position = parent.transform.position + offset;
}
}
void OnTriggerEnter(Collider col)
{
if (col.tag == "parentblock")
{
snapped = true;
snapparent = col.gameObject;
offset = transform.position - snapparent.transform.position; //store relation to parent
}
}
Keep in mind this will only snap child objects to 1 master parent object that you are dragging around. It should go without saying you may need to tweak this for it to perform specifically to your project like you want, but hopefully this gets you in the right direction. (FYI I have not tested this so am not guaranteeing it will work right out of the gate)

Related

Unity: When parenting cloned object, it instantly unparents

I'd like to clone a object and set it as child of another object using the following code:
spawnClone = Instantiate(other.gameObject, position, rotation, plate.transform) as GameObject;
Destroy(spawnClone.GetComponent<Collider>());
//spawnClone.transform.SetParent(plate.transform);
spawnClone.GetComponent<Rigidbody>().isKinematic = true;
spawnClone.SetActive(true);
Unfortunatly the parenting doesn't work and the cloned object is just placed in the root of the scene. If I try to just move the clone object into any other object in the hierarchy, it just gets out of it again right away (while playing).
Is there anything im doing wrong or that I don't see?
Thanks in advance!
EDIT: For a better understanding
I have a gameobject (plate) that has a child object(trigger). The trigger has a mesh collider (trigger enabled) and a script. The script has the following OnTriggerEnter-Method:
private void OnTriggerEnter(Collider other)
{
if (burgerGameManager.gameElement.Contains(other.gameObject.tag)) {
burgerGameManager.addToPlayerOrder(other.gameObject);
other.gameObject.SetActive(false);
Vector3 position;
if (transform.childCount > 1) {
Transform lastChild = transform.GetChild(transform.childCount - 1);
position = lastChild.position;
} else {
position = plate.transform.position;
position.y = position.y + plate.GetComponent<MeshRenderer>().bounds.size.y;
}
spawnClone = Instantiate(other.gameObject, position, other.gameObject.transform.rotation, gameObject.transform) as GameObject;
Destroy(spawnClone.GetComponent<Collider>());
//spawnClone.transform.SetParent(plate.transform);
spawnClone.GetComponent<Rigidbody>().isKinematic = true;
spawnClone.SetActive(true);
}
}
This method is supposed to trigger when the player drops a object above the plate. That object should hide and spawn a clone of it to appear on the plate.
Later on that script is supposed to be attached to the spawned clone aswell, so that the player can stack objects on the plate. But I did not yet come to that since I didn't even get the first step working as we can see...
spawnClone.transform.parent = plate.transform;
Should do it, if the plate is supposed to be the parent
Edit
I've recreated your code as follows
public class Test : MonoBehaviour{
private void OnTriggerEnter2D(Collider2D other)
{
Vector2 position;
other.gameObject.SetActive(false);
position = this.gameObject.transform.position;
position.y += 0.5f;
if (transform.childCount >= 1)
{
Transform lastChild = transform.GetChild(transform.childCount - 1);
position = lastChild.transform.position;
position.y += 0.5f;
}
GameObject spawnClone = Instantiate(other.gameObject, position, Quaternion.identity);
Destroy(spawnClone.GetComponent<Collider2D>());
spawnClone.GetComponent<Rigidbody2D>().isKinematic = true;
spawnClone.transform.parent = this.gameObject.transform;
spawnClone.SetActive(true);
}
}
And I got this behaviour, which is what I think you want
My only conclusion here is that
burgerGameManager.addToPlayerOrder(other.gameObject);
may be messing with the parenting. Maybe try moving this at the end of the function and see what happens?

Unity3D: Camera is unable to move with firstperson script

The object is a satellite type so I have a script to move the object in an orbit using its position. And I also have a script to move the satellite object in first person mode.
Problem is that the user is unable to move the camera by the axis "Mouse X" and "Mouse Y" for first person mode. The problem is happening because the orbit script and the first person script are both controlling the camera so it is unable to move. In the game, when you move the mouse you see the camera jerk and when I disable the orbit script, the first person works.
How can I fix this problem?
A solution can be to use only position for the orbit, and the rotation for first person control?? The first person mode will not control the objects position only rotation. The orbit will not control the objects rotation only position.
Thank you!
Orbit Script
private void FixedUpdate()
{
transform.RotateAround(centerOfOrbit.position, orbitAxis, orbitSpeed * Time.deltaTime);
desiredOrbitPosition = (transform.position - centerOfOrbit.position).normalized * orbitRadius + centerOfOrbit.position;
transform.position = Vector3.MoveTowards(transform.position, desiredOrbitPosition, Time.deltaTime * orbitAdjustSpeed);
}
First Person mode control (does not work with Orbit)
private void Update()
{
if(allowFirstPerson)
{
x+= Input.GetAxis ("Mouse X") * turnSpeed;
if (allowTiltingForward)
{
y -= Input.GetAxis("Mouse Y") * turnSpeed;
}
y = ClampAngle(y, minVerticalAngle, maxVerticalAngle);
if (minHorizontalAngle != 0 || maxHorizontalAngle != 0)
{
x = ClampAngle(x, minHorizontalAngle, maxHorizontalAngle);
}
Quaternion rotation = Quaternion.Euler(y, x, 0);
Vector3 direction = (rotation * Vector3.forward * 100);
aimPoint = new Vector3(direction.x + aimError, direction.y + aimError, direction.z + aimError);
aimDir = (aimPoint - playerCamera.transform.position).normalized;
}
}
This Top View mode control works with the Orbit script but the top view does not use the same controls as the first person but I can not seem to figure out why one works and the other does not.
Top View mode control (does work with Orbit)
private void Update()
{
if(allowTopView)
{
Ray ray = playerCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
topDownAimPoint = new Vector3(0,0,0);
if(Physics.Raycast(ray, out hit))
{
topDownAimPoint = hit.point;
}
aimPoint = new Vector3(topDownAimPoint.x + aimError, topDownAimPoint.y + aimError, topDownAimPoint.z + aimError);
}
}
My understanding is that your problem is caused by two scripts trying to set the rotation for the same object: a camera. If this is the case, you can create an empty Satellite object and make the camera a child of that object in the Hierarchy. Attaching the Orbit Script to the Satellite object and the First Person script to the camera will allow the camera to move in the satellite's orbit but still have its own independent rotation.
EDIT:
To accommodate a 3d model in this situation, create an empty parent object called SatteliteWrapper (or something similar) and attach the Orbit Script to that object. Then make the camera a child of SatteliteWrapper and make the 3d model a child of the camera. That allows the model to rotate with the camera.

Clamping an object axis in Vuforia AR camera

screenshot hereI want to clamp Y-axis on a cube. I can do it in Unity camera. But, it does not react correctly when I am using it in Vuforia camera.
My problem was that the cube follows the camera. I would like the cube to stay in its position and ignore the AR camera position. I sense it has something to do with WorldtoViewpoint but I cannot figure it out. Can you teach me how to do this please? thankyou
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ClampMovementController : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Vector3 pos = transform.localPosition;
pos.y = Mathf.Clamp(transform.position.y, 0f, 0f);
transform.localPosition = pos;
}
}
This is my solution:
Actually its very simple. The INcorrect concept was my object attached to the AR camera, hence, object position is always moving related to camera position. Now. In order to make the object stays in its place. I need to get its localPosition. First. Store the localposition in Vector3 pos. And then do modification on Vector3 pos. At last, reassign the new value to the object localposition.
public class ClampMovementController : MonoBehaviour
{
public float currentPos;
public GameObject capsule;
void Update()
{
//store the value of object localPosition
Vector3 pos = capsule.transform.localPosition;
//modification on the value
pos.y = Mathf.Clamp(pos.y, currentPos, currentPos);
//rerassign the new value to the object localPosition
capsule.transform.localPosition = pos;
}
}
First of all your cube is moving with the camera because your image target is child of your ARCamera. Therefore, when you move the camera image target moves, then your cube moves as well. Make sure your ImageTarget has no parent.
I did not understand why you have to lock any movement in Y axis. I guess you are doing something wrong with lean touch when you move object. I have not used lean touch but i have achieved this with keyboard inputs. You can convert it to lean touch by modifying following script. Just add these line to your ImageTarget's DefaultTrackableEventHandler script:
//Variables for getting capsule and checking if ImageTarget is tracked
private bool isTracked = false;
private GameObject capsule;
Then create an Update method for getting input from user like this.
void Update()
{
if(isTracked)
{
if(Input.GetKey(KeyCode.W))
{
//using forward for moving object in z axis only.
//Also using local position since you need movement to be relative to image target
//Global forward can be very different depending on your World Center Mode
capsule.transform.localPosition += Vector3.forward * Time.deltaTime;
}
else if (Input.GetKey(KeyCode.S))
{
capsule.transform.localPosition -= Vector3.forward * Time.deltaTime;
}
if (Input.GetKey(KeyCode.A))
{
//Using Vector3.left and right to be make sure movement is in X axis.
capsule.transform.localPosition += Vector3.left * Time.deltaTime;
}
else if (Input.GetKey(KeyCode.D))
{
capsule.transform.localPosition += Vector3.right * Time.deltaTime;
}
}
}
As you can see there is no movement in Y axis because i used forward, left and right vectors to make sure movement in in only X and Y axis.
Last you have to make sure isTracked is updated. In order to do that you have to add isTracked = false; in OnTrackingLost method and isTracked = true; in OnTrackingFound method. Good luck!

dragging/dropping and triggers

I am trying to create a drag and drop functionality. I have a game object that lies on a cube (like a table). I want to allow the player to drag and drop this object to multiple "hot spots". At the same time, while this is a 3D game, they should not be allowed to drag the object off of the table, above it, bellow it, etc. Just drag across the top of the table. The way I have this setup is like this:
On the table top (a cube with a rigid body)I have two planes. This plane is just decorative to let the user see where to drop to. Each of these planes has a child plane (a much smaller area, centered in the parent). This child has a Box Collider (isTrigger= true) that extends high up into the air, like a pole sticking out of the ground)
I then have a cube that has a rigid body and a box collider (isTrigger= true). This cube is instantiated onto the "table" top, over one of the hot spots.
HotSpotCode.cs
public class HotSpotCodeScript : MonoBehaviour {
public void OnTriggerEnter(Collider other){
DraggableObject ds = other.GetComponent<DraggableObject>();
ds.startPos = this.gameObject.transform.position;
}
}
DraggableObject.cs
public class DraggableObject : MonoBehaviour {
float lerpTime = 1f;
float currentLerpTime;
float moveDistance = 10f;
Plane plane;
public Vector3 startPos, endPos;
public bool drag;
Vector3 position;
void Start(){
startPos = transform.position;
endPos = transform.position + transform.up * moveDistance;
}
void OnMouseDown(){
plane = new Plane(Vector2.up, transform.position);
drag = true; // start dragging
}
void OnMouseUp(){
lerp();
drag = false;
}
private void lerp() {
currentLerpTime += Time.deltaTime;
if (currentLerpTime > lerpTime)
{
currentLerpTime = lerpTime;
}
float perc = currentLerpTime / lerpTime;
transform.position = Vector3.Lerp(startPos, endPos, perc);
}
public void Update()
{
position = transform.localPosition;
if (drag)
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
float distance;
if (plane.Raycast(ray, out distance))
{
transform.position = ray.GetPoint(distance);
}
}
}
public void OnTriggerEnter(Collider other){
endPos = other.transform.position;
}
}
This code mostly works somewhat. I am able to drag the object around and it won't leave the plan it it resides on, for short distances. But, the further away I drag the object, then release it, the more likely it is that the object will not snap back to its original start position and will get stuck somewhere along the way. I am not sure if this is an issue with the Lerp() function or maybe how the plane it is dragged across is created. Can anyone help me out? If you need more info, please let me know. I've been working on this for weeks and haven't gotten much further that this.
Another issue that pops up, as well, is that when the object that is being dragged is released, and comes into contact with the hot spot's collider, it stops the object at its exact point. So, if a corner of the cube comes into contact with the collider, the cube will not come to rest centered on the hot spot.

Create scipt to allow dragging and snapping

I have a script that lays out prefabs, on a surface, by creating an array of vectors. Each array contain 14 vectors, which produce evenly spaced locations, on a plane.
public List<Vector3> handEastVectorPoints = new List<Vector3>();
public List<Vector3> handSouthVectorPoints = new List<Vector3>();
public List<Vector3> handNorthVectorPoints = new List<Vector3>();
public List<Vector3> handWestVectorPoints = new List<Vector3>();
Then when a prefab is instantiated, my code grabs the correct vector, from the correct array, and add the prefab, like this:
public IEnumerator addTileInHand(GameObject tile, Vector3 v, float time, Vector3 rotationVector, int pos)
{
Quaternion rotation = Quaternion.Euler(rotationVector);
GameObject newTile = Instantiate(tile, v, rotation);
tile.GetComponent<TileController>().canDraw = true;
tile.GetComponent<TileController>().gameStatus = getCurrentGameState();
tile.GetComponent<TileController>().currentTurn = sfs.MySelf.Id;
PlayersHand[pos] = newTile;
yield return new WaitForSeconds(time);
}
When this function is called, its passed a vector, which comes from the above array. The prefab is created, and added to an array of gameObjects.
What I now want to do is allow the player to drag and drop the to various "hot spots", as defined in the first set of arrays (again, their are 14 vectors) and drop them. If there is already a prefab in that position, I would want it to snap to the new position that just opened up. If the tile is dropped anywhere else, it should snap back to its original location. I also just want the object to be dragged either left or right, no need to up and down or up through the Y axis.
Can anyone point me to similar script that might help or provide assistance?
thanks
Edit: Draggable Script Got this working. You can only drag on the X axis, but for my use case, this works.
public class DraggableObject : MonoBehaviour {
private bool dragging = false;
private Vector3 screenPoint;
private Vector3 offset;
private float originalY;
void Start()
{
originalY = this.gameObject.transform.position.y;
}
void OnMouseDown()
{
screenPoint = Camera.main.WorldToScreenPoint(gameObject.transform.position);
offset = gameObject.transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, originalY, screenPoint.z));
}
void OnMouseDrag()
{
Vector3 cursorPoint = new Vector3(Input.mousePosition.x, originalY, screenPoint.z);
Vector3 cursorPosition = Camera.main.ScreenToWorldPoint(cursorPoint) + offset;
transform.position = cursorPosition;
dragging = true;
}
void OnMouseUp()
{
dragging = false;
}
}
I have done a similar thing for my game. This is quite easy. Follow the following steps and let me know if you have any further queries:
Write a script (let's say HolderScript ) and add a boolean occupied to it. Add an OnTriggerEnter (A Monobehaviour function) to the script and update the boolean accordingly:
Case 1 - Holder is not occupied - Make boolean to true and disable Colliders if the prefab is dropped there, else do nothing.
Case 2 - Holder is occupied - if occupied prefab is removed then in OnTriggerExit function make boolean to false and enable colliders.
Write a Script (let's say DraggableObject) and set the drag and isDropped boolean accordingly and you can use Lerp function for snapping back.
Attach the holder script to the droppable area and DraggableObject script to the draggable objects.
This should solve your issue.

Categories