Unity paste <ALL> components recursively to parent and all sub-children - c#

I'm trying to edit an existing component copier for the sake of copying the components over to a new game object all with the same name.
Components are successfully recursively returned with
static Component[] copiedComponents;
static void Copy(){
copiedComponents = UnityEditor.Selection.activeGameObject.GetComponentsInChildren<Component>();
}
pasting is done with
foreach(var targetGameObject in UnityEditor.Selection.gameObjects) {
if (!targetGameObject)
continue;
Undo.RegisterCompleteObjectUndo(targetGameObject, targetGameObject.name + ": Paste All Components");
foreach(var copiedComponent in copiedComponents) {
if (!copiedComponent)
continue;
UnityEditorInternal.ComponentUtility.CopyComponent(copiedComponent);
var targetComponent = targetGameObject.GetComponent(copiedComponent.GetType());
if (targetComponent) // if gameObject already contains the component
{
if (UnityEditorInternal.ComponentUtility.PasteComponentValues(targetComponent)) {
Debug.Log("Successfully pasted: " + copiedComponent.GetType());
} else {
Debug.LogError("Failed to copy: " + copiedComponent.GetType());
}
} else // if gameObject does not contain the component
{
if (UnityEditorInternal.ComponentUtility.PasteComponentAsNew(targetGameObject)) {
Debug.Log("Successfully pasted: " + copiedComponent.GetType());
} else {
Debug.LogError("Failed to copy: " + copiedComponent.GetType());
}
}
}
}
It correctly grabs every component of the parent and every single nested child, but when pasting it will only paste it on the selected parent.
Do I need to store it in a dictionary with keyvalues of the gameObject name and a list of the components or something like that?
Any input would be appreciated

Well you paste into
var targetComponent = targetGameObject.GetComponent(copiedComponent.GetType());
This only looks for components on this target object!
There is no bubbling down any hierarchy like GetComponentsInChildren does
You would basically have to check if the target object's hierarchy even matches at all and look for the correct child object.
For that you would need to additionally store the path to those child objects. You can use AnimationUtility.CalculateTransformPath like e.g.
using System.Linq;
...
private class ComponentInfo
{
public Component Component { get; }
public string Path { get; }
public ComponentInfo(Component component string path)
{
Component = component;
Path = path;
}
}
static ComponentInfo[] copiedComponents;
static void Copy()
{
var sourceRootObject = UnityEditor.Selection.activeGameObject;
// added 'true' to also include inactive
copiedComponents = sourceRootObject.GetComponentsInChildren<Component>(true)
// for each component store along with the path
.Select(c => new ComponentInfo(c, AnimationUtility.CalculateTransformPath(c.transform, sourceRootObject.transform)))
.ToArray();
}
And then later now that you have the paths stored you can use Transform.Find to try and find that object on the receiving side
foreach(var targetGameObject in UnityEditor.Selection.gameObjects)
{
if (!targetGameObject) continue;
Undo.RegisterCompleteObjectUndo(targetGameObject, targetGameObject.name + ": Paste All Components");
foreach(var copiedComponent in copiedComponents)
{
if (!copiedComponent.Component) continue;
// Try to find target child object
GameObject targetObject = null;
if(string.IsNullOrWhiteSpace(copiedComponent.Path))
{
// No path -> root object itself
targetObject = targetGameObject;
}
else
{
// Otherwise try and find child
var child = targetGameObject.transform.Find(copiedComponent.Path);
if(!child)
{
Debug.LogError($"Couldn't find {copiedComponent.Path} within {targetGameObject}");
continue;
}
// If you also want to actually create according children
// I will leave this as a homework for you
// basically you would need to split the path on '/' and for each of those check if
// according child exists and if not create it ;)
targetObject = child.gameObject;
}
// From now on below make sure to use 'targetObject' everywhere instead of 'targetGameObject'
UnityEditorInternal.ComponentUtility.CopyComponent(copiedComponent.Component);
var type = copiedComponent.Component.GetType();
if(targetObject.TryGetComponent(type, out var targetComponent))
{
// if gameObject already contains the component
if (UnityEditorInternal.ComponentUtility.PasteComponentValues(targetComponent))
{
Debug.Log($"Successfully pasted: {type}");
}
else
{
Debug.LogError($"Failed to copy: {type)}");
}
}
else
{
// if gameObject does not contain the component
if (UnityEditorInternal.ComponentUtility.PasteComponentAsNew(targetObject))
{
Debug.Log($"Successfully pasted: {type}");
}
else
{
Debug.LogError($"Failed to copy: {type}");
}
}
}
}

Related

creating global string array through its own class, but still not recognized in different class method C#

