Instantiate a Object below another object instead of above - c#

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;

Related

Objects continue to be added to the List

I have 2 Targets in the world that I want to add to a list when the Player comes close to them.
I first use the Physics OverlapBox method to return an array of colliders. After this, I run a for loop in which the 2 targets should get added to the list. Only 2 target objects are in the scene but the list gets occupied with hundreds of copies of those objects.
Code Down below
private void TrySelectTarget(bool switchInput)
{
targetArray = Physics.OverlapBox(transform.position, range, Quaternion.identity, targetLayer, QueryTriggerInteraction.Ignore);
for (int i = 0; i < targetArray.Length ; i++)
{
if (targetArray[i].TryGetComponent<Target>(out Target target))
{
availableTargets.Add(target);
}
}
}
I did a deblug.Log on targetarray.Length and it returned 2, so I don't understand why so many objects are being added to the availableTargets List.
I am calling the TrySelectTarget() method in Update().
I am new to c# and programming, so apologies if I am making a stupid mistake.
Thank you for the help.
With your code everytime Physics.OverlapBox returns hits you add the same objects that have already references stored in the list, again. To simply solve the issue of not having duplicates being stored you should check if an object is already referenced (has an entry in the list). Do that by doing:
if (targetArray[i].TryGetComponent<Target>(out Target target))
{
if (!availableTargets.Contains(target)) availableTargets.Add(target);
}
That will not solve the issue of targets not being removed when not in range anymore though. If that is needed then you should change your code so that the list gets cleared before any new references are being added. You could do:
availableTargets.Clear();
targetArray = Physics.OverlapBox(transform.position, range, Quaternion.identity, targetLayer, QueryTriggerInteraction.Ignore);
for (int i = 0; i < targetArray.Length ; i++)
...
The better solution to solve for this problem in general is to make use of OnTriggerEnter() and OnTriggerExit() messages provided through a Rigidbody component. Those Methods only get invoked if at least one of the interacting objects has a Rigidbody component. Add a Rigidbody to your player object and a collider with the size of the detection range/zone size and set this collider to be IsTrigger. If you dont want that physics affects the object just check the IsKinematic option on the Rigidbody component. In a script on the player then do:
private void OnTriggerEnter (Collider other)
{
if (other.TryGetComponent<Target>(out Target target))
{
// check if not present already to be 100% sure not to get duplicates
// even though it generally shouldn't be happening, better safe than sorry
if (!availableTargets.Contains(target))
{
availableTargets.Add(target);
}
}
}
private void OnTriggerExit (Collider other)
{
if (other.TryGetComponent<Target>(out Target target))
{
availableTargets.Remove(target);
}
}
I think you either want to
availableTargets.Clear();
first so you start with an empty list every time, as anyway you seem to only be interested in the targets overlapping in that instance.
Or you could use Link and do e.g.
using System.Linq;
...
private void TrySelectTarget(bool switchInput)
{
availableTargets = Physics.OverlapBox(transform.position, range, Quaternion.identity, targetLayer, QueryTriggerInteraction.Ignore)
.Select(col => col.GetComponent<Target>())
.Where(target => target)
.ToList();
}

How to delete all prefabs when an event happens in unity

Extremely new to unity and c# after switching across from Python. Once all the balls are scored in my game I want all the 'blockers' (prefabs that I have instantiated) to be removed from the screen and a new set to be spawned randomly on screen in random positions. The blocker prefabs spawn randomly when all balls are scored, however, the old prefabs, besides the one which deletes each time, stay on screen rather than deleting. I have tried looping through the blockers in the code below to delete and I think this is where the issue is as only one game object deletes at this stage:
if (SpawnManager.tempclonecount == 0 )
{
for (int i = 0; i < SpawnManager.blockeramounts; i++)
{
Destroy(gameObject);
}
SpawnManager.tempclonecount = 1;
}
SpawnManager is an empty object which I have used to spawn objects onto the screen, tempclonecount is a variable stating when the old game objects should be removed from the game. This part of the code works well. blockeramounts is the number of prefabs initially on screen and I hoped to loop through the number of prefabs would delete all of the prefabs. It only deletes one. How do I change this?
Here is the code for creating the blockers in spawn manager also, if helpful:
void Update()
{
int blockeramount = Random.Range(2, 7);
blockeramounts = blockeramount;
for (int i = 0; i < blockeramount; i++)
{
int blockerindex = Random.Range(0, blockerPrefabs.Length);
Instantiate(blockerPrefabs[blockerindex], new Vector3(Random.Range(-30, 30), 0, Random.Range(-30, 30)), blockerPrefabs[blockerindex].transform.rotation);
}
}
While reading your description of the problem I got a bit confused by some of the terms you used. That's all fine since you are new to Unity and still learning. What i managed to figure out is that you have a SpawnManager script attached to an empty game object which instantiates your blocker prefabs. Then in another script you are getting the SpawnManager refernce and checking if you should destroy the current ones and instantiate a new set.
First of all what i would do is, after instantiating an object, to store it in an array or a list
...
public List<GameObject> blockers = new List<GameObject>();
...
void Start()
{
...
}
void Update()
{
int blockeramount = Random.Range(2, 7);
blockeramounts = blockeramount;
for (int i = 0; i < blockeramount; i++)
{
int blockerindex = Random.Range(0, blockerPrefabs.Length);
var blocker = Instantiate(blockerPrefabs[blockerindex], new Vector3(Random.Range(-30, 30), 0, Random.Range(-30, 30)), blockerPrefabs[blockerindex].transform.rotation);
blockers.Add(blocker);
}
}
After which i would add a new method which does the check to see how many remaining blockers there are. This method should go inside SpawnManager.
public void CheckAndDeleteBlockers()
{
if (tempclonecount == 0 )
{
foreach(var blocker in blockers)
{
blockers.Remove(blocker);
Destroy(blocker);
}
}
}
And you should call it from the other script with:
...
public SpawnManager spawnManager;
...
void Start()
{
spawnManager = FindOjectOfType<SpawnManager>();
}
//for example
void Update()
{
spawnManager.CheckAndDeleteBlockers();
}
I understand the way you are trying to do this, but let's say that this isn't the correct way. I would suggest that you look up what object pooling is.
NOTE: The creator of the pooling tutorial that I mentioned above is a great source for Unity and C# beginners, so I would recommend that you watch some of his other videos. Good luck in learning Unity.
What I believe is causing your issue, is that your are only specifying one gameObject to be destroyed. You need to call Destroy() on each blocker you instantiated.
Hope my advice helps.

