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
Related
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.
This code get all the sub folders in a Assets specific folder.
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
public class AddComponents : Editor
{
private static string GetClickedDirFullPath()
{
string clickedAssetGuid = Selection.assetGUIDs[0];
string clickedPath = AssetDatabase.GUIDToAssetPath(clickedAssetGuid);
string clickedPathFull = Path.Combine(Directory.GetCurrentDirectory(), clickedPath);
FileAttributes attr = File.GetAttributes(clickedPathFull);
return attr.HasFlag(FileAttributes.Directory) ? clickedPathFull : Path.GetDirectoryName(clickedPathFull);
}
private static string GetPath()
{
string path = GetClickedDirFullPath();
int index = path.IndexOf("Assets");
string result = path.Substring(index);
return result;
}
private static string[] GetSubFoldersRecursive(string root)
{
var paths = new List<string>();
// If there are no further subfolders then AssetDatabase.GetSubFolders returns
// an empty array => foreach will not be executed
// This is the exit point for the recursion
foreach (var path in AssetDatabase.GetSubFolders(root))
{
// add this subfolder itself
paths.Add(path);
// If this has no further subfolders then simply no new elements are added
paths.AddRange(GetSubFoldersRecursive(path));
}
return paths.ToArray();
}
[MenuItem("Assets/Get Folders")]
private static void GetFolders()
{
List<string> paths = new List<string>();
string selectedPath = GetPath();
var folders = GetSubFoldersRecursive(selectedPath);
foreach (var path in folders)
{
paths.Add(path);
}
}
}
And this get all the prefabs from a specific folder but when I type the folder name :
public List<string> prefabsPaths;
public void AddComponentsToObjects()
{
prefabsPaths = new List<string>();
string[] assetsPaths = AssetDatabase.GetAllAssetPaths();
foreach (string assetPath in assetsPaths)
{
if (assetPath.Contains("Archanor"))
{
if (assetPath.Contains("Prefabs"))
{
prefabsPaths.Add(assetPath);
}
}
}
}
I want to combine both codes so when I make right click and select Get Path it will get all the prefabs from the selected path the right click on the path and the path all sub folders recursive.
Example :
Assets
Folder1
Folder2
Folder3
Folder4
Folder5
Folder6
If in the Assets I make mouse right click on the folder Folder2 and then select from the menu Get Path I want it to get all the prefabs from Folder2 and all the sub folders under Folder2. So In the end I will have a List of all the paths with the folders :
Folder2\1.prefab
Folder2\Folder3\2.prefab
Folder2\Folder3\3.prefab
Folder2\Folder4\4.prefab
In the end my goal is to be able to add to each prefab a component for example a Rigidbody.
So if I have a folder with the 10 prefabs and under it 30 sub folders with more 200 prefabs then get all the folders and prefabs and add a component/s to all the prefabs in all folders.
Here is an alternative and simple way to do this.
string[] files = Directory.GetFiles("DirectoryPath", "*.prefab", SearchOption.AllDirectories);
You can use "Resources.Load("[path]/[filename]") as [Class]" this will give you the asset, prefab, sprite etc you need. Important to note that this will only look in the folder "Resources" in the asset folder (the root in the unity project), so you have to create a "Resources" folder! This is also just a good idea because it can be very slow to look trough all files so this ensures it only looks at the important files. So you can use Vivek nuna's solution to get the file locations and Resources.Load to get the asset. For more information look here: https://docs.unity3d.com/ScriptReference/Resources.html
Hope this helps!
To get actual prefab from path, use AssetDatabase.LoadAssetAtPath(path) and related methods.
It only works in editor, but I guess thats what you need
This is what I wanted and working good for my case :
In the Assets right click on any Folder and then click on Add Components.
It will loop all the folders and sub folders of the folder you did right click Add Components. Then it will find all the prefabs add to them a Rigidbody and save the changes.
Depending on how many prefabs you have it might be a bit slowly but it's doing the job.
Things to add :
Options to add more components or multiple components.
Creating a log/text file with all the changes made prefabs names what changes made and when.
Undo if there an option to make undo on Assets or to make the undo using the text file reading the changes made and revert back.
Backup : Once adding components to make a backup first in a temp folder of the original prefabs (could be also use for undo cases ).
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using UnityEditor;
using UnityEngine;
public class AddComponents : Editor
{
private static string GetClickedDirFullPath()
{
string clickedAssetGuid = Selection.assetGUIDs[0];
string clickedPath = AssetDatabase.GUIDToAssetPath(clickedAssetGuid);
string clickedPathFull = Path.Combine(Directory.GetCurrentDirectory(), clickedPath);
FileAttributes attr = File.GetAttributes(clickedPathFull);
return attr.HasFlag(FileAttributes.Directory) ? clickedPathFull : Path.GetDirectoryName(clickedPathFull);
}
private static string GetPath()
{
string path = GetClickedDirFullPath();
int index = path.IndexOf("Assets");
string result = path.Substring(index);
return result;
}
static List<string> paths = new List<string>();
private static void GetFolders()
{
string selectedPath = GetPath();
string[] assetsPaths = AssetDatabase.GetAllAssetPaths();
foreach (string assetPath in assetsPaths)
{
if (assetPath.Contains(selectedPath))
{
if (assetPath.Contains("Prefabs"))
{
paths.Add(assetPath);
}
}
}
}
[MenuItem("Assets/Add Components")]
public static void AddComponents()
{
GetFolders();
for (int i = 0; i < paths.Count; i++)
{
if (File.Exists(paths[i]))
{
GameObject contentsRoot = PrefabUtility.LoadPrefabContents(paths[i]);
// Modify Prefab contents.
contentsRoot.AddComponent<Rigidbody>();
// Save contents back to Prefab Asset and unload contents.
PrefabUtility.SaveAsPrefabAsset(contentsRoot, paths[i]);
PrefabUtility.UnloadPrefabContents(contentsRoot);
}
}
}
}
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.
I have a unity package with my folder structure pre-setup, but meant to be empty, in order to export the folders I put a file "Delete This File.txt" into every bottom folder in the structure to allow unity to export the folders, but I want to write a script that runs on startup which deletes these files, but i can't seem to get the script to find all the files throughout the various folders. How do i do this?
using UnityEngine;
using UnityEditor;
using System.IO;
[InitializeOnLoad]
public class Startup {
static Startup()
{
foreach (string file in Directory.GetFiles("Assets/")) {
if (file == "Delete This File.txt") {
Debug.Log (file);
} else {
// Do Nothing
}
}
}
}
Some changes are required in your code
[InitializeOnLoad]
public class Startup {
static Startup()
{
// unfortunately this is not available in NET 3.5
// foreach (string file in Directory.EnumerateFiles.....
foreach (string file in Directory.GetFiles("Assets", "Delete This File.txt", SearchOption.AllDirectories))
{
Debug.Log (file);
// File.Delete(file);
}
}
This approach uses the overload of Directory.GetFiles that takes the initial directory, the pattern to search for and the option to search all subdirectory of the root. The resulting strings are the full filenames to delete.
Also, in NET 4.0, there is a better option with Directory.EnumerateFiles as explained by the MSDN docs
The EnumerateFiles and GetFiles methods differ as follows: When you
use EnumerateFiles, you can start enumerating the collection of names
before the whole collection is returned; when you use GetFiles, you
must wait for the whole array of names to be returned before you can
access the array. Therefore, when you are working with many files and
directories, EnumerateFiles can be more efficient.
This can be done with the combination of Directory.GetFiles,Directory.GetDirectories and FileInfo classes. I modified Microsoft's RecursiveFileProcessor code example and added FileInfo and an if statement to make it find all files named "Delete This File.txt" and delete them.
using UnityEngine;
using UnityEditor;
using System.IO;
[InitializeOnLoad]
public class Startup
{
static Startup()
{
ProcessDirectory("Assets/");
}
public static void ProcessDirectory(string targetDirectory)
{
// Process the list of files found in the directory.
string[] fileEntries = Directory.GetFiles(targetDirectory);
foreach (string filePath in fileEntries)
{
FileInfo fileInfo = new FileInfo(filePath);
if (fileInfo.Name == "Delete This File.txt")
{
ProcessFile(filePath);
}
}
// Recurse into subdirectories of this directory.
string[] subdirectoryEntries = Directory.GetDirectories(targetDirectory);
foreach (string subdirectory in subdirectoryEntries)
{
ProcessDirectory(subdirectory);
}
}
// Insert logic for processing found files here.
public static void ProcessFile(string path)
{
Debug.Log("Processed file " + path);
//Delete File
File.Delete(path);
}
}
After discovering that Directory.EnumerateFiles wasn't an option for me I tried the same method with the get files, and found a solution that works. This code not only deletes the text file but the meta file that is associated with it, counting the number of times a text file was deleted and informing me how many were deleted in the log.
using UnityEngine;
using UnityEditor;
using System.IO;
/* Project Name : Ember Guard
* Script Name : RemoveDeleteFile
* Script Path : Assets/Editor/RemoveDeleteFile.cs
* Script Author : FaalFaazDov || Raisltin M. Thoreson
* Created On : 29/08/2016 14:24
* Modified On : N/A
* Version : 0.0.1Alpha
*/
/*************************************************************************
*
* Biophase Entertainment
* __________________
*
* [2016] Biophase Entertainment
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Biophase Entertainment and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Biophase Entertainment
* and its suppliers and may be covered by Canadian and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information, reproduction of this material or attempting
* to read the content of these files is strictly forbidden unless prior written
* permission is obtained from Biophase Entertainment.
*
*/
[InitializeOnLoad]
public class Startup {
static Startup()
{
int x = 0;
string path = Application.dataPath;
string[] files = Directory.GetFiles (path, "Delete This File.txt", SearchOption.AllDirectories);
foreach (string f in files) {
File.Delete (f);
File.Delete (f + ".meta");
x++;
}
string fm;
if (x == 1) {
fm = "file";
} else {
fm = "files";
}
Debug.Log ("Deleted " + x + " " + fm + ".");
}
}
Moving files to the recycle bin and emptying the recycle bin are well documented, but how can a file be programmatically restored from the recycle bin?
There seems not to be a solution in pure C#. You most likely have to resort to P/Invoke.
This article presents a solution in C++ using the SHFileOperation API.
The only other reference to this beyond the previously mentioned link to codeproject that I can see mentions this:
Call SHGetFolderLocation passing CSIDL_BITBUCKET.
Then you can manipulate that folder as usual.
You'll have to create an interop for the SHGetFolderLocation function.
CSIDL_BITBUCKET being the CSIDL ("constant special item ID list") value for the virtual Recycle Bin folder. The quote is taken from here, and will involve interop with the Windows shell. MSDN also mentions that this function has been deprecated in favour of another in Vista.
Hope below code will work to restore the files. Please make sure, STA Calls only supported for shell calls
using System;
using System.Collections;
using System.Windows.Forms;
using System.IO;
using Shell32; //Reference Microsoft Shell Controls And Automation on the COM tab.
using System.Runtime.InteropServices;
using Microsoft.VisualBasic.FileIO;
using System.Threading;
private static void Restore(object param)
{
object[] args = (object[])param;
string filename = (string)args[0];
string filepath = (string)args[1];
Shl = new Shell();
Folder Recycler = Shl.NameSpace(10);
var c = Recycler.Items().Count;
var _recycler = Recycler.Items();
for (int i = 0; i < _recycler.Count; i++)
{
FolderItem FI = _recycler.Item(i);
string FileName = Recycler.GetDetailsOf(FI, 0);
if (Path.GetExtension(FileName) == "") FileName += Path.GetExtension(FI.Path);
//Necessary for systems with hidden file extensions.
string FilePath = Recycler.GetDetailsOf(FI, 1);
if (filepath == Path.Combine(FilePath, FileName))
{
DoVerb(FI, "ESTORE");
break;
}
}
}
private static bool DoVerb(FolderItem Item, string Verb)
{
foreach (FolderItemVerb FIVerb in Item.Verbs())
{
if (FIVerb.Name.ToUpper().Contains(Verb.ToUpper()))
{
FIVerb.DoIt();
return true;
}
}
return false;
}