Unity fill array Gameobject from another script - c#

I have been follow video from youtube for creating custom tabs. But in condition Iam using dynamic panel, and then in script has to fill panel to array Gameobject objectsToSwap. My Question how I fill that array from another script ?
enter image description here

Let's:
-call the script with the Array = A
-call the script with the GameObjects = B
You need to get a reference to the script A on the script B.
Then you could do something like this:
public class A : MonoBehaviour
{
private GameObject[] _array;
public void AssignObjects(GameObject[] objects)
{
_array = objects;
// if you're doing this on the editor then you also want to set A as dirty to make sure the changes are saved
// also need to include #if UNITY_EDITOR so this doesn't get compiled on builds
// this can't be compiled because the UnityEditor namespace does not exist on builds
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(this);
#endif
}
}
public class B : MonoBehaviour
{
[SerializeField] private GameObject[] objects;
[SerializeField] private A otherScript;
[ContextMenu("Assign Objects")] // in case you want to do this using the context menu (click the 3 dots on the inspector of this script)
private void AssignObjects()
{
AssignObjects(otherScript);
}
public void AssignObjects(A script)
{
script.AssignObjects(objects);
}
}
Another option if you have access to all those fields in the editor, just right click on the origin array => Copy then right click on the desired array => Paste

Related

C#/Unity - Unable to save scriptable object to disk

Here is the code of my scriptable object:
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace ModelManager
{
[CreateAssetMenu(fileName = "New Texture Set", menuName = "Model Manager/New Texture Set")]
public class TextureSetSO : ScriptableObject
{
[SerializeField]
public RaceModelSO race;
public List<Textures> textures;
private RaceModelSO _race;
private void OnEnable()
{
_race = race;
}
private void OnValidate()
{
if (race != _race)
{
_race = race;
textures.Clear();
if (race)
{
if (race.GetRaceBodySlots().Length != 0)
{
for (int i = 0; i < race.GetRaceBodySlots().Length; i++)
{
Textures textures = new Textures();
textures.raceSlot = race.GetRaceBodySlots()[i];
this.textures.Add(textures);
}
}
}
}
EditorUtility.SetDirty(this);
AssetDatabase.SaveAssets();
}
private void OnDestroy()
{
AssetDatabase.SaveAssets();
}
private void OnDisable()
{
AssetDatabase.SaveAssets();
}
}
[Serializable]
public class Textures
{
public RaceSlotSO raceSlot;
public Texture normalMap;
public Texture albedoMap;
public Texture metalicMap;
public Texture ambientOcullsionMap;
public Texture emissionMap;
}
}
Whenever I use the editor to set anything inside the List<Textures> textures and then if I close Unity everything is getting lost.
Why is that? How can I keep the changes when close and open back Unity?
Any way to store that info to the disk ?
Problem
The reason why this happens is that the
private RaceModelSO _race;
is not serialized => Not saved.
Thus, everytime you reopen the project _race = null and therefore the check
if (race != _race)
is true and you erase all textures.
Solution
If you need the other field _race to be serialized as well you will need to have [SerializeField] (btw it makes no sense on a public field)
[SerializeField] private RaceModelSO _race;
if you want to serialize it but not exposed in the Inspector you can use
[HideInInspector] private RaceModelSO _race;
you don't need [SerializeField] then, as it is already included in the [HideInInspector].
Note
You should not call AssetDatabase.SaveAssets() here at all. You do this by pressing CTRL + S.
Makes little sense to do this in OnDestroy or OnDisabled at all for a ScriptableObject and is also extremely expensive doing it for every OnValidate call!
You need to call EditorUtility.SetDirty(scriptableObject). Although you seem to call it inside OnValidate, OnValidate isn't called when modifying variables from code. It is called only when modifying the variable via Inspector or undoing/redoing a prior change.
An alternative to EditorUtility.SetDirty is to call Undo.RecordObject(scriptableObject) prior to modifying the ScriptableObject; this method also allows undoing/redoing the change.
P.S. You don't need to call EditorUtility.SetDirty or AssetDatabase.SaveAssets inside OnValidate. You also don't need [SerializeField] attribute on a public field.

Check and create lost GameObjects from code (not during gameplay)

Ok. When I open my project if someone GameObject (or all of them) is not found then it should be created automatically.
For example, I have SceneBuilder as GameObject and it contains all necessary scripts: ObjectsGenerator.cs, PlayerData.cs etc. And if somehow SceneBuilder disappears it must be recover from code. How can I do that?
p.s.: I found only two ways:
creating it from menu [MenuItem("MyTools/CreateGameObjects")], but it not obviously for support.
creating it through [CustomEditor(typeof(SomeOjectScript))] , but it must be already exists in scene (I guess a situation where the scene is completely empty)
p.s.: sorry if the question has already been and I have not found it
updated.
thanks to all. solved something like that
[InitializeOnLoad]
public class Checker : MonoBehaviour
{
static Checker()
{
CheckAndCreateObj();
EditorApplication.hierarchyChanged += hierarchyChanged;
}
private static void hierarchyChanged()
{
CheckAndCreateObj();
}
private static void CheckAndCreateObj()
{
string objName = "OBJECTNAME";
GameObject go = GameObject.Find(objName);
if (go == null)
{
Instantiate(new GameObject(objName));
}
}
}
https://docs.unity3d.com/Manual/RunningEditorCodeOnLaunch.html
You can check it dynamicly in one of your other scripts. You can call it from OnEnable or Start functions. Your MonoBehaviour must contain tag [ExecuteInEditMode] It could be something like this:
[ExecuteInEditMode]
public class YourMonoBehaviour: MonoBehaviour
{
void OnEnable()
{
var myObject = GameObject.Find("GAMEOBJECT_NAME");
if (myObject == null)
{
//Create new GameObject here and add your Component
}
}
}
If you need save some asset links or data in your gameobject with serialization, you can create prefab, cache it and create your object from prefab:
MyObjectType prefabTemplate;
void OnEnable()
{
if (GameObject.Find("GAMEOBJECT_NAME") == null)
{
GameObject.Instantiate(prefabTemplate);
}
}

