Unity: When parenting cloned object, it instantly unparents - c#

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?

Related

Spawn bombs and throw them to the player in Unity

I got a small arena where the player can move on. At the sides of the area there are spawners. These spawners instantiate bombs and should throw them at the player.
For the direction I actually use
transform.lookAt(playerTransform);
So this is a rough map
So the spawners are rotating around the map. They move from one point to the next point.
My bomb object got a rigidbody attached and the gravity is activated. I just need to find out how to make a spawner throwing a bomb to the player.
public class BombSpawner : MonoBehaviour
{
[Range(0, 3)]
[SerializeField]
private int nextPointIndex; // set the first targetpoint
private Vector3[] targetPoints = {
new Vector3(-15,0,15),
new Vector3(15,0,15),
new Vector3(15,0,-15),
new Vector3(-15,0,-15)};
private float movementSpeed = 10;
GameObject bombPrefab;
Transform player;
private void Start()
{
bombPrefab = Resources.Load(StringCollection.BOMB) as GameObject;
player = Globals.GetPlayerObject().transform;
}
private void Update()
{
transform.LookAt(player); // set the object rotation
Vector3 nextPoint = targetPoints[nextPointIndex]; // get the target point
transform.position = Vector3.MoveTowards(transform.position, nextPoint, movementSpeed * Time.deltaTime); // move the spawner
if (transform.position == nextPoint) // point reached? set a new point
{
if (nextPointIndex < targetPoints.Length - 1)
nextPointIndex++;
else
nextPointIndex = 0;
}
}
}
So I could write a method like this
void SpawnBomb()
{
GameObject spawnedBomb = Instantiate(bombPrefab);
}
but how do I achieve the throwing mechanic? For a first try, the targetPoint is player.position that should be fine.
You need to get the direction from spawner to the current position of the target, spawn the bomb and add force to that bomb using the direction you just got.
In order to do this you should substract your spawner's position from your target position.
Vector3 dir = target.transform.position - transform.position;
Now that you have the direction you can spawn your bomb and AddForce() to it. To add force you need to call the Rigidbody component of your spawned Bomb, like this:
spawnedBomb.GetComponent<Rigidbody>().AddForce(dir.normalized * force, ForceMode.Impulse);
Where dir is the direction towards the target (normalized - so the distance doesn't matter) and force is pretty much the speed of the bomb.
Here you can read more about Rigidbody.AddForce.

Why teleportation doesn't work right?

I'm trying to recreate portal scene using
this video. The link to the scene assets and scripts is under the video.
I'm facing a problem with teleportation:
The player should walk through a plane-trigger in the portal to be moved to the location of the second portal. But it seems to me that after teleportation, he appears in the middle of plane-trigger in the second portal. And if he continues moving, he jumps back to the first portal, and then again to the second portal and so on.
Why is it happening and how can it be fixed? As I understand the author of the script, it should teleport player after passing through the trigger, but the script doesn't work like that for me.
Here is the script for teleportation:
using UnityEngine;
using System.Collections;
public class Sender : MonoBehaviour {
public GameObject player;
public GameObject receiver;
private float prevDot = 0;
private bool playerOverlapping = false;
void Start () {
}
void Update()
{
if (playerOverlapping) {
var currentDot = Vector3.Dot(transform.up, player.transform.position - transform.position);
if (currentDot < 10) // only transport the player once he's moved across plane
{
// transport him to the equivalent position in the other portal
float rotDiff = -Quaternion.Angle(transform.rotation, receiver.transform.rotation);
rotDiff += 180;
player.transform.Rotate(Vector3.up, rotDiff);
Vector3 positionOffset = player.transform.position - transform.position;
positionOffset = Quaternion.Euler(0, rotDiff, 0) * positionOffset;
var newPosition = receiver.transform.position + positionOffset;
player.transform.position = newPosition;
playerOverlapping = false;
}
prevDot = currentDot;
}
}
void OnTriggerEnter(Collider other)
{
if (other.tag == "Player")
{
playerOverlapping = true;
}
}
void OnTriggerExit(Collider other)
{
if (other.tag == "Player")
{
playerOverlapping = false;
}
}
}
One way you could do this would be to simply increase the offset of the player's position when teleported, but then you won't achieve the desired smooth transition.
The other method I thought of would be to add a public boolean variable to the Sender script (I'm assuming that the Sender script is on all portals), with a default value of false.
When you teleport, you set the player's position to the new position using the receiving teleporter's position. Because you have access to the receiving teleporter GameObject, you could use GetComponent to get it's Sender script. You could then set this boolean variable to true before teleporting the player.
Once the player is teleported, the OnTriggerEnter method is automatically invoked, which is your problem. You could put an if statement in OnTriggerEnter so that when it was activated, it would only teleport IF the boolean variable was false. Because it's true when the player teleports, you aren't allowed to teleport back until you leave the portal trigger, then you could set the boolean variable back to false. This would mean that the normal behavior of walking through a portal is unaffected, because the default boolean value is false.
Also, this code would probably be much better if you had your actual teleportation in OnTriggerEnter instead of Update, as Absinthe said.
Hope this helps!
I don't know where you're going with your original code (no pun intended) and I didn't watch through the video (TLDR) but you'll need to rethink this from scratch. Using the Update method is totally inappropriate; update fires on every frame - you want to teleport every frame?
Instead use the OnTriggerEnter method:
void OnTriggerEnter(Collider other)
{
other.transform.position = theVectorYouWantToTeleportTo
}

how to snap two objects in runtime in unity?

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)

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.

Camera doesn't smoothly interpolate its rotation

I'm facing a problem in my scene. What i'm doing is there are two panels both panel has one button which performing OnClick() listener with the method name RotateCamera().
Also in hierarchy there is a MainMenu gameobject which attached with one script. When I play the scene and click on panel one button than MainCamera rotate with 90 angle to the direction of second panel. Script works fine but I want to smoothly rotate camera to that panel when I click on that button - right now, it is rotating instantly. I don't know what I'm doing wrong on this script.
public class CameraSmooth : MonoBehaviour {
private bool check = false;
private Transform from;
private Transform to;
void Start(){
from = Camera.main.transform;
}
void Update(){
if (to != null) {
Camera.main.transform.rotation = Quaternion.Slerp (from.rotation, to.rotation, 3 * Time.deltaTime);
}
}
public void RotateCamera(){
if (!check) {
Camera.main.transform.Rotate (0,90f,0f);
to = Camera.main.transform;
check = true;
}
else if (check) {
Camera.main.transform.rotation = Quaternion.identity;
to = Camera.main.transform;
check = false;
}
}
}
The main problem here is that you're calling Camera.main.transform.Rotate() in RotateCamera(). This causes the rotation to be applied to the camera immediately, resulting in the instant rotation you're seeing.
Another small misconception - since Transform is a reference type, from and to always actually point to the same instance (the Transform of the camera)! Lastly, the value of 3 * Time.deltaTime will always average around 3/60, and will likely never end up close to 1, which is needed for an interpolation to be complete. As such, the following line:
Camera.main.transform.rotation = Quaternion.Slerp (from.rotation, to.rotation, 3 * Time.deltaTime);
will not be able to do anything even if the first issue is addressed.
The situation calls for a different solution, in my view:
Storing the target rotations in Quaternion variables rather than setting them directly on the Transform, and keeping track of the camera's initial rotation
Maintaining a timer which keeps track of the progress of the rotation (or you can abandon Slerp() and just apply an incremental rotation directly, that's also a valid approach)
Here's my suggested update to your code:
public class CameraSmooth : MonoBehaviour {
private bool check = false;
private float rotationProgress = 1;
private Quaternion initial;
private Quaternion from;
private Quaternion to;
void Start(){
// Cache the starting rotation, so we can calculate rotations relative to it
initial = Camera.main.transform.rotation;
}
void Update(){
if (rotationProgress < 1) {
rotationProgress += 3 * Time.deltaTime;
Camera.main.transform.rotation = Quaternion.Slerp (from, to, rotationProgress);
}
}
public void RotateCamera(){
from = Camera.main.transform.rotation;
rotationProgress = 0;
if (!check) {
to = initial * Quaternion.Euler(0,90f,0f);
check = true;
}
else if (check) {
to = initial;
check = false;
}
}
}
(Haven't tested this code, so please let me know if there are any issues.) Hope this helps! Let me know if you have any questions!

Categories