Unity 3D: Loading FBX dynamically using AssetBundle - c#

i am trying to load an already created FBX object into the scene at runtime, i searched around and found that assetbundle can be used to do so. I tried this code but it doesnt seem to instantiate the object in the scene and neither does it pop an error.
Here is the code
using System;
using UnityEngine;
using System.Collections;
public class CachingLoadExample : MonoBehaviour {
public string BundleURL;
public string AssetName;
public int version;
void Start() {
StartCoroutine (DownloadAndCache());
}
IEnumerator DownloadAndCache (){
// Wait for the Caching system to be ready
while (!Caching.ready)
yield return null;
// Load the AssetBundle file from Cache if it exists with the same version or download and store it in the cache
using(WWW www = WWW.LoadFromCacheOrDownload (BundleURL, version)){
yield return www;
if (www.error != null)
throw new Exception("WWW download had an error:" + www.error);
AssetBundle bundle = www.assetBundle;
if (AssetName == "")
Instantiate(bundle.mainAsset);
else
Instantiate(bundle.Load(AssetName));
// Unload the AssetBundles compressed contents to conserve memory
bundle.Unload(false);
} // memory is freed from the web stream (www.Dispose() gets called implicitly)
}
}
I added a new empty game object, dragged the C# code to that game object, supplied the asset bundle link "file://C:/Users/Sahibzada/Documents/New Unity Project/Assets/100777102370.FBX" but no luck
Can someone please guide me, whats wrong with the code, i am totally new with scripting in Unity, Thanks

Create a folder named AssetBundles inside your Assets folder, then
you need to create the AssetBundle of your FBX with an Editor script as show in: http://docs.unity3d.com/Manual/BuildingAssetBundles.html
using UnityEditor;
public class CreateAssetBundles
{
[MenuItem ("Assets/Build AssetBundles")]
static void BuildAllAssetBundles ()
{
BuildPipeline.BuildAssetBundles ("Assets/AssetBundles");
}
}
Lastly you need to introduce, in the BundleURL the URL to the new AssetsBundle:
"file://C:/Users/Sahibzada/Documents/New Unity Project/Assets/AssetsBundles/yourassetbundle"
and on the AssetName you need to introduce "yourassetbundle"
I also recommend not having paths with spaces, this can also be an issue.

Related

"The name "Asset Database" does not exist in the current context" error while building unity game [duplicate]

This question already has answers here:
UNITY - The name `AssetDatabase' does not exist in the current context
(3 answers)
Closed 2 years ago.
My teacher gave me a challenge in unity (c#) where I have to load prefabs into an array (as a GameObject[] function). The code I have works in the editor, but when I build the game, it gives me the error The name "Asset Database" does not exist in the curernt context. The code I have currently is:
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEditor;
using System;
public class FindCards : MonoBehaviour
{
public string directoryName;
public List<GameObject> foundGameObjects;
public GameObject[] ListOfCards()
{
// get the directory and the files in them
DirectoryInfo dirInfo = new DirectoryInfo(Application.dataPath + directoryName);
FileInfo[] fileInfo = dirInfo.GetFiles("*.prefab");
// loop through the files
foreach(FileInfo file in fileInfo)
{
// get the path to the directory again (with proper formatting)
string fullPath = file.FullName.Replace(#"\","/");
string assetPath = "Assets" + fullPath.Replace(Application.dataPath, "");
GameObject _asset;
// load the asset from the direcotry
_asset = AssetDatabase.LoadAssetAtPath(assetPath, typeof(GameObject)) as GameObject;
// add it to the list
foundGameObjects.Add(_asset);
Debug.Log(_asset.name);
}
// return the list as an array
return(foundGameObjects.ToArray());
}
public void SetCardProperties(string cardName, Card card)
{
// get the first string, and the int
// from the card's name
string[] sub = cardName.Split('_');
// (if the name was Diamond_07, // string name
// there would be an int = 07 and // name[0]
// a string = Diamond) // name[1]
card.suit = sub[0];
Int32.TryParse(sub[1], out card.number);
}
}
The code that is calling this function looks like this (note that this is in a different script):
private void Start()
{
canScore = true;
gameIsGoing = true;
// set up cards
cards = findCards.ListOfCards();
for (int i = 0; i < cards.Length; i++)
{
Card card = cards[i].GetComponent<Card>();
findCards.SetCardProperties(cards[i].name, card);
}
// set up deck
ResetDeck();
}
The directory here:
Prefabs in the directory
Errors here:
Errors unity gives me
I have been pulling my hair out, because there was nothing online I could find that would help me. My teacher gave this to me as challenge because he also did not know how to do it which is why I'm asking this question...
Thanks in advance!
AssetsDatabase it seems that works only in Editor. Check this thread for more info:
https://answers.unity.com/questions/1068055/assetdatabase.html
Edit:
`Resources.Load()ยด search in the path: "Assets/Resources".
Look what documentation says:
If an asset can be found at path, it is returned with type T, otherwise returns null. If the file at path is of a type that cannot be converted to T, also returns null. The path is relative to any folder named Resources inside the Assets folder of your project. More than one Resources folder can be used. For example, a project may have Resources folders called Assets/Resources and ##AssetsAssetsResources##. The path does not need to include Assets and Resources in the string, for example loading a GameObject at ##AssetspathShotgun.prefab## would only require Shotgun as the path. Also, if ##AssetspathMissiles/PlasmaGun.prefab exists it can be loaded using GunspathPlasmaGun## as the path string. If you have multiple Resources folders you cannot duplicate the use of an asset name.