Created a simple class with one element, a string [] array and filled it with elements. I want to be able to add and delete elements in this array anywhere in my application, but it is not accessable. been trying variations for two days so coming to the best for help.
enter code here
public static void TestSlots()
{
String currentBehavior = _core.GetVariable("$_current_name", false);
bool success1 = _core.ApiApp().SetDiagOutput("MYWARNING " + currentBehavior + " is");
int indexNumber = MedIntents.medIntents.IndexOf(currentBehavior);
;
if (indexNumber < 0)
{
return;
}
else
{
//Global.MedIntents.RemoveAt(indexNumber);
bool success = _core.ApiApp().SetDiagOutput("MYWARNING " + currentBehavior + " removed");
}
//now check if they asked or hit more than one important entity
return;
}
// Console.WriteLine("Hello World!");
}
internal class MedIntents
{
public string[] medIntents = new string[] {
"any_chills",
"constant_or_intermittent",
"gradual_or_sudden",
"had_them_before",
"how_often",
"howdoesitstart",
"hurt_elsewhere",
"nausea_or_vomitting",
"numbness",
"pain_relief",
"relation_to_food_or_medical",
"scaleofone2ten",
"warning_signs"
};
medIntents is an instance member. You may want to convert it to a static member. To prevent modification, you can add the readonly modifier. – Crafted Pod 2 hours ago

Float from json file returning 0 in Unity

So I'm currently new to Unity, and brand new to using web apis and json files with Unity.
I'm trying to display text on the screen which states the local temperature from a json file, but it keeps displaying 0.
My code:
private IEnumerator GetWeatherInfo()
{
var www = new UnityWebRequest("http://api.weatherapi.com/v1/current.json?key="
+ API_key + "&q=" + latitude + "," + longitude)
{
downloadHandler = new DownloadHandlerBuffer()
};
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
//error
yield break;
}
Info = JsonUtility.FromJson<current>(www.downloadHandler.text);
currentWeatherText.text = "Current weather: " + Info.temp_f;
}
[Serializable]
public class current
{
public float temp_f;
}
Here's the json file in question:
I suspect it may have something to do with the filename but I'm not sure.
Your current class does not match with the JSON structure you receive ...
In the JSON you receive there are only two outer fields location and current so when the serializer searches for a field called temp_f on root level .. well there is none ;)
So you also have to mirror this nested structure and it should probably rather be
[Serializable]
public class Root
{
public Current current;
}
[Serializable]
public class Current
{
public float temp_f;
}
and then use
Info = JsonUtility.FromJson<Root>(www.downloadHandler.text);
currentWeatherText.text = "Current weather: " + Info.current.temp_f;

Valid AssetBundle Throws Null Reference Exception (NRE) With Any API Call

This question is not about what a null reference is, this question is about why a valid AssetBundle is throwing an NRE in an API I cannot step through
I have been converting my project from using the Resource folder to AssetBundles. I have tagged my scriptable objects in the "scriptableobjects" asset bundle, and I've loaded and stored that bundle in the "Assets/AssetBundles" folder.
Here the code where I build my assets:
public class AssetBundleManager
{
[MenuItem("Assets/Build AssetBundles")]
static void BuildAllAssetBundles()
{
string assetBundleDirectory = "Assets/AssetBundles";
if (!Directory.Exists(assetBundleDirectory))
{
Directory.CreateDirectory(assetBundleDirectory);
}
//https://docs.unity3d.com/Manual/AssetBundles-Building.html
BuildPipeline.BuildAssetBundles(assetBundleDirectory,
BuildAssetBundleOptions.UncompressedAssetBundle,
BuildTarget.StandaloneWindows);
}
}
Before looking at the code below, here is proof that the AssetBundle is not null when it is going to be used:
After building the assets, I then go to actually load these assets and their resources:
public class X_AssetBundleManager : MonoBehaviour
{
private static Maps _maps;
private static AssetBundle _scriptableObjectsBundle;
public static T LoadScriptableObject<T>(string assetName) where T : ScriptableObject
{
if (_scriptableObjectsBundle == null)
{
AssetBundle _scriptableObjectsBundle = AssetBundle.LoadFromFile("Assets/AssetBundles/scriptableobjects");
if (_scriptableObjectsBundle == null)
{
Debug.Log("Failed to load 'scriptableobjects' AssetBundle!");
return null;
}
}
if (_scriptableObjectsBundle.Contains(assetName)) // NRE HERE
{
Debug.Log("DOES CONTAIN");
}
else
{
Debug.Log("DOES NOT CONTAIN");
}
try
{
// NRE HERE
var all = _scriptableObjectsBundle.LoadAllAssets();
}
catch (Exception e)
{
Debug.LogError(e);
}
T obj = _scriptableObjectsBundle.LoadAsset<T>(assetName); // NRE HERE
if (obj == null)
{
Debug.LogError("Failed to load " + assetName + " in scriptableobjects");
}
return obj;
}
}
The _scriptableObjects variable is not null. Its a valid object. However, calling ANY api on it causes a null reference exception.
I can't even step through the code to see whats null. The AssetBundle itself is not null, but any call causes this.
Any advice on how to further debug this issue or what the problem might be?
in
AssetBundle _scriptableObjectsBundle = ...
you overrule (hide) your already existing field variable equally named _scriptableObjectsBundle ... so this outer field _scriptableObjectsBundle still remains null! and the one you actually assign only exists within the
if(_scriptableObjectsBundle == null)
block &rightarrow; simply remove this additional AssetBundle
_scriptableObjectsBundle = AssetBundle.LoadFromFile("Assets/AssetBundles/scriptableobjects");

