How to assign TextAssets to a scene gameobject via editor script? - unity3d

I have a scene with levels. This scene has 1.000 levels. Its level has a TextAsset. I would like to run an EditorScript and assign those TextAssets via script and not by Dragging and dropping 1.000 TextAssets to the scene. Then save the scene automatically.
Scene mainScene = SceneManager.GetSceneByName("Main");
GameObject[] gameObjects = mainScene.GetRootGameObjects();
GameManager gameManager = gameObjects[0].GetComponent<GameManager>();
List<LanguageCategory> languageCategories = new List<LanguageCategory>();
LanguageCategoryPackInfo l1 = new LanguageCategoryPackInfo();
l1.displayName = "t1";
LanguageCategoryPackInfo l2 = new LanguageCategoryPackInfo();
l2.displayName = "t2";
languageCategories.Add(l1);
languageCategories.Add(l2);
gameManager.LanguageCategoryPackInfos = languageCategories;
//do the same assignment again, but with no luck
mainScene.GetRootGameObjects()[0].GetComponent<GameManager>().LanguageCategoryPackInfos = languageCategories;
EditorSceneManager.SaveScene(mainScene);
After running the above code the scene doesn't seems to be changed.

The problem with directly making some changes in the component doesn't mark it as dirty and thus changes are not saved! (see also explenation in SerializedObject and solution below.)
You can do this manually using EditorUtility.SetDirty. As noted there you should always combine it with first also calling Undo.RecordObject.
If you don't care about the Undo/Redo functionality you can also simply use EditorSceneManager.MarkSceneDirty
Scene mainScene = SceneManager.GetSceneByName("Main");
GameObject[] gameObjects = mainScene.GetRootGameObjects();
GameManager gameManager = gameObjects[0].GetComponent<GameManager>();
List<LanguageCategory> languageCategories = new List<LanguageCategory>();
LanguageCategoryPackInfo l1 = new LanguageCategoryPackInfo();
l1.displayName = "t1";
LanguageCategoryPackInfo l2 = new LanguageCategoryPackInfo();
l2.displayName = "t2";
languageCategories.Add(l1);
languageCategories.Add(l2);
// record an undo point
Undo.RecordObject(gameManager, "languages added");
gameManager.LanguageCategoryPackInfos = languageCategories;
// mark the object dirty so it will be saved
EditorUtility.SetDirty(gameManager);
// or simply directly
EditorSceneManager.MarkSceneDirty();
EditorSceneManager.SaveScene(mainScene);
As alternative though it is more complex it is always recommended to instead go through the SerializedObject and SerializedPropertys which handles all the marking dirty and undo/redo functionalities for you automatically.
Scene mainScene = SceneManager.GetSceneByName("Main");
GameObject[] gameObjects = mainScene.GetRootGameObjects();
GameManager gameManager = gameObjects[0].GetComponent<GameManager>();
SerializedObject serializedObject = new SerializedObject(gameManager);
SerializedProperty languageCategories = serializedObject.FindProperty("LanguageCategoryPackInfos");
// load the current real values into the serializedObject
serializedObject.Update();
languageCategories.arraySize = 2;
SerializedProperty l1 = languageCategories.GetArrayElementAtIndex(0);
l1.FindPropertyRelative("displayName").stringValue = "t1";
SerializedProperty l2 = languageCategories.GetArrayElementAtIndex(1);
l2.FindPropertyRelative("displayName").stringValue= "t2";
// writes the changes bakc to the actual properties and marks them as dirty etc
serializedObject.ApplyModifiedProperties();
EditorSceneManager.SaveScene(mainScene);
Note: Typed on smartphone so no warrenty, but I hope the idea gets clear

Related

Saving Prefav Data In Unity between scene transition

I am making a game where the data to be displayed in the Main Menu is fetched from the API. I store the data in a class and loop through the data to map the data in the Menu.
foreach(var dt in mainMenuDto.data)
{
GameObject _catogeryPrefabTemp = Instantiate(catogeryPrefab);
Vector3 originalSize = _catogeryPrefabTemp.transform.localPosition;
_tabGroup.objectsToSwap.Add(_catogeryPrefabTemp);
_catogeryPrefabTemp.transform.SetParent(catogeryParent.transform);
_catogeryPrefabTemp.transform.localPosition = originalSize;
_catogeryPrefabTemp.transform.localScale = new Vector3(1, 1, 1);
foreach (var item in dt.games)
{
Texture2D image = await (GetRemoteTexture(item.thumbnail));
tex.Add(image);
await _catogeryPrefabTemp.GetComponent<CatogeryInitialise>().InstantiateGame(gameButtonPrefab, item.slug,image);
}
}
Now , this works fine but when I change the scene and come back to The main Menu, the whole mapping process takes place again. How do I change the main menu to be saved even during scene changes.
Use DontDestroyOnLoad
This method will tell unity to keep your menu between scenes. You will be able to see it under separate DontDestroyOnLoad label in your scene hierarchy.