can't be loaded because another AssetBundle with the same files is already loaded. error while loading different bundles

I've tried load asset bundles from URLs. When I load only single asset bundle, it works well. But if I try to load multiple asset bundles, Unity gives this error: The AssetBundle 'https://example.com/uc?export=download&id=1234567' can't be loaded because another AssetBundle with the same files is already loaded.
example code as follows
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using TMPro;
public class sahnecagir : MonoBehaviour
{
public TextMeshProUGUI Log;
// Start is called before the first frame update
void Start()
{
StartCoroutine(LoadBundleFromUrl("https://example.com/uc?export=download&id=123456", 2, 0, (uint)Crc.Gifts));
}
IEnumerator LoadBundleFromUrl(string url, int tip, uint version, uint crc)
{
using (UnityWebRequest uwr = UnityWebRequestAssetBundle.GetAssetBundle(url, version, crc))
{
uwr.SendWebRequest();
while (!uwr.isDone)
{
if(tip == 1) Log.text = "Rooms loading " + (uwr.downloadProgress * 100).ToString("0") + "%";
else if(tip == 2) Log.text = "Gifts loading " + (uwr.downloadProgress * 100).ToString("0") + "%";
yield return null;
}
if (uwr.isNetworkError || uwr.isHttpError)
{
Debug.Log(uwr.error);
}
else
{
if (tip == 1)
{
MainConfig.Instance.RoomBgBundle = DownloadHandlerAssetBundle.GetContent(uwr);
}
else if (tip == 2)
{
MainConfig.Instance.GiftBundle = DownloadHandlerAssetBundle.GetContent(uwr);
}
}
if (uwr.isDone && tip == 1)
{
Log.text = "Rooms loading 100%";
StartCoroutine(LoadBundleFromUrl("https://example.com/uc?export=download&id=1234567", 2, 0, 0));
}
else
{
Log.text = "Gifts loading 100%";
}
yield return null;
}
}
}
In my case I've been trying to modify a bundle with different files but Unity refuses to load the two different files even if they have different names, a different structure, different files inside, nothing.
Opening the bundle in Notepad++ brings up what I assume to be a "container name" of CAB-a317cdbf067bab183d6dd12d870c1407 as an example. This is what Unity seems to be checking for to determine whether a bundle is "identical."
A simple find and replace to a different name and boom. The bundles load now.

Use monobehaviour from asmdef generated assembly

