Unity 3D Strange Behaviour Attaching Loaded Prefab to Object - c#

Hey Everyone could use some advice. On running my program I'm loading a prefab and attaching it to a bone of a character object.
The character is called "MrPresident" and it has an attach point called "genSuit_AttachHandRight". Through another script I call this function to load a prefab called 'basicBriefcase', attach it to this attach point, and attempt to set its position to (0, 0, 0) relative to the attach point.:
public void Equip(string mode)
{
// on startup load basic equipment
if ( mode == "Initialize" )
{
// right hand equipment
string modelRightAttachPoint = championProperties.modelRightAttachPoint;
string defaultRightHandType = championProperties.defaultRightHandType;
string defaultRightHandObject = championProperties.defaultRightHandObject;
if ( defaultRightHandType == "custom")
{
string championName = this.name;
GameObject equipment = Instantiate(Resources.Load("Equipment/" + championName + "/" + defaultRightHandObject, typeof(GameObject))) as GameObject;
equipment.name = defaultRightHandObject; // gets rid of the (Clone)
equipment.transform.parent = GameObject.Find(modelRightAttachPoint).transform;
equipment.transform.position = Vector3.zero;
}
}
}
The prefab is loading in the Hierarchy and appears to be attached to the attach point when I press play, but its nowhere to bee seen in the Game view. When I try debugging by running this:
public void Update()
{
print(GameObject.Find("basicBriefcase").transform.position);
}
The value is flying all over the place. The first line of output is (0, 0, 0) on the first Update, but the position after that its changing every cycle through update. The character its attached to is animated but its just sitting in place, and I would think the position relative to the attach point should remain constant at what i set it. If I remove the line
equipment.transform.parent = GameObject.Find(modelRightAttachPoint).transform;
Then the position stays at (0, 0, 0), but because i havent attached to the approprite parent (the attach point) the position is obviously wrong. Am I missing something here? Thanks for your help.
EDIT
I discovered that i should be using
equipment.transform.localPosition = Vector3.zero;
instead of .position but now I'm having a whole new problem. Where before the equipment was attaching ok to the parent, now it just dissapears when I try to set the parent.
If i execute this statement
GameObject equipment = Instantiate(Resources.Load(startPath + defaultRightHandObject, typeof(GameObject))) as GameObject;
The object appears in the hierarchy with no parent. But if I try to do:
Transform trans = GameObject.Find("MrPresidentAttachRightHand").transform;
equipment.transform.SetParent(trans, true);
The object just dissapears. Its nowhere to be found, its not attached to the parent it should be attached to. I've tried using .parent and .SetParent. I've tried reloading the prefab it is supposed to attach to. If i try to set the parent to something like Camera.main.transform, it works. If i set it to the highest parent in the prefab i'm trying to attach it to, it works. But if i try to attach it to the attach point, it dissapears. What am I doing wrong?

Just don't use transform.parent to set the parent of a GameObject. Use the transform.SetParent function which let's you specify if the object should be place relative to the parent.
That should be:
Transform trans = GameObject.Find(modelRightAttachPoint).transform;
equipment.transform.SetParent(trans, true);
If you still have issue, pass false to it:
Transform trans = GameObject.Find(modelRightAttachPoint).transform;
equipment.transform.SetParent(trans, false);

Related

Instantiate a Object below another object instead of above