Is there a way to specify where a game object spawns in the hierarchy in unity?

I have set up a random spawner that creates new game objects but they are being created outside of my canvas and therefore can't be seen when play the game. Is there any way to fix this? The objects 'neg thoughts' are UI Buttons and are being created outside of the canvas even though I need them to appear on screen so they can be used.
I did see a similar question but the suggestions didn't work for my problem.
this is very frustrating for me and any help would be awesome!
You can simply pass the parent into Instantiate
parent Parent that will be assigned to the new object
var newObj = Instantiate(prefab, parentTransform);
or with additional transforms
var newObj = Instantiate (prefab, position, rotation, parentTransform);
Or as others already said you can still do it afterwards at any time either by simply assigning a new transform.parent
newObj.transform.parent = parentTransform;
or using transform.SetParent
newObj.transform.SetParent(parentTransform, worldPositionStays);
The advantage of the later is that you have an optional parameter worldPositionStays
worldPositionStays If 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.
assigning a new
transform.parent = parentTransform;`
will always act the same way as
transform.SetParent(parentTransform);
since the default value for worldPositionStays (so if you don't explicitly pass it) is true.
So for the specific case of UI in a Canvas you could use
public TheSpawnComponent : MonoBehaviour
{
// Via the Inspector drag&drop any object here that is inside the canvas
// or the canvas itself
[SerializeField] private GameObject parentInCanvas;
[SerializeField] private Button buttonPrefab;
public void DoInstantiate()
{
var newButton = Instantiate (buttonPrefab, parentInCanvas);
//Todo Position and callback
}
}
Or if the spawner script is attached to an object inside the canvas anyway you could also spawn as child of this one directly using
var newButton = Instantiate(buttonPrefab, transform);
You can specify the transforms parent after you have created the game object.
spawnedObject.transform.parent = canvas.transform;
You have to use below code
public GameObject Prefab; // Object to Create
public Transform ParentOfObj;// Must Be inside the canvas or canvas it self
void Start()
{
GameObject g = Instantiate(Prefab) as GameObject;
g.transform.SetParent(ParentOfObj);
g.transform.localScale = Vector3.one;
}

Unable to get new unity particle system code working post update

I used to have the following particle system that worked.
// In the inspector I drag in the leaf particle system.
public ParticleSystem LeafStormParticleSystem;
private IEnumerator activate(float ActivateFor) {
//Change number of particles to 150
LeafStormParticleSystem.maxParticles = 150;
// LeafStormParticleSystem.
var newEmission = LeafStormParticleSystem.emission;
var rate = newEmission.rate;
rate.constantMin = 20;
rate.constantMax = 21;
newEmission.rate = rate;
Now as you can probably tell, this simple increases the number of particles. Now, this used to work and probably doesn't because of the new particle system I keep reading about.
However on this new particle system I have a question and issues getting it to work.
Issue
Correct me if i'm wrong but the particle system is defined as follows
void Start()
{
ParticleSystem ps = GetComponent<ParticleSystem>();
var main = ps.main;
main.startDelay = 5.0f;
main.startLifetime = 2.0f;
}
Now if I have 3 particle systems, how do I specify which one i'm referring to? Since I cant define it as public and drag the particle system to it in the editor anymore?
Issue B
Now i tried following what unity said in their forums and did the following
ParticleSystem ps = GetComponent<ParticleSystem>();
var main = ps.main;
main.maxParticles = 150;
// LeafStormParticleSystem.maxParticles = 150;
do not create your own module instances, get them from a ParticleSystem instance UnityEngine.ParticleSystem+MainModule.set_maxParticle
Any help with Issues A and B will be appreciated.
Thank you
Issue A
Only one ParticleSystem component can be attached to any one GameObject at a time. Sub-ParticleSystems must therefore be attached to separate GameObjects too (generally children of the GameObject holding the first ParticleSystem), which can be dropped directly onto public fields.
public ParticleSystem LeafStormParticleSystem;
void Start ()
{
if (LeafStormParticleSystem != null)
{
var main = LeafStormParticleSystem.main;
main.maxParticles = 150;
}
}
Issue B
Your code looks fine, however a critical part of the error message was missing; NullReferenceException which is telling you that a reference in your code is equal to NULL. In your case, that reference would be the ps variable used to store the ParticleSystem reference, which is either a consequence of not attaching this script to the GameObject holding your ParticleSystem or simply that you have no ParticleSystem attached at all. In either case, make sure both script and ParticleSystem are attached to the same GameObject and check your references like so;
void Start ()
{
ParticleSystem ps = GetComponent<ParticleSystem>();
if (ps != null)
{
var main = ps.main;
main.maxParticles = 150;
}
}

How to add gameobjects via script in the hierarchy in edit mode?

I would like to create a menu item that can build a game object with children and components.
I can't use prefabs for the whole objects because I need that the children are separate prefabs.
You can use something like this:
[MenuItem("YOUR_MENU_NAME/YOUR_SUBMENU_NAME/YOUR_METHOD_NAME %&n")]
static void CreateAPrefab()
{
//Parent
GameObject prefab = (GameObject)PrefabUtility.InstantiatePrefab(AssetDatabase.LoadAssetAtPath<Object>("Assets/Prefabs/TestPrefab.prefab"));
prefab.name = "TestPrefab";
if(Selection.activeTransform != null)
{
prefab.transform.SetParent(Selection.activeTransform, false);
}
prefab.transform.localPosition = Vector3.zero;
prefab.transform.localEulerAngles = Vector3.zero;
prefab.transform.localScale = Vector3.one;
//Child 1
GameObject prefabChild1 = (GameObject)PrefabUtility.InstantiatePrefab(AssetDatabase.LoadAssetAtPath<Object>("Assets/Prefabs/TestPrefabChild1.prefab"));
prefabChild1.name = "TestPrefabChild";
prefabChild1.transform.SetParent(prefab.transform, false);
prefabChild1.transform.localPosition = Vector3.zero;
prefabChild1.transform.localEulerAngles = Vector3.zero;
prefabChild1.transform.localScale = Vector3.one;
//Child2...
//...
Selection.activeGameObject = prefab;
}
Don't forget to include using UnityEditor; and to either place the script in an Editor/ folder or use #if UNITY_EDITOR / #endif around the parts using the editor methods. Also I added a shortcut to the MenuItem using %&n (Ctrl + Alt + N).
If you need to change the prefabs that compose the instantiated object, you can try to retrieve them all in your project (maybe be and heavy operation depending on your project) as they did here and display a kind of checklist where you select the prefabs needed as children inside an EditorWindow.

Unity: GameObject always at center regardless of position changes

I am working on a 2D game and have created a game object using C# script as below. I also set my camera to orthogonal and have adjusted my sprite based on the width of the screen. Regardless of the position I set, the object is always at the center of the screen. How can I solve this?
using UnityEngine;
using System.Collections;
public class TestingPositions : MonoBehaviour {
GameObject hero;
Sprite heroSprite;
Vector3 heroPosition;
// Use this for initialization
void Start () {
hero = new GameObject ();
Instantiate (hero, heroPosition, Quaternion.identity);
Camera camera = GameObject.FindGameObjectWithTag ("MainCamera").GetComponent<Camera> ();
heroPosition = camera.ScreenToWorldPoint(new Vector3(Screen.width/4, Screen.height/4, camera.nearClipPlane));
heroSprite = Resources.Load <Sprite> ("Sprites/heroImage");
SpriteRenderer renderer = hero.AddComponent<SpriteRenderer>(); renderer.sprite = heroSprite;
}
}
when you use Instantiate you have to use it on
an existing model.
Instantiate means "duplicate this model" or "copy this model", or "make a new one, using this model as an example".
What you are doing, is creating a brand new empty "hero" game object - and then "instantiating" it. That is meaningless and does nothing.
What you must do whenever you want to use "Instantiate" is this:
public GameObject modelPerson;
Note that the name must be "modelSomething".
first put that in your code. LOOK at the Inspector. MAKE your actual model hero (or whatever it is)
Sit it somewhere off camera where it is not seen.
Now, drag that thing to the "modelPerson" slot in the Inspector.
If you are not familiar with the basics of using Inspector-dragging in Unity, review basic Unity tutorials https://unity3d.com/learn/tutorials/topics/scripting
Next in your code, perhaps in Start, try this
GameObject newHero = Instantiate( modelPerson );
newHero.transform.position = .. whatever you want
newHero.transform.rotation = .. whatever you want
newHero.name = "Dynamically created";
newHero.transform.parent = .. whatever you want
once you understand these basics, there is very much more to learn about Instantiate. You can ask that in separate questions. Good luck.
Your need to save the reference to your gameObject that is created with Instantiate, because Instantiate makes a copy not modifies the original.
To modify a gameobjects position after instantiation, you need to use gameobject.transform.position = newPosition; To modify it before instantiation, you would need to do the "heroPosition" line before using heroPosition in Instantiate.
So like this:
using UnityEngine;
using System.Collections;
public class TestingPositions : MonoBehaviour
{
GameObject hero;
SpriteRenderer heroSprite;
// Use this for initialization
void Start()
{
Camera camera = GameObject.FindGameObjectWithTag("MainCamera").GetComponent<Camera>();
//Save the reference to the instantiated object into a variable
//Since you are creating an object from scratch, you don't even need Instantiate, which means copy - not create.
hero = new GameObject();
//Set its position
hero.transform.position = camera.ScreenToWorldPoint(new Vector3(Screen.width / 4, Screen.height / 4, camera.nearClipPlane));
//Set its rotation
hero.transform.rotation = Quaternion.identity;
//Add sprite renderer, save the reference
heroSprite = hero.AddComponent<SpriteRenderer>();
//Assign the sprite
heroSprite.sprite = Resources.Load<Sprite>("Sprites/heroImage");
}
}