How to validate if a folder already exists

I want to create folders and add classes within the folders. I can create the folder once, but once I have created it, I just want to add classes. My code gives error because I'm trying to create a folder several times, which is not right. So before adding a class to the folder, I want to check if the folder already exists and if it exists I just want to add the class.
ProjectItem rootFolder = project.ProjectItems.AddFolder(folder);
ProjectItem item = rootFolder.ProjectItems.AddFromTemplate(itemPath, className);
According to documentation, there is no kind of Exists function which would tell us if a folder already existed.
So you have at least two options:
1. Try and ignore
Simply:
try
{
var rootFolder = project.ProjectItems.AddFolder(folder);
}
catch
{
/* folder already exists, nothing to do */
}
2. Solution folders can only occur in the first level below the solution root node, so we can get away with a non-recursive solution.
public static bool CheckIfSolutionFolderExists(ProjectItems projectItems, string foldername)
{
foreach(var projectItem in projectItems)
{
if(projectItem.Kind == EnvDTE.vsProjectItemKindVirtualFolder)
{
if(projectItem.Name == foldername)
{
return true;
}
}
}
return false;
}
For a recursive solution, I found this, which boils down to:
public static bool CheckIfFileExistsInProject(ProjectItems projectItems, string fullpath)
{
foreach(ProjectItem projectItem in projectItems)
{
if(projectItem.Name == fullpath)
{
return true;
}
else if ((projectItem.ProjectItems != null) && (projectItem.ProjectItems.Count > 0))
{
/* recursive search */
return CheckIfFileExistsInProject(projectItem.ProjectItems, fullpath);
}
}
return false;
}
A robust pattern for solution and project folder management would use AddFromDirectory whenever you want to mirror the file system hierarchy in the project tree, and AddFolder only for virtual folders that have no representation in the file system.
ProjectItem item;
if(Directory.Exists(itemname))
{
item = project.AddFromDirectory(itemname);
}
else
{
item = project.AddFolder(itemname);
}
(source: inspired from this)
You can use method Directory.Exists
and in your code it'll be look like there
if(!Directory.Exists(folder)) {
ProjectItem rootFolder = project.ProjectItems.AddFolder(folder);
}
ProjectItem item = rootFolder.ProjectItems.AddFromTemplate(itemPath, className);
You need to do the following:
if(Directory.Exists(path))
{
// The folder already exists
}
else
{
//Create a new folder here ...
}
Please check this out for more details on Directory.Exists

Making elements changeable from references

So! I've got a C# array.
And I've got a function that returns an element from the array, so the data from that reference can be accessed. Yay!
It would be really super awesome convenient if changing that reference then affected that original element in the array. Is this what static variables do? Is there a way to do it? How to do? For example:
Function A finds an item:
public TutorialPopupBehavior GetBehavior(string behaviorName) {
foreach(TutorialPopupBehavior beh in _tutorialItems) {
if(beh._popupName == behaviorName) {
return beh;
}
}
print ("Could not find behavior of name " + behaviorName);
return null;
}
And then returns it to function B, which then, ideally, would be able to change a property of the returned item:
public void SetTutorialItem(bool state, string itemName) {
TutorialPopupBehavior beh = GetBehavior(itemName);
if(beh == null) {
print ("No tutorial item found, so can't set it to " + state);
return;
}
//idealistic code: beh._isShown = true;
}
The _isShown property of that element would then be changed permanently in the original _tutorialItems array...how do you all accomplish this, or design differently, so as to avoid the problem? The reason I ask is because I have a number of arrays to search, and I don't want to complicate my code by asking the same class to search through the same set of arrays more than once.
public void GetBehavior(string behaviorName, ref TutorialPopupBehavior b) {
foreach(TutorialPopupBehavior beh in _tutorialItems) {
if(beh._popupName == behaviorName) {
b = beh;
Return;
}
}
print ("Could not find behavior of name " + behaviorName);
b = null;
}
Read this msdn article

Categories

Resources