First of all, i created a GIF to show what is currently happen.
GIF with my current problem
and
Awhat I want
I have a List of GameObject which add the bodyParts temp and Instantiate it in the correct time and position.
Now this is working like expected, but i want this new bodyParts below another object instead of above.
As you can see the Head is "under" the new body parts, but it should always on Top and every new part should spawn under the next. (only should looks like! I dont want to change the Z position.)
i tried :
bodyParts.transform.SetAsFirstSibling();
to change the Hierarchy, but this do nothing. I also can drag and drop the Clones to a other position in Hierarchy but they just stay at the same position (above another).
Is this possible and what should i have to do?
Here some of my Code which makes the process:
private void CreateBodyParts()
{
if (snakeBody.Count == 0)
{
GameObject temp1 = Instantiate(bodyParts[0], transform.position, transform.rotation, transform);
if (!temp1.GetComponent<MarkerManager>())
temp1.AddComponent<MarkerManager>();
if (!temp1.GetComponent<Rigidbody2D>())
{
temp1.AddComponent<Rigidbody2D>();
temp1.GetComponent<Rigidbody2D>().gravityScale = 0;
}
snakeBody.Add(temp1);
bodyParts.RemoveAt(0);
}
MarkerManager markM = snakeBody[snakeBody.Count - 1].GetComponent<MarkerManager>();
if (countUp == 0)
{
markM.ClearMarkerList();
}
countUp += Time.deltaTime;
if (countUp >= distanceBetween)
{
GameObject temp = Instantiate(bodyParts[0], markM.markerList[0].position, markM.markerList[0].rotation, transform);
if (!temp.GetComponent<MarkerManager>())
temp.AddComponent<MarkerManager>();
if (!temp.GetComponent<Rigidbody2D>())
{
temp.AddComponent<Rigidbody2D>();
temp.GetComponent<Rigidbody2D>().gravityScale = 0;
}
snakeBody.Add(temp);
bodyParts.RemoveAt(0);
temp.GetComponent<MarkerManager>().ClearMarkerList();
countUp = 0;
}
}
Finally i found the working Solution.
It has nothing to do with which hierarchy order GameObjects spawn in.
Just the Layer and the LayerOrder are responsible for it.
So I give my parent object a specific layer name (manually in the inspector under "Additional Settings" or programmatically)
I chose the programmatic way...
Any newly spawned GameObject that is Child would get a lower number
yourGameObject.GetComponent<Renderer>().sortingLayerID = SortingLayer.NameToID("Player");
yourGameObject.GetComponent<Renderer>().sortingOrder = -snakeBody.Count;

Detecting if 2D Collider is touching Mouse Pointer from Other script in Unity 2D

I am working on a project using Unity2D, the goal is to be able to reference other main scripts to gain information from. In this specific case, I need to detect if the mouse pointer is touching a collider, however, from a separate script.
Usually, I would be able to create a boolean, and on mouse over set it to true, on mouse exit set it to false, like this:
bool isHovered;
void OnMouseEnter() {
isHovered = true;
}
void OnMouseExit() {
isHovered = false;
}
However, in the script, instead of doing this for each individual script, I would like to reference another script, like this:
public GameManager g;
void Update() {
if (g.IsTouchingMouse(gameObject)) { //Code }
}
But there's multiple problems with this. In my game manager class, I would need something like this
public bool IsTouchingMouse(gameObject g) { return value }
Which there is multiple issues with this, because I don't have a way to register the OnMouseEnter and OnMouseExit events for those objects on another script, and I don't have a way to store the values for every single gameObject to ensure this will work for every object without having to manually modify this script.
I'm looking for two things, #1, how can I detect mouseovers on objects from scripts who's parents are not that gameObject, two, are there any ideas on how I would go about creating a function to return this variable instantly?
Somewhat annoying, but I figured out a solution a few minutes after posting. So I will share it here.
public bool IsTouchingMouse(GameObject g)
{
Vector2 point = Camera.main.ScreenToWorldPoint(Input.mousePosition);
return g.GetComponent<Collider2D>().OverlapPoint(point);
}
What this code is basically doing, is making a function that takes a gameObject as an input, creates a vector2 based on the position of the mouse cursor in world space, and then returns weather or not the 2D collider that the object contains is actually touching this imaginary point, the variable "point" should be interchangable with any 2D world space location. I was pretty much overcomplicating the entire issue.
Find two resources you like and import them into Unity's Assets and set their Texture Type to Cursor
Create a new script and mount it to an empty object.
Open the script, create three public variables of type Texture2D, return to Unity and drag your favorite pointer map to the variable window.
To set the object label, we judge which pointer to change by customizing the label; so first set the label for the object. For example, the ground, I added the Ground tag to it, the wall added the Wall tag; the column added the Cylinder tag.
Write a method to change the mouse pointer, where the main API for changing the pointer is Cursor.SetCursor()
void setCursorTexture()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);//Define the ray pointed by the mouse in the game window
RaycastHit hitInfo; //Information of ray collision
if (Physics.Raycast(ray, out hitInfo))//Determine whether to hit the object
{
// switch pointer
switch (hitInfo.collider.gameObject.tag)
{
case "Ground":
Cursor.SetCursor(groundCr, new Vector2(16, 16), CursorMode.Auto);
break;
case "Wall":
Cursor.SetCursor(wallCr, new Vector2(16, 16), CursorMode.Auto);
break;
case "Cylinder":
Cursor.SetCursor(CylinderCr, new Vector2(16, 16), CursorMode.Auto);
break;
default:
Cursor.SetCursor(null,Vector2.zero,CursorMode.Auto);
break;
}
}
}
Implement this method in the Update() method called every frame.
END (you can go to run the program) Thank you and hope to help you
This method works without exception. To solve the problem try the following method:
public Collider2D collider; // target Collider
void Update()
{
var mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition);
if (collider.bounds.IntersectRay(mouseRay)) Debug.Log("Mouse on collider..");
}
For New Input System?
But Input.mousePosition gives a new system error in Unity. To solve this problem, call the mouse position as shown below:
var mousePos = Mouse.current.position.ReadValue();
You can also check if (new input system enabled) is active as below:
#if ENABLE_INPUT_SYSTEM
var mousePos = Mouse.current.position.ReadValue();
#else
var mousePos = Input.mousePosition;
#endif
then follow the direction of the camera ray:
var mouseRay = Camera.main.ScreenPointToRay(mousePos);

