saving player position while moving between scenes Unity 3D - c#

I've started learning 3D game development and now I've reached the scenes changing.
I looked over the internet and I couldn't find a solution that I can implement in my project.
At this point, I've two scenes, the street which is the main scene, and a bar scene.
I've created a sliding door that has a collider called portal that triggers the scene switch.
I've managed to swap the scene but when I'm trying to go back to my main scene I respawn and the starting point and not at the bar exit. I've tried to save the player position in a temp variable but that didn't work well. what should I do to make my player start at the store exit ?
public class PlayerMotion : MonoBehaviour
{
//...
public static Vector3 playerPosition; // for respawning use
public static bool respawnNeeded = false;
CharacterController cController;
void Update()
{
if(respawnNeeded)
{
cController.transform.position = new Vector3(StreetToBarFFPortal.tempPosition.x,
StreetToBarFFPortal.tempPosition.y,
StreetToBarFFPortal.tempPosition.z);
respawnNeeded = false;
}
//..... some movement code
playerPosition = new Vector3(cController.transform.position.x, cController.transform.position.y, cController.transform.position.z);
}
public class StreetToBarFFPortal : MonoBehaviour
{
public static Vector3 tempPosition;
private void OnTriggerEnter(Collider other)
{
if(other.CompareTag("Player"))
{
int index = SceneManager.GetActiveScene().buildIndex;
if(index==0)
{
PlayerMotion.respawnNeeded = true;
tempPosition = new Vector3(PlayerMotion.playerPosition.x,
PlayerMotion.playerPosition.y,
PlayerMotion.playerPosition.z-4);
}
index = 1 - index;// 1 transfers to 0 and 0 transfers to 1
SceneManager.LoadScene(index);
}
}
}
I also have a GlobalManeger script with a singeltone design pattern if that helps.

You have two options:
Store it in the DontDestroyOnLoad()
Store it in playerprefs before you load the other scene and load it back when the scene is loaded again. Since you're storing a Vector3 then store the x, y and z like this:
PlayerPrefs.SetFloat("X", playerPosition.x);
PlayerPrefs.SetFloat("Y", playerPosition.y);
PlayerPrefs.SetFloat("Z", playerPosition.z);
and load is like this:
float xpos = PlayerPrefs.GetFloat("X");
float ypos = PlayerPrefs.GetFloat("Y");
float zpos = PlayerPrefs.GetFloat("Z");
playerPosition = new Vector3(xpos, ypos, zpos);

You have tempPosition which as I can see used to store player position before teleportation, am I right?
So I can't see where this tempPosition variable came from. Is it static or from a singletone script? Anyway you need to have this variable through scenes. So it should be in the script that has DontDestroyOnLoad() method;

Related

Unity - Cant access reference to another script

I am trying to learn how does Unity work and I now struggle with problem that I cannot access script from another script. I was searching on Interner for couple hours, I have tried many options but nothing helped.
I have 2 scripts.
CoinSpawn.cs - attached to Player (I would change it to other object but I dont know yet to which one, because its something that runs in background so it really dont need to be on player)
CollectingCoin.cs - attached to Coin (Coin is object, that its not on game scene on the start, it spawns randomly)
CoinSpawn is script that randomly spawn Instantiate of object Coin. I want to change value of CoinSpawn.currentCoinOnScreen from CollectingCoin. I ve tried
CoinSpawn test = GameObject.Find("CoinSpawn").GetComponent<CoinSpawn>();
and it doesnt work. I also have my both scripts in the same asset folder. What am I doing wrong? Thank you
CoinSpawn.cs
public class CoinSpawn : MonoBehaviour
{
public GameObject coin;
public int maximumCoinPerScreen = 10;
public int currentCoinOnScreen = 0;
private int randomNumber;
private Vector2 spawnPosition;
private void Update()
{
randomNumber = Random.Range(1, 1000);
if(randomNumber >= 0 && randomNumber <= 1 && currentCoinOnScreen != maximumCoinPerScreen)
{
currentCoinOnScreen++;
float spawnY = Random.Range
(Camera.main.ScreenToWorldPoint(new Vector2(0, 0)).y, Camera.main.ScreenToWorldPoint(new Vector2(0, Screen.height)).y);
float spawnX = Random.Range
(Camera.main.ScreenToWorldPoint(new Vector2(0, 0)).x, Camera.main.ScreenToWorldPoint(new Vector2(Screen.width, 0)).x);
spawnPosition = new Vector2(spawnX, spawnY);
GameObject coinObject = Instantiate(coin, spawnPosition, Quaternion.identity);
}
}
}
CollectingCoin.cs
public class CollectingCoin : MonoBehaviour
{
UnityEngine.UI.Text Coins;
public static int totalCoins = 0;
private void Start()
{
Coins = GameObject.Find("Score").GetComponent<UnityEngine.UI.Text>();
}
void OnTriggerEnter2D(Collider2D c2d)
{
if (c2d.CompareTag("Player"))
{
totalCoins++;
Destroy(gameObject);
Coins.text = "COINS: " + totalCoins.ToString();
// TESTING
CoinSpawn test = GameObject.Find("CoinSpawn").GetComponent<CoinSpawn>();
CoinSpawn test2 = GetComponent<CoinSpawn>();
}
}
}
GameObject.Find("CoinSpawn").GetComponent<CoinSpawn>();
Searches for a GameObject with the name CoinSpawn. Since you told us this component is rather attached to the player object it makes sense that it isn't found.
GetComponent<CoinSpawn>();
searches for a CoinSpawn component on the very same object your CollectingCoin is attached to. From your description this clearly also isn't the case.
Since you say the CoinSpawn is attached to the player then you probably rather want to get the component from
void OnTriggerEnter2D(Collider2D c2d)
{
if (c2d.CompareTag("Player"))
{
...
// rather get the component on the player object you collided with
CoinSpawn test = c2d.GetComponent<CoinSpawn>();
}
}
Alternatively assuming there is only one single instance of CoinSpawn in your scene anyway and not necessarily on your player you could use FindObjectOfType
CoinSpawn test = FindObjectOfType<CoinSpawn>();
First of all, Do not ever use GameObject.Find(), its very expensive as it will go through all game objects in your scene to find the object. and this not a performance wise.
There are many ways to do so.
Easyest one:
Add both script to same gameobject as component.
Make a global variable CoinSpawn inside CollectingCoin script and then use [serializedFiled] tag on top of it, by this way, you can drag and drop the reference in the editor before you start play. and you can access it the way you want.
2nd way:
Is same as first one, but instead of serializedFiled, just cache it at the beginning by using GetComponent.
Just make sure you have both scripts attached to the same gameobject.
public class CollectingCoin : MonoBehaviour
{
UnityEngine.UI.Text Coins;
public static int totalCoins = 0;
CoinSpawn coinSpawn;
private void Start()
{
coinSpawn = GetComponent<CoinSpawn>();
Coins = GameObject.Find("Score").GetComponent<UnityEngine.UI.Text>();
}
void OnTriggerEnter2D(Collider2D c2d)
{
if (c2d.CompareTag("Player"))
{
totalCoins++;
Destroy(gameObject);
Coins.text = "COINS: " + totalCoins.ToString();
// DO Whaterver you want with coinSpawn here
}
}
}

Understanding Unity's GameObject.Find(), GetComponent() and objects recycling

New to unity.
So I created a simple a simple muzzle flash particle animation that is supposed to be displayed on enemies gun when the player gets close to him, simulating a shot without the actual bullet. However I get a null reference exception in this part muzzleFlash.Play(); I believe it's because I am not actually getting the muzzle flash component in the start function with the code I have, actually I know that is it after going to in to debug mode I found out. I am having a really hard time figuring out how to access that component. Below is my code and I'm also posting a picture of my hierarchy. Thanks in advance.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StaticShootingEnemy : MonoBehaviour
{
[SerializeField] private float _range = 12f;
private Transform _player;
private bool _alive;
private float _distance;
private ParticleSystem muzzleFlash;
// Use this for initialization
void Start()
{
_player = GameObject.Find("Player").transform;
_alive = true;
muzzleFlash = (ParticleSystem)this.gameObject.GetComponent("muzzleFLash");
}
// Update is called once per frame
void Update()
{
_distance = Vector3.Distance(this.transform.position, _player.transform.position);
if (_alive && _distance < _range)
AttackPlayer();
}
private void AttackPlayer()
{
//Turning enemy to look at player
transform.LookAt(_player);
Ray ray = new Ray(transform.position, transform.forward);
RaycastHit hit;
if (Physics.SphereCast(ray, 0.75f, out hit))
{
//TODO: Fix enemy shooting fast when gettting close to him.
GameObject hitObject = hit.transform.gameObject;
if (hitObject.GetComponent<PlayerController>())
{
muzzleFlash.Play();
Debug.Log("Player Hit!");
}
else
muzzleFlash.Stop();
}
}
public void SetAlive(bool alive)
{
_alive = alive;
}
}
You probably have an object "muzzleFlash" as child to object your script attached to. So, in this case you'd better have a reference to your ParticleSystem object that is called muzzleFlash.
[SerializeField] private ParticleSystem muzzleFlash; // drag and drop your ParticleSystem muzzleFlash in inspector
or at least you could find that muzzleFlash like this
GameObject muzzleFlashObj = GameObject.Find("muzzleFlash");
ParticleSystem muzzleFlash = muzzleFlashObj.GetComponent<ParticleSystem>();
In your case it's null because there is probably no component that is called MuzzleFlash on that object. The component you want to get is ParticleSystem.
What component is the staticshootingenemy script on? if it is not on the same component as the particle system then its not finding it because this.gameObject.GetComponent("muzzleFLash") does not exist on that component. You can use GameObject.Find("muzzleFLash") to search for the particle system.
So back to your comment, you could implement something like a pool for your muzzle flashes.
public class MuzzleFlashEffect : MonoBehaviour
{
[SerializeField] private ParticleSystem particleEffect;
private Queue<MuzzleFlashEffect> poolQueue;
public void SetPoolQueue(Queue<MuzzleFlashEffect> queue)
{
poolQueue = queue;
}
public void Play()
{
StartCoroutine(Playing());
}
private IEnumerator Playing()
{
particleEffect.Play();
while (particleEffect.isPlaying)
{
yield return null; // wait until particle animation is done, then recycle effect
}
particleEffect.Stop();
poolQueue.Enqueue(this); // recycle this effect
}
// you can do the same thing for Animation as well, or even write some abstract PoolableVFX class that would be usefull for Animation , ParticleSystems etc..
}
//assume you have some game controller that manage what is going on in the scene
public class GameController : MonoBehaviour
{
[SerializeField] private MuzzleFlashEffect muzzleFlashPrefab;
private Queue<MuzzleFlashEffect> poolQueue = new Queue<MuzzleFlashEffect>(10); // 10 is enough i guess and it's good to set it at instantiation to avoid memory fragmentation
private MuzzleFlashEffect GetMuzzleFlash(Vector3 pos, Quaternion rot)
{
MuzzleFlashEffect muzzleFlash;
// if we already have some effects, then play them, otherwise make a new one and recycle it then
if (poolQueue.Count > 0)
{
muzzleFlash = poolQueue.Dequeue();
}
else
{
muzzleFlash = Instantiate(muzzleFlashPrefab);
muzzleFlash.SetPoolQueue(poolQueue);
}
muzzleFlash.transform.position = pos;
muzzleFlash.transform.rotation = rot;
return muzzleFlash;
}
void Update()
{
// your fancy logic ...
GameObject mutantGunEnd = new GameObject("mutant");
//assume that here you want your muzzle flash effect, so you do:
var muzzleFlash = GetMuzzleFlash(mutantGunEnd.transform.position, mutantGunEnd.transform.rotation); // or you might want to pass mutantGunEnd.transform.forward instead, it depends...
muzzleFlash.Play();
// your fancy logic ...
}
}
So, in this case you have only as many instance of ParticleEffect as you need and saving some resources. You could also create a universal generic pool for any type of object you want to recycle. (you want to recycle instead of instantiation, cuz Instantiation is cpu expensive).
M.b this is a bit overkill here, but i just wanted to share how would i think about this here

Move spawn object to random position

I have a spawner object. Every time a gameobject is spawned, I want that object to move randomly (wandering). The problem in my script is that the gameobject movement is very random (jittery). How can I solve this?
void Start ()
{
InvokeRepeating("SpawnNPC", Spawntime, Spawntime);
}
// Update is called once per frame
void Update () {
population = GameObject.FindGameObjectsWithTag("NPCobject");
for (int i = 0; i < population.Length; i++)
{
getNewPosition();
if (population[i].transform.position != pos)
{
population[i].transform.position = Vector3.MoveTowards(population[i].transform.position, pos, .1f);
}
}
}
void getNewPosition()
{
float x = Random.Range(-22, 22);
float z= Random.Range(-22, 22);
pos = new Vector3(x, 0, z);
}
I made the New randomize vector in different method, because I plan to change it with pathfinder function and make it in different thread/task.
You are choosing a new direction every single frame. That will always be very jittery. You wan't to only change direction after, at least, a small interval. Here is a link to one way to do that from Unity's website. https://docs.unity3d.com/ScriptReference/MonoBehaviour.InvokeRepeating.html
What about using Navigation? As you said wandering, I thought it would give you a nice result and also make your code simple.
The following screenshot is a sample with Navigation. The moving game objects are also changing their direction nicely, although it cannot be seen in the sample because the game object is capsule...
Ground game object in the sample program has NavMesh. See here to build NavMesh.
Agent game object has NavMeshAgent Component. See here to set it up.
Th Behaviour class below is for Agent game object.
using UnityEngine;
using UnityEngine.AI;
public class NavAgentBehaviour : MonoBehaviour {
public Transform[] Destinations { get; set; }
// Use this for initialization
void Start ()
{
InvokeRepeating("changeDestination", 0f, 3f);
}
void changeDestination()
{
var agent = GetComponent<NavMeshAgent>();
agent.destination = Destinations[Random.Range(0, Destinations.Length)].position;
}
}
The next Behaviour class is just for spawning the Agent and setting up the destinations. On Unity, set it to whatever game object in the scene, and allocate game objects to the fields.
using UnityEngine;
public class GameBehaviour : MonoBehaviour {
public GameObject Agent;
public Transform SpawnPoint;
public Transform Destination1;
public Transform Destination2;
public Transform Destination3;
// Use this for initialization
void Start()
{
Agent.SetActive(false);
InvokeRepeating("Spawn", 0f, 2f);
}
void Spawn() {
var newAgent = Instantiate(Agent);
newAgent.GetComponent<NavAgentBehaviour>().Destinations = new[] { Destination1, Destination2, Destination3 };
newAgent.transform.position = SpawnPoint.position;
newAgent.SetActive(true);
}
}
Increase the number of destination, to make the moves look more random. By the way, the destinations do not need to be specified by game objects, which is only for making it easy to see the sample program's behaviour.
The source of the jitteriness comes from the fact that you are updating the position to move every frame so your objects never have a consistent location to move to. I would instead suggest attaching a new script to each of your objects that individually handles their movement. In that script you could do something like the following, which has a delay to keep the target position for more than 1 frame.
float delaytimer;
Vector3 pos;
void Start () {
getNewPosition(); // get initial targetpos
}
void Update () {
delaytimer += Time.deltaTime;
if (delaytimer > 1) // time to wait
{
getNewPosition(); //get new position every 1 second
delaytimer = 0f; // reset timer
}
transform.position = Vector3.MoveTowards(transform.position, pos, .1f);
}
void getNewPosition()
{
float x = Random.Range(-22, 22);
float z= Random.Range(-22, 22);
pos = new Vector3(x, 0, z);
}
You are changing the direction they are moving in every frame, thats what is causing the jitter.
You could wait a few moments before you change the direction, perhaps something like this.
// Outside update
float betweenChanges = 2;
float lastChange = 0;
// Inside update
if(Time.realtimeSinceStartup > lastChange)
{
// Change directions
// ...
lastChange = Time.realTimeSinceStart + betweenChanges;
}
You could also solve this by using InvokeRepeating or a Coroutine.
If you dont want all the NPC's to change direction at the same time and still plan on controlling every NPC from the same class like in your example, you should perhaps add a timer for each NPC instance instead and use that to decide when to change its direction.
A better idea would be to let each NPC have its own Movement-script.

Shooting laser continuously in Unity

I'm trying to create a falling word game like z-type. Once the game starts a few words are displayed on the screen. When the user types a letter and if that matches with the first letter of any of the words displayed, an activeWord tag is added to the word. I have also created a laser script that checks if the tag is active and when that happens, it shoots the laser.
What's happening right now is that the laser is shot only once i.e when the first letter matches but doesn't shoot a laser when the remaining words are typed.
This is the laser script:
using UnityEngine;
using UnityEngine.UI;
public class Laser : MonoBehaviour {
public float speed = 10.0f;
private Vector3 laserTarget;
private void Update () {
GameObject activeWord = GameObject.FindGameObjectWithTag("activeWord");
if (activeWord && activeWord.GetComponent<Text>().text.Length > 0) {
laserTarget = activeWord.transform.position; // find position of word
transform.Translate(laserTarget * speed * Time.deltaTime); // shoot the laser
}
}
}
I'm also adding the code that I use in the display/UI field.
public void RemoveLetter() {
/* remove the first letter if its correct and so
on for the remaining letters. change the color of the word to red and add
the "activeddWord" tag.*/
text.text = text.text.Remove(0, 1);
text.color = Color.red;
text.gameObject.tag = "activeWord";
}
public void RemoveWord() { // destroy the word once all the letters match
Destroy(gameObject);
}
Can someone please have a look at the code and tell me where I'm making a mistake.
I think you have to reset the position of your laser if it reaches the target:
public float speed = 10.0f;
private Vector3 laserTarget;
private Vector3 laserOrigin;
private void Start () {
// save laser's origin position
laserOrigin = transform.position;
}
private void Update () {
GameObject activeWord = GameObject.FindGameObjectWithTag("activeWord");
if (activeWord && activeWord.GetComponent<Text>().text.Length > 0)
{
laserTarget = activeWord.transform.position; // find position of word
transform.Translate(laserTarget * speed * Time.deltaTime); // shoot the laser
float distance = Vector3.Distance(laserTarget , transform.position);
if(distance < 0.05f){ // I don't know your scaling, perhaps change the limit here!
transform.position = laserOrigin;
}
}
}
Here is one way you can do this using Instantiate() and prefabs. The benefit of this method is that it is scalable. You can create multiple lasers with minimal tweaking. Please note that to use multiple lasers you will have to remove WaitForThisLaserDestroyed;.
To get this to work you will have to start by changing your laser gameObject into a prefab and adding this script onto it:
https://docs.unity3d.com/Manual/Prefabs.html
public class Laser : MonoBehaviour
{
public float speed = 10.0f;
public Vector3 laserTarget;
public float destroyLaserAfterTime = 3f;
private void Update ()
{
transform.Translate(laserTarget * speed * Time.deltaTime);
}
}
And then on some arbitrary other object. For example an empty game object in the same scene:
public class LaserInitializer : MonoBehaviour
{
public GameObject laserPrefab;
public GameObject laserOrigin;
private GameObject WaitForThisLaserDestroyed;
private void Update ()
{
GameObject activeWord = GameObject.FindGameObjectWithTag("activeWord");
if (WaitForThisLaserDestroyed == null && activeWord && activeWord.GetComponent<Text>().text.Length > 0)
{
CreateLaser(activeWord);
}
}
private void CreateLaser(GameObject activeWord)
{
GameObject activeLaser = Instantiate(laserPrefab, laserOrigin.Transform.Position, Quaternion.identity) as GameObject;
Laser laserScript = activeLaser.GetComponent<Laser>();
laserScript.laserTarget = activeWord.transform.position;
WaitForLaserDestroyed = activeLaser;
Destroy(activeLaser, destroyLaserAfterTime);
}
}
To explain the code:
The Laser prefab has its own script for moving towards the word, and as soon as it exists and has the target passed to it, it will move towards the active word.
Somewhere else in the scene you have a game object that exists to hold the second script. Lets call it the "controller game object". It checks to see if the words are "active", as per the earlier design. When a word is active this script creates exactly one laser, and tells it to target the active word.
You have another gameobject (this can be the same one as the controller game object), that marks the origin of the laser. You can do this in other ways, but I thought that using a game object to mark the start point would be an easy way for beginners.

OnGui elements interacting with other OnGui elements

Within my 2d game i wsih to have a number of OnGui elements there for the user to select, however, the cursor that im using is another ongui element(using kinect to navigate) is this possible by any chance, at the moment im using planes but i will be zooming in and out of the camera so ill essentially need them attatched to the screen. Any ideas, suggestions or workarounds.
THis is currently my cursor.
using UnityEngine;
using System;
using System.Collections;
public class PillarAgent : MonoBehaviour {
public SkeletonWrapper sw;
public Vector3 distance;
public float progress =0f;
public Texture2D cursor;
public Texture2D load;
public Camera mainCam;
public float startTime;
private int roundedRestSecounds;
// Use this for initialization
float differencex = 0;
float differencey = 0;
void Start () {
distance =new Vector3(0f,0f,0f);
}
float translate(float value, float leftMin, float leftMax,
float rightMin,float rightMax)
{
float leftSpan = leftMax - leftMin;
float rightSpan= rightMax - rightMin;
float valueScaled = (value-leftMin)/(leftSpan);
return rightMin+(valueScaled * rightSpan);
}
// Update is called once per frame
void Update () {
if (sw.pollSkeleton())
{
distance.x=sw.bonePos[0,0].x - sw.bonePos[0,7].x;//5 is left shoulder
distance.y=sw.bonePos[0,0].y -sw.bonePos[0,7].y;
differencex=translate(distance.x,.6f,0,0,Screen.width);
differencey=translate(distance.y,-.5f,0,0,Screen.height);
//Debug.Log();
float width = sw.bonePos[0,5].x+ sw.bonePos[0,9].x;
float height =sw.bonePos[0,4].y- sw.bonePos[0,0].y;
float heightdiv= (height/2)+sw.bonePos[0,0].y;
}
}
void OnGUI() {
//left top width height
Rect r = new Rect(differencex,differencey,80,50);
GUI.Label(r,cursor);
GUI.BeginGroup(new Rect(differencex,differencey+50,50*Mathf.Clamp01(progress),15));
//Debug.Log(progress);
GUI.DrawTexture(new Rect(0,0,50,50),load);
GUI.EndGroup();
transform.position =mainCam.ScreenToWorldPoint(new Vector3(differencex,Screen.height-differencey,50));
//mainCam.fieldOfView()
}
void OnCollisionStay(Collision Other)
{
startTime+=Time.deltaTime;
if(Other.gameObject.GetComponent(typeof(TextControl)))
{
roundedRestSecounds=Mathf.CeilToInt(Time.time);
progress = Time.time *0.2f;
CurrentState=true;
}
else if(Other.gameObject.tag==("Scalpal")){
progress = startTime *0.5f;
//scallpall activated
//
}
}
void OnCollisionExit(Collision Other){
startTime =0f;
progress =0f;
}
public Boolean CurrentState{get;set;}
}
The next class is essentially the class in which i pick up my tools, currently this code doesnt work(not sure why), but what i wish to do is select some tools which show up on the screen so that i can use them,for e.g pick up paint brush start painting bricks or what not. at the moment i have my tools on a plane, i wish to always have them on the screen at all times when the camera moves.
using UnityEngine;
using System.Collections;
public class SelectTool : MonoBehaviour {
public Tools tools;
public float startTime;
public bool ScalpalSelected;
public GameObject selectedTool;
void Start()
{
tools = this.GetComponent<Tools>(); //in order to use this tools muyst be attached to the game object
//this is essentially saying with regards to this game object get the component named tools
}
void update()
{
}
void OnCollisionStay(Collision Other)
{
startTime +=Time.deltaTime;
if(startTime >5f){
if(Other.collider.tag==("Scalpal"))
{
selectedTool = Other.collider.gameObject;
Debug.Log(selectedTool+" What in gods good name is:" +tools.utilities[0]);
}
else {
selectedTool=null;
}
if(selectedTool){
for(int i=0;i<tools.utilities.Length;i++)
{
}
}
ScalpalSelected=true;
renderer.material.color = Color.yellow;
}
}
void OncollisionStay(Collision other){
startTime = 0f;
}
}
From the comment section to the question I will assume that you want to know how to do this:
"... you want your plane objects to move together with the camera ..." - Steven Mills
"thank you #StevenMills Do you have any example of how this can be done?" j bel
While the answer provided in the comments is to just manually add the planes as children of the camera (a very straightforward, manual approach), I will give another way to do this through scripting (in light of maybe this will help someone else out disregarding the likeliness of someone using this solution).
The idea of this is to create a script (thus following being attached to the MainCamera) that will search through all of the GameObject in the Object Hierarchy with the method GameObject.FindGameObjectsWithTag. Once we have all our GameObject with the associated Tag, we can then loop through the array and parent the attached script's GameObject to each.
public class ParentGameObjects : MonoBehaviour {
//The tag to search for on all game objects in the hierarchy
public String objectTag;
//The game objects that we will parent the main camera to
private GameObject[] children;
//It's not necessary to store references to the children but if you want to modify them at some point you will be able to
void Start() {
//Find all game objects with the tag we want
children = GameObject.FindGameObjectsWithTag(objectTag);
//Loop through all of the game objects found and parent this object's transform
for(int i = 0; i < children.Length; i++) {
children[i].transform.parent = transform;
}
}
}
Now, there are a few things you have to do for this script to work:
Attach this script to any GameObject that you want to parent other objects to.
In the Inspector of the GameObject that the script is attached to, enter in the name of the Tag you want to use.
For all GameObject(s) in the Hierarchy that should be added as children, assign the same Tag.
Of course there are other things you can do like, for example, instead of only searching for one Tag be able to search for multiple but that requires a bit (not exactly much) more work. Nonetheless, I hope this will at least be useful information to someone on how parenting works via scripting.

Categories