We would like to distribute our project with assembly files instead of .cs scripts.
We thought that this would be easy thanks to assembly definition files, as unity is creating assembly files for the scripts they refer to anyway.
It turns out that when removing the .cs files and putting the assemblies, we ran into a problem :
The monobehaviors defined in the assemblies (so previously in our scripts) can't be added manually to a scene :
"Can't add script component xxx because the script class cannot be found"
While if we add the component through script (i.e. AddComponent) it works.
I'm using Unity 2017.3.f1 to generate the assembly files
Is there a trick to make this work ? or should I try to generate the assemblies using another approach ?
OP here.
Short answer is : don't keep both asmdef and assembly files. Remove the asmdef file if you replace the scripts with the generated assembly
What I ended up doing is the roughly following (this was for CI purpose):
First, we need to make sure Unity compiles the assembly file. So I have a GenerateAssemblies.cs file in an Editor folder that can be executed from command line:
GenerateAssemblies.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.Compilation;
using UnityEngine;
[InitializeOnLoad]
public static class GenerateAssemblies
{
private static string BATCH_MODE_PARAM = "-batchmode";
private const string REPLACE_ASSEMBLY_PARAM = "-replaceassembly";
static GenerateAssemblies()
{
List<String> args = Environment.GetCommandLineArgs().ToList();
if (args.Any(arg => arg.ToLower().Equals(BATCH_MODE_PARAM)))
{
Debug.LogFormat("GenerateAssemblies will try to parse the command line to replace assemblies.\n" +
"\t Use {0} \"assemblyname\" for every assembly you wish to replace"
, REPLACE_ASSEMBLY_PARAM);
}
if (args.Any(arg => arg.ToLower().Equals(REPLACE_ASSEMBLY_PARAM))) // is a replacement requested ?
{
int lastIndex = 0;
while (lastIndex != -1)
{
lastIndex = args.FindIndex(lastIndex, arg => arg.ToLower().Equals(REPLACE_ASSEMBLY_PARAM));
if (lastIndex >= 0 && lastIndex + 1 < args.Count)
{
string assemblyToReplace = args[lastIndex + 1];
if (!assemblyToReplace.EndsWith(ReplaceAssemblies.ASSEMBLY_EXTENSION))
assemblyToReplace = assemblyToReplace + ReplaceAssemblies.ASSEMBLY_EXTENSION;
ReplaceAssemblies.instance.AddAssemblyFileToReplace(assemblyToReplace);
Debug.LogFormat("Added assembly {0} to the list of assemblies to replace.", assemblyToReplace);
lastIndex++;
}
}
CompilationPipeline.assemblyCompilationFinished += ReplaceAssemblies.instance.ReplaceAssembly; /* This serves as callback after Unity as compiled an assembly */
Debug.Log("Forcing recompilation of all scripts");
// to force recompilation
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone) + ";DUMMY_SYMBOL");
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
}
}
}
Then I have a ReplaceAssemblies.cs file in an editor folder that will :
find the assembly file correpsonding to the asmdef file
save the guid/classes correspondance of the script files
move the script files in a temporary folder
move the assembly in the same folder as the asmdef file
move the asmdef to a temporary folder
Replace the Guid and File ID values for each script in the assembly (to avoid breaking references in scenes and prefabs)
ReplaceAssemblies.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEditor.Compilation;
using UnityEngine;
public class ReplaceAssemblies : ScriptableSingleton<ReplaceAssemblies>
{
public static string ASSEMBLY_EXTENSION = ".dll";
public static string ASSEMBLY_DEFINITION_EXTENSION = ".asmdef";
[SerializeField]
private List<String> assembliesFilesToReplace = new List<string>();
[SerializeField]
private List<string> pathsOfAssemblyFilesInAssetFolder = new List<string>();
[SerializeField]
private List<string> pathsOfAssemblyFilesCreatedByUnity = new List<string>();
[SerializeField]
private string tempSourceFilePath;
private static readonly string[] fileListPath = { "*.prefab", "*.unity", "*.asset" };
public string TempSourceFilePath
{
get
{
if (String.IsNullOrEmpty(tempSourceFilePath))
{
tempSourceFilePath = FileUtil.GetUniqueTempPathInProject();
}
return tempSourceFilePath;
}
}
void OnEnable()
{
Debug.Log("temp dir : " + TempSourceFilePath);
}
public void ReplaceAssembly(string assemblyPath, CompilerMessage[] messages)
{
string assemblyFileName = assembliesFilesToReplace.Find(assembly => assemblyPath.EndsWith(assembly));
// is this one of the assemblies we want to replace ?
if (!String.IsNullOrEmpty(assemblyFileName))
{
string[] assemblyDefinitionFilePaths = Directory.GetFiles(".", Path.GetFileNameWithoutExtension(assemblyFileName) + ASSEMBLY_DEFINITION_EXTENSION, SearchOption.AllDirectories);
if (assemblyDefinitionFilePaths.Length > 0)
{
string assemblyDefinitionFilePath = assemblyDefinitionFilePaths[0];
ReplaceAssembly(assemblyDefinitionFilePath);
}
}
}
public void AddAssemblyFileToReplace(string assemblyFile)
{
assembliesFilesToReplace.Add(assemblyFile);
}
private void ReplaceAssembly(string assemblyDefinitionFilePath)
{
Debug.LogFormat("Replacing scripts for assembly definition file {0}", assemblyDefinitionFilePath);
string asmdefDirectory = Path.GetDirectoryName(assemblyDefinitionFilePath);
string assemblyName = Path.GetFileNameWithoutExtension(assemblyDefinitionFilePath);
Assembly assemblyToReplace = CompilationPipeline.GetAssemblies().ToList().Find(assembly => assembly.name.ToLower().Equals(assemblyName.ToLower()));
string assemblyPath = assemblyToReplace.outputPath;
string assemblyFileName = Path.GetFileName(assemblyPath);
string[] assemblyFilePathInAssets = Directory.GetFiles("./Assets", assemblyFileName, SearchOption.AllDirectories);
// save the guid/classname correspondance of the scripts that we will remove
Dictionary<string, string> oldGUIDToClassNameMap = new Dictionary<string, string>();
if (assemblyFilePathInAssets.Length <= 0)
{
// Move all script files outside the asset folder
foreach (string sourceFile in assemblyToReplace.sourceFiles)
{
string tempScriptPath = Path.Combine(TempSourceFilePath, sourceFile);
Directory.CreateDirectory(Path.GetDirectoryName(tempScriptPath));
if (!File.Exists(sourceFile))
Debug.LogErrorFormat("File {0} does not exist while the assembly {1} references it.", sourceFile, assemblyToReplace.name);
Debug.Log("will move " + sourceFile + " to " + tempScriptPath);
// save the guid of the file because we may need to replace it later
MonoScript monoScript = AssetDatabase.LoadAssetAtPath<MonoScript>(sourceFile);
if (monoScript != null && monoScript.GetClass() != null)
oldGUIDToClassNameMap.Add(AssetDatabase.AssetPathToGUID(sourceFile), monoScript.GetClass().FullName);
FileUtil.MoveFileOrDirectory(sourceFile, tempScriptPath);
}
Debug.Log("Map of GUID/Class : \n" + String.Join("\n", oldGUIDToClassNameMap.Select(pair => pair.Key + " : " + pair.Value).ToArray()));
string finalAssemblyPath = Path.Combine(asmdefDirectory, assemblyFileName);
Debug.Log("will move " + assemblyPath + " to " + finalAssemblyPath);
FileUtil.MoveFileOrDirectory(assemblyPath, finalAssemblyPath);
string tempAsmdefPath = Path.Combine(TempSourceFilePath, Path.GetFileName(assemblyDefinitionFilePath));
Debug.Log("will move " + assemblyDefinitionFilePath + " to " + tempAsmdefPath);
FileUtil.MoveFileOrDirectory(assemblyDefinitionFilePath, tempAsmdefPath);
// Rename the asmdef meta file to the dll meta file so that the dll guid stays the same
FileUtil.MoveFileOrDirectory(assemblyDefinitionFilePath + ".meta", finalAssemblyPath + ".meta");
pathsOfAssemblyFilesInAssetFolder.Add(finalAssemblyPath);
pathsOfAssemblyFilesCreatedByUnity.Add(assemblyPath);
// We need to refresh before accessing the assets in the new assembly
AssetDatabase.Refresh();
// We need to remove .\ when using LoadAsslAssetsAtPath
string cleanFinalAssemblyPath = finalAssemblyPath.Replace(".\\", "");
var assetsInAssembly = AssetDatabase.LoadAllAssetsAtPath(cleanFinalAssemblyPath);
// list all components in the assembly file.
var assemblyObjects = assetsInAssembly.OfType<MonoScript>().ToArray();
// save the new GUID and file ID for the MonoScript in the new assembly
Dictionary<string, KeyValuePair<string, long>> newMonoScriptToIDsMap = new Dictionary<string, KeyValuePair<string, long>>();
// for each component, replace the guid and fileID file
for (var i = 0; i < assemblyObjects.Length; i++)
{
long dllFileId;
string dllGuid = null;
if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(assemblyObjects[i], out dllGuid, out dllFileId))
{
string fullClassName = assemblyObjects[i].GetClass().FullName;
newMonoScriptToIDsMap.Add(fullClassName, new KeyValuePair<string, long>(dllGuid, dllFileId));
}
}
Debug.Log("Map of Class/GUID:FILEID : \n" + String.Join("\n", newMonoScriptToIDsMap.Select(pair => pair.Key + " : " + pair.Value.Key + " - " + pair.Value.Value).ToArray()));
ReplaceIdsInAssets(oldGUIDToClassNameMap, newMonoScriptToIDsMap);
}
else
{
Debug.Log("Already found an assembly file named " + assemblyFileName + " in asset folder");
}
}
/// <summary>
/// Replace ids in all asset files using the given maps
/// </summary>
/// <param name="oldGUIDToClassNameMap">Maps GUID to be replaced => FullClassName</param>
/// <param name="newMonoScriptToIDsMap">Maps FullClassName => new GUID, new FileID</param>
private static void ReplaceIdsInAssets(Dictionary<string, string> oldGUIDToClassNameMap, Dictionary<string, KeyValuePair<string, long>> newMonoScriptToIDsMap)
{
StringBuilder output = new StringBuilder("Report of replaced ids : \n");
// list all the potential files that might need guid and fileID update
List<string> fileList = new List<string>();
foreach (string extension in fileListPath)
{
fileList.AddRange(Directory.GetFiles(Application.dataPath, extension, SearchOption.AllDirectories));
}
foreach (string file in fileList)
{
string[] fileLines = File.ReadAllLines(file);
for (int line = 0; line < fileLines.Length; line++)
{
//find all instances of the string "guid: " and grab the next 32 characters as the old GUID
if (fileLines[line].Contains("guid: "))
{
int index = fileLines[line].IndexOf("guid: ") + 6;
string oldGUID = fileLines[line].Substring(index, 32); // GUID has 32 characters.
if (oldGUIDToClassNameMap.ContainsKey(oldGUID) && newMonoScriptToIDsMap.ContainsKey(oldGUIDToClassNameMap[oldGUID]))
{
fileLines[line] = fileLines[line].Replace(oldGUID, newMonoScriptToIDsMap[oldGUIDToClassNameMap[oldGUID]].Key);
output.AppendFormat("File {0} : Found GUID {1} of class {2}. Replaced with new GUID {3}.", file, oldGUID, oldGUIDToClassNameMap[oldGUID], newMonoScriptToIDsMap[oldGUIDToClassNameMap[oldGUID]].Key);
if (fileLines[line].Contains("fileID: "))
{
index = fileLines[line].IndexOf("fileID: ") + 8;
int index2 = fileLines[line].IndexOf(",", index);
string oldFileID = fileLines[line].Substring(index, index2 - index); // GUID has 32 characters.
fileLines[line] = fileLines[line].Replace(oldFileID, newMonoScriptToIDsMap[oldGUIDToClassNameMap[oldGUID]].Value.ToString());
output.AppendFormat("Replaced fileID {0} with {1}", oldGUID, newMonoScriptToIDsMap[oldGUIDToClassNameMap[oldGUID]].Value.ToString());
}
output.Append("\n");
}
}
}
//Write the lines back to the file
File.WriteAllLines(file, fileLines);
}
Debug.Log(output.ToString());
}
[MenuItem("Tools/Replace Assembly")]
public static void ReplaceAssemblyMenu()
{
string assemblyDefinitionFilePath = EditorUtility.OpenFilePanel(
title: "Select Assembly Definition File",
directory: Application.dataPath,
extension: ASSEMBLY_DEFINITION_EXTENSION.Substring(1));
if (assemblyDefinitionFilePath.Length == 0)
return;
instance.ReplaceAssembly(assemblyDefinitionFilePath);
}
}
I was experiencing this issue, and like you, I was using the information provided from asmdef files to provide all the required information (which .cs files, what references, defines, etc) to build an assembly.
I found that the issue was the DLL I was creating had the same name as the asmdef file I was using to provide the information. Even though the asmdef file was no longer being compiled (because all the scripts had been removed to build the DLL), it was still interfering with the project.
So for me, the inconsistency between accessing a script from inside the editor and from inside scripts was because there was a DLL and as asmdef file with the same name in the project.
Giving the compiled DLL a different name or removing the asmdef file was the solution for me.
Just tested with Unity 2019.3.0b1.
Content of test class:
using System.Reflection;
using UnityEngine;
namespace Assets.Test
{
public class TestBehaviour : MonoBehaviour
{
void Start()
{
Debug.Log(Assembly.GetAssembly(GetType()));
}
}
}
First project with source code and assembly definition file
Second project with the generated DLL, working as expected
As far as i'm concerned, the use of asmdef merely forces unity3d to compile your scripts into separate assemblies that are then referenced by your project.
Actually, it creates projects in your unity solution that contain your .cs files and each of these projects is compiled into its own output assembly.
The error you are seeing might be related to assembly caching.
I've had that error a few months ago and it was due to an outdated assembly still being cached.
As a result, unity3d editor kinda hiccuped when loading the project and therefore could not load the specific assembly.
I fixed it by deleting the directories Library, obj and Temp and then reloaded the unity3d project.
To get rid of that for good, we have moved away from asmdef and .cs files inside our unity projects once and for all.
All our scripts have been extracted to separate projects that are never touched by unity3d.
Every project references UnityEngine.dll and/or UnityEditor.dll (for Editor assemblies) depending on which unity3d types it may require.
The projects are built locally using Visual Studio or server side in our CI pipeline.
Output is copied manually into the assets directory of a unity project where it is then loaded from within unity3d editor.
This last step is a pain admittedly but i have yet to find time to streamline this process some more.
This approach has a few benefits
We are in control of our code in one single repository.
There is only one single point of truth and every developer commits changes onto the same code base.
There are no copies of .cs files across any number of unity projects that consume our types.
There is no need to figure out merge conflicts from updating a unitypackage where there have been deletions.
Unit tests can be done server side (CI pipeline) without the need of some docker image with unity3d on top (ofc there are restrictions on how much you can test without the entire unity3d environment running).
We create our own NuGet packages that can be referenced in projects (vcproj, not unity projects!).
Types deriving from MonoBehaviour can be added to GameObjects via code or via unity3d editor.
You also get to explore loaded assemblies inside your unity3d editor project view by clicking on the arrow of an assembly which will expand to show the list of contained relevant types.
Let's talk about downsides
One example is that we use SteamVR for interacting with controls.
The SteamVR plugin for unity3d is released through unity's asset store and annoyingly it contains script files and resources but no assemblies.
This goes for pretty much all assets in the store by the way.
Since we can't build against code, we have to go through the trouble of compiling SteamVR once and then copy the output assembly somewhere else.
This is not just as tedious as a task can be, it also has some limitations of its own which i get to later.
Anyway, this lets us reference a compiled asset with our own code so we get to use asset specific types like SteamVR_Action in our code without having to use unity3d editor and script files in unity projects (or reflection which would be even worse).
Limitations of compiled assets like this are two fold.
For once, it is horribly inefficient to get there in the first place.
On the other hand, you'll only have to do that once for every version of an asset.
Once that's done, make it a private NuGet package and you're golden.
The other limitation is the way how unity3d approaches dependency injection.
Actually i'm not entirely sure what it really is they try to do but here goes.
Unity3d wants you to only ever reference assemblies from within ../UnityInstallDirectory/Editor/Data/Managed/.
In a perfect world, your own assemblies reference that big gunky UnityEngine.dll in this directory and once loaded by unity3d editor everything works as expected.
When you compile a unity project from within unity3d editor however, the resulting assembly references all the assemblies from within ../UnityInstallDirectory/Editor/Data/Managed/UnityEngine/ which contains a very small version of UnityEngine.dll which in turn acts as a type forwarder to all the other sub modules.
Not such a perfect world now is it?
Your previously compiled asset requires the type MonoBehaviour to sit in an assembly called UnityEngine.CoreModule.dll.
Your own project however expects it to sit in UnityEngine.dll since you're a good fellow and follow the rules.
This is just asking for trouble and to get around this problem we are now directly referencing all the managed sub modules from within ../UnityInstallDirectory/Editor/Data/Managed/UnityEngine/.
We also ignore unity3d editor moaning about how we are doing it wrong.
tl;dr
By doing all from above and leaving asmdef and .cs files out of the equation we are able to build, unit test and pack our logic and types into assemblies.
We are also able to keep a clean code base that can be easily maintained and extended without dozens of copies of the same code in multiple locations and/or repositories.
Why unity3d does things the way they do, i'll never understand.
I do know there is a thing called Building from HEAD but since the entirety of the .net ecosystem is using the binary format to share content in the form of referable assemblies, why would you want to do things differently?
This is a topic for another day though.
If you made it all the way through this post, i sincerely hope it is helping you fix your problem at hand.
In case i misinterpreted your question ... sorry :-)
Unity3d is weird ...

