I have a game object that has a lot of children (spheres)
At some stage of the game I'd like to move every child down smoothly so it would look like it's falling apart. What I've tried is using foreach loop to move every element with Vector3.Lerp. Sadly everything starts lagging very badly.
Would be great to have them fall down smoothly like this
foreach (Transform child in GameObject.Find("Carbon").transform)
{
child.transform.position = Vector3.Lerp(child.transform.position,
new Vector3(child.transform.position.x,
child.transform.position.y - 200,
child.transform.position.z), 0.2f);
}
I think the main problem comes from GameObject.Find(). From the look of your code, it finds carbon transform in every foreach. If you have 100 objects it will use "GameObject.Find" 100 times in one frame.
This can be fixed by caching transform:
List<Transform> carbonList = new List<Transform>();
void Start () {
//cache carbon tranform in Start() or OnEnable()
carbonList.AddRange( transform.GetComponentsInChildren<Transform>());//use this if you attach your code in object parent
//loop transform form caching list so it will not keep finding transform and cause lagging
foreach (Transform child in carbonList)
{
var childPos = child.position;
child.position = Vector3.Lerp(childPos, new Vector3(childPos.x, childPos.y - 200, childPos.z), 0.2f);
}
}
if your script is not in parent object you can use find tag instead.
http://answers.unity3d.com/questions/24257/how-do-i-find-all-game-objects-with-the-same-name.html
Your animation graphic seems to show that all of your spheres maintain the same relative position to each other. Rather than "falling apart", they seem to be falling together as a body. If that's want you want, rather than animate each individual sphere, put them all under a single parent object and animate that parent.
Related
I am programming an endless runner that manipulates an airplane with the flexion of the wrist. I have a set of prefabs that move in front of the player. I have a simple prefab, it's a platform and an object called "Bone" as a child. his "Bone" each time the player touches it gives him 1 pt. However, I want to know (if possible) how to change the position of this child object via script. I have written a rough code of the main idea:
//This is my main prefab
var assetPath = "Assets/BonusH.prefab";
//Find the child "Bone"
var bone = GameObject.Find("BonusH/Bone");
//Edit mode
using (var editingScope = new
PrefabUtility.EditPrefabContentsScope(assetPath))
{
//Set the new location inside the prefab
bone.transform.Translate(1,1,1);
}
I am doing a project to create a plant through different parts selection randomly.
The project randomly selects a trunk, and each trunk has a specific place for fruit.
What are your thoughts on setting a point on each trunk, so the fruit (another 3D object) can be snapped?
For example, if the first trunk is selected = 1 position.
If second one = the point changes.
The hard part is that this point shall change if the trunk scale or rotation changes.
Any thoughts would be appreciated.
It depends, but I can give a suggestion:
Fixed Grid
Basically rather than have an object be positioned respective to another's,
we have all objects in the world be snapped to a Global Grid.
Logic wise, it would have every GameObject snapped to say, every 1f of x/y axis.
Kind of like mine-craft, where every block is snapped to the World-Grid
Problem is, it really depends on your model/art and how freely you want a player to be able to snap stuff to anywhere.With the solution to the latter having the Grid be more precise.
The project randomly selects a trunk, and each trunk has a specific place for fruit. What are your thoughts on setting a point on each trunk, so the fruit (another 3D object) can be snapped?
This can work, and some Colony building games use it too to prevent the player from putting an extension/add-on for a building anywhere.
The hard part is that this point shall change if the trunk scale or rotation changes.
When you scale/rotate a parent, the child object's position/rotation/scale will change respectively, so you do not have to worry about that.
But if that is not the intended effect, you can detach all child-objects first, then re-attach it later like so:
public void DoScalingRotation() {
List<Transform> children = new List<Transform>();
foreach (Transform child in gameObject.transform) {
// Add to a list to re-attach the child-object later
children.Add(child);
// Detach the child object
child.parent = null;
}
// DO YOUR SCALING/ROTATION HERE
// Re-attach child-object
foreach (Transform child in gameObject.transform) {
child.parent = gameObject.transform;
}
}
Which also raises the question, do you need to attach the object as a child?At most it's for cleanliness-sake during debugging.
I have a player that can move around a randomly generated map of rooms. I've been trying to figure out a way to keep all the rooms fully transparent/invisible, and then as soon as the player enters the room, that room becomes opaque.
I already have a way to detect the room the player is in, the main issue I'm having is changing the transparency of the room. Each room is made up of 30 or so "wall" tiles, which are just default square 2D sprites for now. These are the only thing I need to change the visibility of for now.
I saw that I can change the "material render value" or something similar to that per sprite, but I'm not sure of an easy way to do that for a whole lot of sprites at once that only have the default sprite renderer component and not a custom material.
Do I need to completely overhaul my rooms' walls to be one prefab for the whole wall? Or is there some way I could easily loop through each wall in a targeted room and change the transparency/visibility?
You could use the GameObject.SetActive(bool) it has the utility to disable and enable the visibility of the GameObject.
(I believe you have already done this, but if not) In your hierarchy you would first create an Empty and add your room to it, rename it with the name of your room and you could use the following script when detecting the room the player is in.
public GameObject room; //link your room here
void Start()
{
room.SetActive(false);
}
void Update()
{
//When detecting the room
room.SetActive(true);
}
The bool parameter of the SetActive() void sets whether the object is visible or not, when false it is invisible, and when true it is visible. I don't have your script as a reference, so I used an example
You should use GetChild() and for {}. Get child gets the child of the index you chose, and accesses it. Here is the script:
void Update()
{
for (int i = 0, i <= transform.childCount, i += 1)
{
GameObject child = GetChild(i);
// change the values here using child.GetComponent<>()
}
}
This is what the computer does: sets int i. Does this object have less children than the variable, i? Yes, run the code underneath. set child to the child of index i. i increases by 1. Repeat until i is greater than the child count.
Links:
https://docs.unity3d.com/ScriptReference/Transform.GetChild.html
https://www.w3schools.com/cs/cs_for_loop.asp
I have a UI in Unity where I constantly move items from one place to another.
Some of said items are containers, and I decided to give each containers their own child gameObject representing and containing the actual container's content once opened (aka activated the gameObject). I chose to do it this way to be easier to track each container's actual containers.
What I want: When changing the item's parent (aka moving it around since all my parents have layout groups), the child gameObject should remain at the same world position
What I have: When changing the item's parent, the child gameObject changes world position
What I can do: I could reset the position of the child gameObject each time, as I do the first time the gameObject is instantiated using this code (the relative gameObject is stretched)
Vector3[] corners = new Vector3[4];
transform.parent.parent.GetComponent<RectTransform>().GetWorldCorners(corners);
float width = corners[2].x - corners[0].x;
float height = corners[1].y - corners[0].y;
RectTransform itemTransform = item.transform.GetChild(1).GetComponent<RectTransform>();
itemTransform.position = new Vector3(width / 2, height / 2);
What I want to know: Is there something built in Monobehavior or Unity that prevents a child gameObject from moving
Here are images step by step of one of the cases it would apply:
Here I have a bag item called "sac a dos" in my room
Now I have opened The bag, which shows up in the middle of the screen
I now have created a chest item called "Coffre" at the same place, causing the bag to be put as child of the chest's container child gameObject
Here I opened the chest, positioned at the right place, and showing the bag that was put as child of the chest's container child gameObject
Here I opened the bag in the chest, causing the bag's child gameObject to be misplaced since the bag's position changed
I think You should separate items data from their display.
When You have a common display of your elements write a Show method, where you could pass showed item data.
Example:
public class ItemDisplay : MonoBehaviour
{
public void Show(ItemData itemData)
{
this.gameObject.SetActive(true);
foreach (var property in itemData.properties)
{
DisplayProperty(property);
}
}
private void DisplayProperty(){...}
}
Ran into a U.I problem that I am struggling to solve (UI is always an issue for me). Gist of the project is that I have an array of spawn points (Positions[i]), an array composed of enemy prefabs(Enemies[j]) and just a UI panel prefab with a text component (EnemyHUD). When I pass string names through a certain function, as long as there is a prefab with the same name and position available to it, it will load in the enemy. Now, for every enemy prefab loaded in, I would like a "EnemyHUD" prefab to instantiate at at the enemy positions and with text displaying the name of the enemy.
GameObject HUD = Instantiate(EnemyHUB, Positions[i].position, Positions[i].rotation);
This line spawns the EnemyHUD prefab at the right location but its Instantiates them outside of the canvas so they show up as red x's. So I added this:
HUD.transform.SetParent(GameObject.Find("Canvas").transform, false);
This fixes the issue of Instantiating outside of the canvas but also (irritatingly)resets the Instantiate position and embarrassingly, I'm not sure how to set it back while making sure it stays a child of the Canvas. Have not event touched the name change part yet.
I have been working on this this since last night and while I got a lot of different results experimenting, none have been the one that I want. Very much still a novice, so I am sure it is looking me in the face so please help me find it.
First of all I would strongly recommend to avoid Find whenever possible! Rather already reference the according Canvas and store its reference. Especially the i lets assume you are doing this in a loop so using Find repeatedly is extremely inefficient!
// Drag you Canvas object into this field via the Inspector in Unity
[SerializeField] private Canvas _parentCanvas;
private void Awake()
{
// use this only as fallback an only ONCE
if(!_parentCanvas) _parentCanvas = GameObject.Find("Canvas");
if(!_parentCanvas) Debug.LogError("Could not get Canvas!", this);
}
Well and then the second parameter of Transform.SetParent is called
worldPositionStaysIf true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before.
And
The default value of worldPositionStays argument is true.
Simply either leave it out like
HUD.transform.SetParent(_parentCanvas);
// this equals
//HUD.transform.SetParent(_parentCanvas, true);
or you could even do it in one single go already in Object.Instantiate which takes an additional parameter
parent Parent that will be assigned to the new object.
// This directly instantiates the new hud as child of _parentCanvas
// while still taking the position and rotation in absolute worldspace
GameObject HUD = Instantiate(EnemyHUB, Positions[i].position, Positions[i].rotation, _parentCanvas);