Unity - Instantiated objects spawn on the same position in Coroutine, while instantiate them directly do spawn correctly

What i want to achieve:
Im creating a procedural generated city. I got the roads working, together with the spawning of the houses, they spawn along the road and dont get spawned if they are colliding with either the road and/or another building. Now since i sometimes i want to load a big city, this costs a lot of computing time, so i wanted to put in a Coroutine to slowly load in all the houses. (Please tell me if there is a better way, or a "correct" way, this is the only thing that i knew that could work).
What i did / Problem:
So what i did, is all the segments i use to create a house i put in a list, and once all the houses are pregenerated (not spawned/instantiated yet) i want to use the coroutine to spawn them individually. But whenever i use the coroutine to spawn them, they spawn inside eachother.
While if i spawn them directly from their script (still using the list) they do spawn correctly, i have no clue what i am doing wrong and how to fix this.
public void Spawner(Vector3 buildingPosition, Quaternion buildingRotation, int buildingHeight, GameObject buildingParent, LayerMask m_LayerMask, BuildingGenerator buildGenerator)
{
GameObject baseBuilding = buildingBase[Random.Range(0, buildingBase.Count)];
baseBuilding.transform.position = buildingPosition;
baseBuilding.transform.rotation = buildingRotation;
Debug.Log(baseBuilding.transform.position);
Collider[] hitColliders = Physics.OverlapBox(baseBuilding.transform.position, baseBuilding.GetComponentInChildren<Renderer>().bounds.extents /2, Quaternion.identity, m_LayerMask);
if (hitColliders.Count() > 1)
{
return;
}
buildGenerator.Segments.Add(baseBuilding);
buildingPosition.y += baseBuilding.GetComponentInChildren<Renderer>().bounds.max.y - buildingPosition.y;
for (int i = 0; i <= buildingHeight; i++)
{
buildingRotation *= Quaternion.Euler(0, 0, 0);
GameObject middleBuilding = buildingMiddle[Random.Range(0, buildingMiddle.Count)];
middleBuilding.transform.position = buildingPosition;
middleBuilding.transform.rotation = buildingRotation;
//buildGenerator.Segments.Add(middleBuilding);
buildingPosition.y += middleBuilding.GetComponentInChildren<Renderer>().bounds.max.y - buildingPosition.y;
}
if (buildingRoof.Count != 0)
{
GameObject roofBuilding = buildingRoof[Random.Range(0, buildingRoof.Count)];
roofBuilding.transform.position = buildingPosition;
roofBuilding.transform.rotation = buildingRotation;
buildGenerator.Segments.Add(roofBuilding);
}
Instantiate(buildGenerator.Segments[1]); //If i use this, they spawn on the correct place.
Debug.Log(buildGenerator.Segments.Count);
}
{
StartCoroutine(LoadSegments(buildingParent));
}
public IEnumerator LoadSegments(GameObject buildingParent)
{
for (int i = 0; i < Segments.Count; i++)
{
GameObject SpawnedSegment = Instantiate(Segments[i]);
//SpawnedSegment.transform.parent = buildingParent.transform;
yield return new WaitForEndOfFrame();
}
}
Extra info:
I make use of 3 scripts to spawn the whole city, the roadGenerator, which will spawn the roads, those road points get stored in a list. After the roads are generater the roadgenerator will call the building Generator. The building generator will go through the whole list of all the roads, and create buildings aside them, the buildings are created via a 3rd script (not instantiated). The building script, this script contains the building segments, and will piece them together and also check for collisions (First code block). Once these buildings are all created and put into a list, the buildingGenerator will start the coroutine and instantiate all these buildings (thus on the wrong position)(Second code block).
Hope i provided enough information, else id be happy to provide more.
Okay so i finally got it working. The problem was not at the coroutine, but at the list. My code would constantly override my list objects. So i created a new script, with basic attributes like the object, the position and rotation. So then i add those new scripts to the list, and spawn them in the coroutine. (In my comment i said i had some other problems after i fixed this, this is because i used .bounds.max.y, while i should have used .bounds.size.y. This is thus also the fix for that).
I hope someone in the future might find this helpful!

"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 };

Unity 3D Strange Behaviour Attaching Loaded Prefab to Object

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);

Categories