Multiple characters in videogame using Unity

I am developing a videogame using Unity and I want the user to be able to choose from a group of characters who have different attributes and weapons.
Do I have to create a scene for every character for each level or there is a dynamic way of doing it?
Your scripts :
GameManager :
public class GameManager : MonoBehavior
{
public static GameObject character; // this field is to remember the selection. Since it is 'static', it will keep value in between scenes.
}
LevelManager :
public class LevelManager : MonoBehavior
{
public void Start()
{
// read info stored in GameManager
GameManager.character.transform.position = /* your start position */
}
}
In scene 1 (selection):
store charcter selected in GameManager.character
In scene 2 (level):
An empty object with LevelManager script.
Note: Since we use static, I'm actually unsure that you even need the GameManager script to be attached to an empty object, for both scenes.
In the selection scne, just make sure to assign the proper thing to character (instance of Zelda or Megaman character prefab)

Assign gameObject of a script via another script?

How do I assign a gameObject of one script through another script's gameObject? For example;
Script_1
public class Script_1 : MonoBehaviour
{
public OVRScript OVR;
}
Script_2
public class Script_2 : MonoBehaviour
{
private Script_1 src_1;
public GameObject Front;
void Start()
{
src_1 = (Script_1) GameObject.FindObjectOfType(typeof(Script_1));
src_1.GetComponent<OVRScript >().OVR = Front //I am facing problem here
}
}
Both GameObjects "OVR" and "Front" contain the OVRScript
src_1.GetComponent<OVRScript>().OVR = Front.GetComponent<OVRScript>().OVR;
I don't know or see the OVRScript class but isn't OVR rather a member of Script_1?
And then you would want to use GetComponent on the Front in order to get a component attached to it.
// If possible rather drag your Script_1 in here directly via the Inspector
[SeializeField] private Script_1 src_1;
void Start()
{
// Now I would use find only as fallback
if(!scr_1) src_1 = GameObject.FindObjectOfType<Script_1>();
// then you want to assign the OVR field of the 'src_1' of type 'Script_1'
// and not use 'src_1.GetComponent<OVRScript>()' which would return
// the reference of an 'OVRScript' component attached to the same GameObject as the Script_1
//
// And you want to fill it with the reference of an 'OVRScript' attached to 'Front'
src_1.OVR = Front.GetComponent<OVRScript>();
}
(see [SerializeField])
It would be even better if you directly define
public OVRScript Front;
now if you drag in a GameObject it a) is checked if this GameObject actually has a OVRScript attached, otherwise you can't drop it and b) instead of the GameObject reference already the OVRScript reference is serialized and stored so there is no need for GetComponent anymore:
[SeializeField] private Script_1 src_1;
public OVRScript Front;
void Start()
{
// Now I would use find only as fallback
if(!scr_1) src_1 = GameObject.FindObjectOfType<Script_1>();
src_1.OVR = Front;
}

Variables in script do not show in Inspector (Unity)

I'm trying to learn how to use Unity and following online tutorials but I am currently having a problem that I don't understand how to fix.
I have a Sprite in my scene and I have attached a script to it however in the Inspector it shows the script is there but I cannot see the variables inside? I had this problem previously and it sorted itself out.
What is the cause of this problem/how do I fix it?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpaceShip : MonoBehaviour {
public float speed = 30;
public GameObject theBullet;
private void FixedUpdate()
{
float horzMove = Input.GetAxisRaw("Horizontal");
GetComponent<Rigidbody2D>().velocity = new Vector2(horzMove, 0) *
speed;
}
// Update is called once per frame
void Update () {
if (Input.GetButtonDown("Jump"))
{
Instantiate(theBullet, transform.position, Quaternion.identity);
}
}
}
Edit: The problem was solved by reimporting.
You either need to declare the variables as Public or [SerializeField] for member variables to appear in the inspector. Note that by declaring something as public allows access to the variable from outside the class (from other scripts/classes for example). By default, private is assigned to member variables.
Example:
public class testscript : MonoBehaviour
{
public int foo; // shows up in inspector
[SerializeField] private int bar; // also shows up while still being private
void Start()
{
}
}
Not is a problem, You forget to do something surely.
It is common at first with Unity.
Start again.
In the scene create a new GameObject and add you script.
If the inspector shows not variable:
The varible do not is public (false, if is public in you script)
There is some syntax error in the script!
or
You were not adding the correct script to the GameObject.
There are not many secrets to that, if all is well enough that the variable is public and this outside of a method of the script so that it is seen in the inspector.
One tip, do not use a GetComponent or Instantiate inside a FixedUpdate or Update because they are expensive, save the Rigidbody2D in a variable in the Start and then use it.
Sorry for my English and good luck.

Categories