Creating Assets On The Fly

I want to create an asset on the fly from a file system resource. I have a bunch of text files that are used as content in my game and I want to be replace/update the files prior to building without actually having to open Unity to do it. I thought I might accomplish this by using the command line features with an Editor script that used C# file IO to load my file system resources, create new assets, and then create a new AssetBundle.

The part I haven't figured out is how to create a TextAsset from a file. This appears to be done internally? Any pointers are appreciated.

In the end I went with the Bash script below. This script takes a directory and creates an asset bundle with all the files in the directory. It does that by first creating a temporary Unity project in /tmp, then copying the files to Assets, then it creates a script in the Editor directory that uses BuildPipeline to create the AssetBundle. Anyway, usage is:

script.sh MyDirectoryToBundlify MyAssetBundle.unity3d

Just made it up earlier today, no guarantee there aren't bugs.


#!/bin/bash

UNITY_EXEC=/Applications/Unity/Unity.app/Contents/MacOS/Unity

if [ -z "$2" ]; then
  echo "You must provide a path to the bundle assets and a path to the resulting bundle.";
  exit 1;
fi

export UNITY_ASSET_BUNDLE_PATH=${2}

CREATION_TIME=`date +%s`
ASSET_BUNDLE_PROJECT_DIR=/tmp/AssetBundle-${CREATION_TIME}

echo "Creating temporary project.";
${UNITY_EXEC} -batchmode -quit -createProject ${ASSET_BUNDLE_PROJECT_DIR};

# Copy the project assets from the source folder
echo "Copying resources from source folder to assets folder.";
cd $1;
cp -r . ${ASSET_BUNDLE_PROJECT_DIR}/Assets;

echo "Finding assets.";
cd ${ASSET_BUNDLE_PROJECT_DIR};
ASSETS_TO_BUNDLE=`find Assets -type f -name "*.*" | sed 's/^.\///g' | sed 's/^/assetPathsList.Add("/g' | sed 's/$/");/g'`

# Copy the bundler script into the project
mkdir ${ASSET_BUNDLE_PROJECT_DIR}/Assets/Editor/;
cat > ${ASSET_BUNDLE_PROJECT_DIR}/Assets/Editor/AssetsBundler.cs  assetPathsList = new List();
           ${ASSETS_TO_BUNDLE};

           ArrayList assetList = new ArrayList();

           foreach(string assetPath in assetPathsList)
           {
             Debug.Log("Loading " + assetPath);
             UnityEngine.Object[] assets = AssetDatabase.LoadAllAssetsAtPath(assetPath);
             foreach(UnityEngine.Object asset in assets)
             {
                Debug.Log("Found asset: " + asset.name);
                assetList.Add(asset);
             }
           }

           UnityEngine.Object[] allAssets = (UnityEngine.Object[]) assetList.ToArray(typeof(UnityEngine.Object));    
           BuildPipeline.BuildAssetBundle(null, allAssets, "${UNITY_ASSET_BUNDLE_PATH}", BuildAssetBundleOptions.CompleteAssets);
    }
}
END

echo "Building the bundle.";
${UNITY_EXEC} -batchmode -quit -projectProject ${ASSET_BUNDLE_PROJECT_DIR} -executeMethod AssetsBundler.Bundle;

echo "Deleting temporary project.";
rm -rf ${ASSET_BUNDLE_PROJECT_DIR};

Here is one way to do it that assumes the file is in the Resources directory of your project. This example looks for a file called "puzzles.txt".

StringReader reader = null; 

TextAsset puzdata = (TextAsset)Resources.Load("puzzles", typeof(TextAsset));
// puzdata.text is a string containing the whole file. To read it line-by-line:
reader = new StringReader(puzdata.text);
if ( reader == null )
{
   Debug.Log("puzzles.txt not found or not readable");
}
else
{
   // Read each line from the file
   while ( (string txt = reader.ReadLine()) != null )
      Debug.Log("-->" + txt);
}

Note that this file (and everything in Resources) is being packaged with the rest of the game when Unity builds it.