Load file downloaded iOS app created in Unity

I'm trying to load a json file downloaded and saved in the persistentDataPath of an iOS device. The download and save works great. However I think I have big issues to load the file. In fact when I try to compile the project in Xcode I have some errors messages.
First here is my C# code :
using UnityEngine;
using System.Collections;
using System.IO;
using System.Net;
using UnityEngine.UI;
public class ReadJson : MonoBehaviour
{
public Text City;
public Text Temperature;
public Image Weather;
public Text WeatherUrl;
[System.Serializable]
public class CityInfo
{
public string name;
}
[System.Serializable]
public class CurrentCondition
{
public string date;
public string hour;
public int tmp;
public int wnd_spd;
public int wnd_gust;
public string wnd_dir;
public double pressure;
public int humidity;
public string condition;
public string icon;
public string icon_big;
}
[System.Serializable]
public class RootObject
{
public CityInfo city_info;
public CurrentCondition current_condition;
}
void Start () {
WebClient client = new WebClient();
File.Delete(Path.Combine (Application.persistentDataPath, "myjson.json"));
client.DownloadFile ("http://www.myjsonurl", Path.Combine (Application.persistentDataPath, "myjson.json"));
TextAsset asset = Resources.Load (Path.Combine (Application.dataPath + "/Documents", "myjson")) as TextAsset;
RootObject m = JsonUtility.FromJson<RootObject> (asset.text);
City.text = m.city_info.name;
Temperature.text = (m.current_condition.tmp).ToString();
}
}
And now the xcode console :
2016-10-21 17:01:20.766001 json[1404:516674] [DYMTLInitPlatform] platform initialization successful
2016-10-21 17:01:20.929950 json[1404:516508] -> registered mono modules 0xb95f70
2016-10-21 17:01:21.356590 json[1404:516508] You've implemented -[<UIApplicationDelegate> application:didReceiveRemoteNotification:fetchCompletionHandler:], but you still need to add "remote-notification" to the list of your supported UIBackgroundModes in your Info.plist.
-> applicationDidFinishLaunching()
2016-10-21 17:01:21.452967 json[1404:516508] Metal GPU Frame Capture Enabled
2016-10-21 17:01:21.453369 json[1404:516508] Metal API Validation Enabled
-> applicationDidBecomeActive()
Init: screen size 750x1334
Initializing Metal device caps
Initialize engine version: 5.3.4f1 (fdbb5133b820)
UnloadTime: 1.705875 ms
Salut/var/mobile/Containers/Data/Application/51A2490E-94EB-4904-9F2E-112AD5632A98/Documents
(Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 37)
NullReferenceException: A null value was found where an object instance was required.
(Filename: currently not available on il2cpp Line: -1)
Setting up 1 worker threads for Enlighten.
Thread -> id: 19f17000 -> priority: 1
If someone has an idea. Thanks a lot in advance.
The problem with your code is on this line:
TextAsset asset = Resources.Load(Path.Combine(Application.dataPath + "/Documents", "myjson")) as TextAsset;
I don't think you understand how and when to use Resources.Load. If you want to use the Resources.Load function, you must create a special folder from the Editor. This folder should be at <ProjectDirectory>/Assets/Resources. You can only put/write stuff into this folder from the Editor. You can't do it after build on iOS. It becomes read only after you build it. This special folder can then be read with the Resources.Load function.
Replacing:
TextAsset asset = Resources.Load(Path.Combine(Application.dataPath + "/Documents", "myjson")) as TextAsset;
with
string text = File.ReadAllText(Path.Combine(Application.persistentDataPath, "myjson.json"));
RootObject m = JsonUtility.FromJson<RootObject>(text);
City.text = m.city_info.name;
Temperature.text = (m.current_condition.tmp).ToString();
Should solve your problem.
Another API that should not be using is WebClient. Unless you have a good reason to use that, you shouldn't. You should either use the WWW or UnityWebRequest(newer and recommended) API.
This is what your code should look like without WebClient:
void Start()
{
StartCoroutine(DownloadAndloadJson());
}
IEnumerator DownloadAndloadJson()
{
string url = "http://www.myjsonurl";
UnityWebRequest www = UnityWebRequest.Get(url);
yield return www.Send();
if (www.isError)
{
Debug.Log(www.error);
}
else
{
//Sucessfully downloaded the Json File
Debug.Log("Json: " + www.downloadHandler.text);
string jsonFile = www.downloadHandler.text;
//Delete old json file. Don't know why but this was in your original code
File.Delete(Path.Combine(Application.persistentDataPath, "myjson.json"));
//Save the downloaded data as File
File.WriteAllText(Path.Combine(Application.persistentDataPath, "myjson.json"), jsonFile);
//Load the downloaded data
string text = File.ReadAllText(Path.Combine(Application.persistentDataPath, "myjson.json"));
Debug.Log(text);
RootObject m = JsonUtility.FromJson<RootObject>(text);
City.text = m.city_info.name;
Temperature.text = (m.current_condition.tmp).ToString();
}
}
If you have problems please update to Unity 5.4. The UnityWebRequest API should be used for this and it was introduced in 5.3 but bugs were fixed in 5.4. You need using UnityEngine.Networking; to use UnityWebRequest.