"Selection.objects" doesn't seem to work in open prefab hierarchy window

I want to select several game objects in open prefab but Selection.objects, which normally works for scene game objects, like on this picture:
doesn't seem to work properly for prefab objects.
Actually, the Inspector window displays common properties of the selected objects as expected but in the Hierarchy window they are not selected (highlighted) like on the picture above.
Here is how my script looks like in my MyEditor : EditorWindow:
int childObjectsCount;
string prefabName;
...
if (AssetDatabase.LoadMainAssetAtPath(prefabName) is GameObject prefab)
{
if (AssetDatabase.OpenAsset(prefab))
{
if (childObjectsCount > 0)
{
List<GameObject> innerObjects = FindInnerObjects(prefab);
Selection.objects = innerObjects.ToArray();
}
}
}
The reason it doesn't work is that the Gameobject specified by Selection.objects is not the Gameobject in the current PrefabStage, so we need to get the current PrefabStage
// LoadPrefab
GameObject assetGameObject = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
// OpenPrefab
AssetDatabase.OpenAsset(assetGameObject);
// GetPrefabStage
PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
// Get Root Gameobejct Of Prefab Which We Are Editing
GameObject instanceGameobject = prefabStage.prefabContentsRoot;
// Then Select It , it works well(highlight)
Selection.objects = new Object[] {instanceGameobject };

How can I change the parent of a GameObject instantiated via PrefabUtility.InstantiatePrefab?