I wrote on this topic in an article on my website, Reading Text Data Into a Unity Game. It describes other options as well. There are ways to read a textfile that is not packaged into the game. That would be preferred if (for example) you wanted to let modders edit the file without Unity or your source code.

To solve my problem I wrote one script to load the files from the file system using C# System.IO and a class that extends AssetPostprocessor. By creating/copying my files from the file system to a location under the project directories the AssetPostProcessor is called. It collects the assets I'm interested in and then writes them out to a new AssetBundle using BuildPipeline.BuildAssetBundle. Here's a simplified version of each:

using UnityEngine;
using UnityEditor;
using System.Collections;
using System.IO;

public class AssetBundleBuilder : ScriptableObject {

[MenuItem ("Custom/Create Asset")]
static void CreateAsset() {		
	FileStream file = new FileStream("/Users/ben/AssetBundleSandbox/Assets/testFile.xml", FileMode.OpenOrCreate, FileAccess.Write);
    StreamWriter sw = new StreamWriter(file);
    sw.Write("Hello World");
    sw.Close();
    file.Close();   

    AssetDatabase.Refresh(ImportAssetOptions.Default);  
}

}

using UnityEngine;
using UnityEditor;
using System.Collections;

public class CustomPostProcessor :  AssetPostprocessor {
  public static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
  {
  Object[] assetObjects = new Object[importedAssets.Length];
  int assetIndex = 0;

  foreach(string importedAsset in importedAssets)
  {
    Debug.Log("Postprocessing " + importedAsset + "!");
    assetObjects[assetIndex] = AssetDatabase.LoadAssetAtPath(importedAsset, typeof(TextAsset));
  }

  bool buildSuccessful = BuildPipeline.BuildAssetBundle(null, assetObjects, "/tmp/TestAssetBundle.unity3d", BuildAssetBundleOptions.CompleteAssets);
  if(buildSuccessful)
  {
     Debug.Log("Success!"); 
  }
  else
  {
     Debug.Log("Failed!");  
  }
}

}

So I never end up having to create the assets directly but the end result is files outside of a Unity project are imported and then stored in an AssetBundle, which is what I needed.

a mod of unitycoder’s code and create prefab from selected code:

Note: edit the code to make assets and prefabs in different folders because they have same icons in unity and it will be confusing so make 2 subfolders in savedmesh folder for each.


using UnityEditor;
using UnityEngine;
 
/// <summary>
/// Creates a prefab from a selected game object.
/// </summary>
class CreatePrefabFromSelected
{
	const string menuName = "GameObject/Create Prefab From Selected";
	
	/// <summary>
	/// Adds a menu named "Create Prefab From Selected" to the GameObject menu.
	/// </summary>
	[MenuItem(menuName)]
	static void CreatePrefabMenu ()
	{
		var go = Selection.activeGameObject;
		
		Mesh m1 = go.GetComponent<MeshFilter>().mesh;//update line1
		AssetDatabase.CreateAsset(m1, "Assets/savedMesh/" + go.name +"_M" + ".asset"); // update line2
		
		
		var prefab = EditorUtility.CreateEmptyPrefab("Assets/savedMesh/" + go.name + ".prefab");
		EditorUtility.ReplacePrefab(go, prefab);
		AssetDatabase.Refresh();
	}
	
	/// <summary>
	/// Validates the menu.
	/// The item will be disabled if no game object is selected.
	/// </summary>
	/// <returns>True if the menu item is valid.</returns>
	[MenuItem(menuName, true)]
	static bool ValidateCreatePrefabMenu ()
	{
		return Selection.activeGameObject != null;
	}
}

Hey guys, it seems like I found a possible solution, which does not require going through bash scripting. For example it can be the case when taking screenshots of object in the scene and then want to put them onto AssetBundle. Here I made a video with explanations how actually to do that: https://www.youtube.com/watch?v=3uwzxDvXKOU

is it like i can change my prefab later on ??

can u mail me the entire pachage to test plz