How to play sounds loaded on the fly

I trying to make a tool for the sound designer of my group that would allow him to hear his sound file played in Unity.
Check if it's loadable
Check if the volume is right
Check if loops properly
and so on ...
My issue is to find how Unity can manage loading audio files laying somewhere in a folder.
I found a lot of topics speaking about it but no real solutions to how you can make Unity load external files dynamically.
If you want to load files from the same directory as the .exe / .app you can use this :
Using the System.IO DirectoryInfo() to get all files names
Using the WWW class to stream/load the files found
Here the code :
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
public class SoundPlayer : MonoBehaviour {
string absolutePath = "./"; // relative path to where the app is running
AudioSource src;
List<AudioClip> clips = new List<AudioClip>();
int soundIndex = 0;
//compatible file extensions
string[] fileTypes = {"ogg","wav"};
FileInfo[] files;
void Start () {
//being able to test in unity
if(Application.isEditor) absolutePath = "Assets/";
if(src == null) src = gameObject.AddComponent<AudioSource>();
reloadSounds();
}
void reloadSounds() {
DirectoryInfo info = new DirectoryInfo(absolutePath);
files = info.GetFiles();
//check if the file is valid and load it
foreach(FileInfo f in files) {
if(validFileType(f.FullName)) {
//Debug.Log("Start loading "+f.FullName);
StartCoroutine(loadFile(f.FullName));
}
}
}
bool validFileType(string filename) {
foreach(string ext in fileTypes) {
if(filename.IndexOf(ext) > -1) return true;
}
return false;
}
IEnumerator loadFile(string path) {
WWW www = new WWW("file://"+path);
AudioClip myAudioClip = www.audioClip;
while (!myAudioClip.isReadyToPlay)
yield return www;
AudioClip clip = www.GetAudioClip(false);
string[] parts = path.Split('\\');
clip.name = parts[parts.Length - 1];
clips.Add(clip);
}
}
[EDIT]
If people wants to improve on the file management I recommend this link

Categories

Resources