Usually you would instantiate a clone of a prefab in a script using
var obj = (GameObject)Instantiate(....);
and than change it's parent using
var obj.transform.SetParent(...);
However I'm writing on a little editor script for instantiating prefabs over the menu entries that should keep their link to the prefab. I do it in a static class like:
public static class EditorMenu
{
// This is a reference that I already have and is not null
privtae static Transform exampleParent;
[MenuItem("Example/Create Clone")]
private static void CreateClone()
{
var prefab = AssetDatabase.LoadAssetAtPath("Example/Path/myObject.prefab", typeof(GameObject));
var obj = (GameObject)PrefabUtility.InstantiatePrefab(prefab);
obj.transform.position = Vector3.zero;
obj.transform.rotation = Quaternion.identity;
Selection.activeGameObject = obj;
}
}
I had to use
(GameObject)PrefabUtility.InstantiatePrefab(prefab);
because Instantiate() doesn't keep the link to the prefab but creates a clone.
So the above code works great so far and the object is inserted to the scene as if I would drag and drop it.
Now I would like to change this newly instantiated objects parent to parent so I added the line
obj.transform.SetParent(exampleParent, true);
I also tried
obj.transform.parent = exampleParent;
But both throw an exception:
Setting the parent of a transform which resides in a prefab is disabled to prevent data corruption.
UnityEngine.Transform:SetParent(Transform)
prefab and thereby obj are the topmost GameObject of the prefab so I thought I am setting the parent of the whole instantiated object not any transform inside of the prefab hierachy.
How can I change the parent of a GameObject instantiated via PrefabUtility.InstantiatePrefab?
Update
A workarround I just tried was to actually use Instantiate and do
var obj = Object.Instantiate(prefab, cancel.transform);
// remove the added "(clone)" suffix
obj.name = prefab.name;
obj.transform.SetParent(cancel.transform);
however as mentioned before this doesn't completely keep the prefb functionality intact .. so it only allows me to Revert but not to Apply changes. As workaround it might be good enough however since in my case I use it as a shortcut for instantiating a prefab which users are not really supposed to change later...
Sorry I just found the issue:
I didn't add it since the error message was confusing/bad formulated.
I was using Resources.FindObjectsOfTypeAll for checking if the exampleParent of type SomeType was in the scene.
Following Unity's example which should exclude prefabs I used
// Validation for the Menu item
[MenuItem("Example/Create Clone", true]
private bool TargetAvailable()
{
foreach (var target in (SomeType[])Resources.FindObjectsOfTypeAll(typeof(SomeType))
{
if (target.hideFlags == HideFlags.NotEditable || target.hideFlags == HideFlags.HideAndDontSave)
continue;
if (!EditorUtility.IsPersistent(target.transform.root.gameObject))
continue;
exampleParent = target.transform;
return true;
}
exampleParent = null;
return false;
}
But this seems actually to be wrong and not working since it allways returned me the SomeType reference from the prefabs! (I already found it a bit strange that they do
!EditorUtility.IsPersistent(target.transform.root.gameObject))
continue;
I'm not sure if that ! maybe is a type in their example code?!
So the error which sounds like setting the parent was not allowed actually means and should say
setting the parent to a Transform which resides in a prefab is not allowed ...
than I would have found the actual issue in the first place.
So again as a workaround until I can figure out that FindObjectsOfTypeAll thing I switched to Object.FindObjectOfType instead assuming my target will always be active in the scene. And using SetParent works now together with PrefabUtitlity.InstantiatePrefab.
How can I change the parent of a GameObject instantiated via PrefabUtility.InstantiatePrefab?
This is right, but make sure cancel is also an instantiated object.
obj.transform.parent = cancel.transform;

Instantiating a GameObject causes said object to have its Transform destroyed?

New to Unity and C#
This is actually just a small issue that I'm curious about... I ran into it while tweaking this code in a (failed) attempt to make it work. I have been trying to get this code to work for a few hours now.
Anyway, when this code is executed, there is only one error, but it appears 3 times. It says "Can't destroy Transform component of 'Pillar1'. If you want to destroy the game object, please call 'Destroy' on the game object instead. Destroying the transform component is not allowed."
First time I've gotten THAT.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlatformGenerator : MonoBehaviour {
public GameObject Single;
private GameObject NewPillar;
private int PillarCount;
private bool run;
private int px;
private int py;
private int pz;
void Start () {
px = 0;
py = 0;
pz = 0;
bool run = true;
PlatformCreate ();
}
void Update () {
if (run) {
PlatformCreate ();
if (PillarCount == 3) {
run = false;
}
}
}
void PlatformCreate () {
PillarCount +=1;
Single.transform.position = new Vector3 (px, py, pz);
NewPillar = Instantiate (Single, Single.transform);
NewPillar.name = "Pillar" +PillarCount;
px += 2;
}
}
1) Using following statements causes undesired result or throw error -
NewPillar = Instantiate (Single, Single.transform);
OR
NewPillar = Instantiate (Single);
NewPillar.transform.parent = Single.transform;
2) You can bypass this by using following code -
NewPillar = Instantiate (Single, Single.transform.position, Single.transform.rotation);
Explanation : Single is a prefab and not an actual object in the scene. Also transform is a component which determines the Position, Rotation and Scale of object in the scene. As per Unity3D, since Single is a prefab, using it's transform component directly is disabled to prevent data corruption. Which is why we get error while using statements in point 1 above.
But we can use the position, rotation and scale data stored within the prefab's transform component. Which lets us use the statement in point 2 above.
You can not call Destroy on references of type 'Transform'. What the error is saying that you need to pass the GameObject to the Destroy method, and not the Transform.
I am guessing that the part of the code 'destroying' a transform is missing, but in any case, my guess is like this :
Destroy(transform); // -1
or
Destroy(pillar1.transform); //Where pillar1 is a Gameobject -2
or
Destroy(pillar1); // Where pillar1 is a Transform -3
Replace
-1 With
Destroy(gameObject);
-2 With
Destroy(pillar1); //Where pillar1 is a Gameobject -2
-3 With
Destroy(pillar1.gameObject);
The error indicates that there is an object Pillar1 already and so we assume it is one of the subsequent calls to PlatformCreate.
The call to Instantiate includes all children. Thus you clone Single to Pillar1 and reparent the new game object to Single. In the next call both of them are cloned. So Pillar2 will be Single and Pillar1
Although I don't see why this should not work, I suspect an internal error. Are you sure this is what you want (maybe you just wanted to specify the position and forgot the rotation (s. Instantiate)? If it is what you wanted then try to split the Instantiate process into 2 steps:
Call Instantiate without parent specification
pillar2.SetParent(Single)

Categories