asset文件 unity3d
2018-09-29 12:29:25 aa20274270 阅读数 388
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Node : ScriptableObject {

    public string Name;
}

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class FlowNode : Node {

    public string Type;
}

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GraphSerializationData : ScriptableObject {

    public List<Node> list = new List<Node>();
}

 

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class TestSerializationTool {
    [MenuItem("Assets/TestSerialization")]
    static void TestSerialization() {
        Debug.Log("TestSerialization");

        GraphSerializationData data = ScriptableObject.CreateInstance<GraphSerializationData>();

        string filePath = "Assets/testAsset.asset";
        AssetDatabase.DeleteAsset(filePath);
        AssetDatabase.CreateAsset(data, filePath);
        AssetDatabase.Refresh();


        FlowNode fn = ScriptableObject.CreateInstance<FlowNode>();
        fn.Name = "lzz";
        fn.Type = "123";

        data.list.Add(fn);

        AssetDatabase.AddObjectToAsset(fn, data);

        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();


    }
}

上面的例子 演示 有继承关系的类都可以存储到.asset中。

可以尝试使用 : [PreferBinarySerialization]

参考:https://answers.unity.com/questions/842058/serializing-a-collection-of-scriptableobjects-to-a.html

2018-02-26 14:07:20 qq_15559109 阅读数 4691
在游戏开发中,经常会用到一些配置文件保存一些数据,然后项目运行中读取这些配置文件中的数据在游戏中使用。


如:配置血条:根据角色类型(人物、动物、怪物等)配置不同的血条,包括血条大小,血条名或血条预设,血条颜色等一些简单数据。


如:配置子弹:子弹类型(真子弹、假子弹、追踪子弹等),子弹速度,伤害数值,子弹关联的特效等。


诸如此类的配置很多种,可创建一个可序列化的类存储数据,或者创建 XML 、JSON 文件保存数据,创建 Excel 文件,创建 TXT 文件,皆可完成需求,灵活使用这些方法保存配置数据。


在此介绍一下使用可序列化类保存配置,并且将可序列化类保存成Unity的自定义文件(.asset),然后配置自定义文件(.asset)。


优点:
可以保存数据类型多样(int、string、Vector3、GameObject、Transform、Texture等)如关联预设,关联图片等资源数据,而XML、TXT等只能保存(int、string、Vector3 等基本数据类型)。


缺点:
如果配置数据中保存了(GameObject、Texture)等资源数据,当关联的资源被删除时,配置数据将丢失,需要重新将新的资源再次关联到配置数据上。


下面做个简单的子弹配置数据

方式一:

// 创建一个可序列化的子弹类

Bullet.CSusing UnityEngine;

using System.Collections;

using System;

// 子弹类型枚举

public enum BulletType

{ DirectAttack = 0, // 直接攻击  

Phony, // 假子弹  

Real, // 真子弹 

Track, // 追踪子弹}

/// <summary>/// 可序列化/// </summary>

[Serializable]

public class Bullet : ScriptableObject { // Bullet 类直接继承自 ScriptableObject  

// 子弹类型 public BulletType bulletType = BulletType.DirectAttack;

// 子弹速度
    public int speed = 10;


    // 伤害数值
    public int damage = 5;


    // 子弹关联的特效
    public GameObject effectObj;
}

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


public class CreateAsset : Editor {


    // 在菜单栏创建功能项
    [MenuItem("CreateAsset/Asset")]
    static void Create()
    {
        // 实例化类  Bullet
        ScriptableObject bullet = ScriptableObject.CreateInstance<Bullet>();


        // 如果实例化 Bullet 类为空,返回
        if (!bullet)
        {
            Debug.LogWarning("Bullet not found");
            return;
        }
        // 自定义资源保存路径
        string path = Application.dataPath + "/BulletAeeet";
        // 如果项目总不包含该路径,创建一个
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }


        //将类名 Bullet 转换为字符串
        //拼接保存自定义资源(.asset) 路径
        path = string.Format("Assets/BulletAeeet/{0}.asset", (typeof(Bullet).ToString()));

        // 生成自定义资源到指定路径
        AssetDatabase.CreateAsset(bullet, path);
    }
}

如果想自定义 文件的 Inspector面板,使用编辑器类重写 Bullet.cs 的Inspector面板, 代码如下

using UnityEngine;
using System.Collections;
using UnityEditor;

[CustomEditor(typeof(Bullet))]
public class BulletInspector : Editor {

    // 子弹类型
    public SerializedProperty bulletType;

    // 子弹速度
    public SerializedProperty speed;

    // 伤害数值
    public SerializedProperty damage;

    // 子弹关联的特效
    public SerializedProperty effectObj;

    private void OnEnable()
    {
        bulletType = serializedObject.FindProperty("bulletType");
        speed = serializedObject.FindProperty("speed");
        damage = serializedObject.FindProperty("damage");
        effectObj = serializedObject.FindProperty("effectObj");
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();

        EditorGUI.indentLevel = 1;

        EditorGUILayout.PropertyField(bulletType, new GUIContent("子弹类型"));
        GUILayout.Space(5);

        EditorGUILayout.PropertyField(speed, new GUIContent("子弹速度"));

      GUILayout.Space(5);        EditorGUILayout.PropertyField(damage, new GUIContent("伤害数值"));

        GUILayout.Space(5);
        EditorGUILayout.PropertyField(effectObj, new GUIContent("特效对象"));
        GUILayout.Space(10);
        // 打印数据
        if (GUILayout.Button("Debug"))
        {
            Debug.Log("bulletType    :" + (BulletType)bulletType.enumValueIndex);
            Debug.Log("speed         :" + speed.intValue);
            Debug.Log("damage        :" + damage.intValue);


            if (effectObj.objectReferenceValue)
            {
                Debug.Log("effectObj    :" + effectObj.objectReferenceValue);
            }
        }


        if (GUI.changed)
        {
            EditorUtility.SetDirty(target);
        }

serializedObject.ApplyModifiedProperties();
    }

}

方式二:


读取asset文件



2018-03-04 21:05:00 weixin_34021089 阅读数 10

本文章由cartzhang编写,转载请注明出处。 全部权利保留。


文章链接:http://blog.csdn.net/cartzhang/article/details/60878354
作者:cartzhang

一、前言


美术想要一个把unity中*.asset的模型导出来,导成3D Max能够打开的模式。fbx或obj.

须要导出的格式:

这里写图片描写叙述
图1

也就是须要一个工具,个人认为这个问题。肯定之前Unity的前辈就有解决方法了。于是乎网上一通下载和測试。

二、解包工具集合


网络上找来了各种測试。可是没有一个适合我的。非常多都是失败。打不开。


參考宣雨松的博客。找了还是没有结果。

这里写图片描写叙述
图3

解包工具有非常多种类,
disunity github地址: https://github.com/ata4/disunity

还有就是AssetAssetsExport,还有Unity Studio.
别人的博客里面都有比較多的介绍和说明。这里就具体说了。



最后还网上wiki里,找到了一个合适的我自己的解包。
http://wiki.unity3d.com/index.php?

title=ObjExporter

三、初步成果


找到了一个站点:http://wiki.unity3d.com/index.php?

title=ObjExporter

能够导出部分对象。


例如以下图:

这里写图片描写叙述
图0

而原来unity中模型是这个样子的。

这里写图片描写叙述
图4

导出的仅仅有武器和头盔,没有人物主体body.

四、bug改动

事实上也不能算bug,或许人家没有这种须要呢。

 Component[] meshfilter = selection[i].GetComponentsInChildren<MeshFilter>();
            MeshFilter[] mf = new MeshFilter[meshfilter.Length];
            int m = 0;
            for (; m < meshfilter.Length; m++)
            {
                exportedObjects++;
                mf[m] = (MeshFilter)meshfilter[m];
            }


代码中是要查找全部组件中的MeshFilter,发现SkinnedMeshRender组件竟然没有这个MeshFilter这个组件,所以总会导出少一个,而这个竟然是人的主体。

这里写图片描写叙述
图5

本来说让美术自己加入一个MeshFilter组件,然后依据mesh render中的mesh自己来加入一个相应的mesh.

既然是程序。那就想办法。思路非常明显,既然是有meshrender,就从这入手呗。



代码还是不难度。

// 没有meshFilter。加入一个meshFilter.
            SkinnedMeshRenderer[] meshfilterRender = selection[i].GetComponentsInChildren<SkinnedMeshRenderer>();
            for (int j = 0; j < meshfilterRender.Length; j++)
            {   
                if (meshfilterRender[j].GetComponent<MeshFilter>() == null)
                {
                    meshfilterRender[j].gameObject.AddComponent<MeshFilter>();
                    meshfilterRender[j].GetComponent<MeshFilter>().sharedMesh = Instantiate(meshfilterRender[j].sharedMesh);
                }
            }


这样改动过,就会自己主动在没有MeshFilter,可是有skinnedMeshRender组件的节点下,加入一个MeshFilter,然后就能够正常导出成.obj文件,与.FBX是相似的。都能够被3D max编辑使用。

这里写图片描写叙述
图7

最后的在VS中看的模型,由于没有安装3Dmax.

这里写图片描写叙述
图6

尽管看起来简陋,可是满足他们小须要,就好了。

贴出基本的代码:

/*
Based on ObjExporter.cs, this "wrapper" lets you export to .OBJ directly from the editor menu.

This should be put in your "Editor"-folder. Use by selecting the objects you want to export, and select
the appropriate menu item from "Custom->Export". Exported models are put in a folder called
"ExportedObj" in the root of your Unity-project. Textures should also be copied and placed in the
same folder.
N.B. there may be a bug so if the custom option doesn't come up refer to this thread http://answers.unity3d.com/questions/317951/how-to-use-editorobjexporter-obj-saving-script-fro.html 

Updated for Unity 5.3

2017-03-07
@cartzhang
fixed can not create obj file in folder.
*/

using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System;

struct ObjMaterial
{
    public string name;
    public string textureName;
}

public class EditorObjExporter : ScriptableObject
{
    private static int vertexOffset = 0;
    private static int normalOffset = 0;
    private static int uvOffset = 0;


    //User should probably be able to change this. It is currently left as an excercise for
    //the reader.
    private static string targetFolder = "ExportedObj";


    private static string MeshToString(MeshFilter mf, Dictionary<string, ObjMaterial> materialList)
    {
        Debug.Assert(null != mf);
        Mesh m = mf.sharedMesh;
        Material[] mats = mf.GetComponent<Renderer>().sharedMaterials;

        StringBuilder sb = new StringBuilder();
        if (null == m)
            return sb.ToString();

        sb.Append("g ").Append(mf.name).Append("\n");
        foreach (Vector3 lv in m.vertices)
        {
            Vector3 wv = mf.transform.TransformPoint(lv);

            //This is sort of ugly - inverting x-component since we're in
            //a different coordinate system than "everyone" is "used to".
            sb.Append(string.Format("v {0} {1} {2}\n", -wv.x, wv.y, wv.z));
        }
        sb.Append("\n");

        foreach (Vector3 lv in m.normals)
        {
            Vector3 wv = mf.transform.TransformDirection(lv);

            sb.Append(string.Format("vn {0} {1} {2}\n", -wv.x, wv.y, wv.z));
        }
        sb.Append("\n");

        foreach (Vector3 v in m.uv)
        {
            sb.Append(string.Format("vt {0} {1}\n", v.x, v.y));
        }

        for (int material = 0; material < m.subMeshCount; material++)
        {
            sb.Append("\n");
            sb.Append("usemtl ").Append(mats[material].name).Append("\n");
            sb.Append("usemap ").Append(mats[material].name).Append("\n");

            //See if this material is already in the materiallist.
            try
            {
                ObjMaterial objMaterial = new ObjMaterial();

                objMaterial.name = mats[material].name;

                if (mats[material].mainTexture)
                    objMaterial.textureName = AssetDatabase.GetAssetPath(mats[material].mainTexture);
                else
                    objMaterial.textureName = null;

                materialList.Add(objMaterial.name, objMaterial);
            }
            catch (ArgumentException)
            {
                //Already in the dictionary
            }


            int[] triangles = m.GetTriangles(material);
            for (int i = 0; i < triangles.Length; i += 3)
            {
                //Because we inverted the x-component, we also needed to alter the triangle winding.
                sb.Append(string.Format("f {1}/{1}/{1} {0}/{0}/{0} {2}/{2}/{2}\n",
                    triangles[i] + 1 + vertexOffset, triangles[i + 1] + 1 + normalOffset, triangles[i + 2] + 1 + uvOffset));
            }
        }

        vertexOffset += m.vertices.Length;
        normalOffset += m.normals.Length;
        uvOffset += m.uv.Length;

        return sb.ToString();
    }

    private static void Clear()
    {
        vertexOffset = 0;
        normalOffset = 0;
        uvOffset = 0;
    }

    private static Dictionary<string, ObjMaterial> PrepareFileWrite()
    {
        Clear();

        return new Dictionary<string, ObjMaterial>();
    }

    private static void MaterialsToFile(Dictionary<string, ObjMaterial> materialList, string folder, string filename)
    {
        using (StreamWriter sw = new StreamWriter(folder + Path.DirectorySeparatorChar + filename + ".mtl"))
        {
            foreach (KeyValuePair<string, ObjMaterial> kvp in materialList)
            {
                sw.Write("\n");
                sw.Write("newmtl {0}\n", kvp.Key);
                sw.Write("Ka  0.6 0.6 0.6\n");
                sw.Write("Kd  0.6 0.6 0.6\n");
                sw.Write("Ks  0.9 0.9 0.9\n");
                sw.Write("d  1.0\n");
                sw.Write("Ns  0.0\n");
                sw.Write("illum 2\n");

                if (kvp.Value.textureName != null)
                {
                    string destinationFile = kvp.Value.textureName;


                    int stripIndex = destinationFile.LastIndexOf(Path.DirectorySeparatorChar);

                    if (stripIndex >= 0)
                        destinationFile = destinationFile.Substring(stripIndex + 1).Trim();


                    string relativeFile = destinationFile;

                    destinationFile = folder + Path.DirectorySeparatorChar + destinationFile;

                    Debug.Log("Copying texture from " + kvp.Value.textureName + " to " + destinationFile);

                    try
                    {
                        //Copy the source file
                        File.Copy(kvp.Value.textureName, destinationFile);
                    }
                    catch
                    {

                    }


                    sw.Write("map_Kd {0}", relativeFile);
                }

                sw.Write("\n\n\n");
            }
        }
    }

    private static void MeshToFile(MeshFilter mf, string folder, string filename)
    {
        Dictionary<string, ObjMaterial> materialList = PrepareFileWrite();

        using (StreamWriter sw = new StreamWriter(folder + Path.DirectorySeparatorChar + filename + ".obj"))
        {
            sw.Write("mtllib ./" + filename + ".mtl\n");

            sw.Write(MeshToString(mf, materialList));
        }

        MaterialsToFile(materialList, folder, filename);
    }

    private static void MeshesToFile(MeshFilter[] mf, string folder, string filename)
    {
        Dictionary<string, ObjMaterial> materialList = PrepareFileWrite();

        using (StreamWriter sw = new StreamWriter(folder + Path.DirectorySeparatorChar + filename + ".obj"))
        {
            sw.Write("mtllib ./" + filename + ".mtl\n");

            for (int i = 0; i < mf.Length; i++)
            {
                sw.Write(MeshToString(mf[i], materialList));
            }
        }

        MaterialsToFile(materialList, folder, filename);
    }

    private static bool CreateTargetFolder()
    {
        try
        {
            System.IO.Directory.CreateDirectory(targetFolder);
        }
        catch
        {
            EditorUtility.DisplayDialog("Error!", "Failed to create target folder!", "");
            return false;
        }

        return true;
    }

    [MenuItem("Custom/Export/Export whole selection to single OBJ")]
    static void ExportWholeSelectionToSingle()
    {
        if (!CreateTargetFolder())
            return;


        Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);

        if (selection.Length == 0)
        {
            EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
            return;
        }

        int exportedObjects = 0;

        ArrayList mfList = new ArrayList();

        for (int i = 0; i < selection.Length; i++)
        {
            Component[] meshfilter = selection[i].GetComponentsInChildren(typeof(MeshFilter));

            for (int m = 0; m < meshfilter.Length; m++)
            {
                exportedObjects++;
                mfList.Add(meshfilter[m]);
            }
        }

        if (exportedObjects > 0)
        {
            MeshFilter[] mf = new MeshFilter[mfList.Count];

            for (int i = 0; i < mfList.Count; i++)
            {
                mf[i] = (MeshFilter)mfList[i];
            }

            string filename = EditorSceneManager.GetActiveScene().name + "_" + exportedObjects;

            int stripIndex = filename.LastIndexOf(Path.DirectorySeparatorChar);

            if (stripIndex >= 0)
                filename = filename.Substring(stripIndex + 1).Trim();

            MeshesToFile(mf, targetFolder, filename);


            EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects to " + filename, "");
        }
        else
            EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
    }



    [MenuItem("Custom/Export/Export each selected to single OBJ")]
    static void ExportEachSelectionToSingle()
    {
        if (!CreateTargetFolder())
            return;

        Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);

        if (selection.Length == 0)
        {
            EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
            return;
        }

        int exportedObjects = 0;


        for (int i = 0; i < selection.Length; i++)
        {
            // 没有meshFilter,加入一个meshFilter.
            SkinnedMeshRenderer[] meshfilterRender = selection[i].GetComponentsInChildren<SkinnedMeshRenderer>();
            for (int j = 0; j < meshfilterRender.Length; j++)
            {   
                if (meshfilterRender[j].GetComponent<MeshFilter>() == null)
                {
                    meshfilterRender[j].gameObject.AddComponent<MeshFilter>();
                    meshfilterRender[j].GetComponent<MeshFilter>().sharedMesh = Instantiate(meshfilterRender[j].sharedMesh);
                }
            }

            Component[] meshfilter = selection[i].GetComponentsInChildren<MeshFilter>();
            MeshFilter[] mf = new MeshFilter[meshfilter.Length];
            int m = 0;
            for (; m < meshfilter.Length; m++)
            {
                exportedObjects++;
                mf[m] = (MeshFilter)meshfilter[m];
            }

            MeshesToFile(mf, targetFolder, selection[i].name + "_" + i);
        }

        if (exportedObjects > 0)
        {
            EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects", "");
        }
        else
            EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
    }

}


能够直接复制到直接的项目中使用。

五、怎么使用呢?


首先把代码复制到项目中,直接下载project也行。

步骤一

这里写图片描写叙述
图8

步骤二

这里写图片描写叙述
图10

步骤三

这里写图片描写叙述
图11

就能够使用你的模型编辑工具来查看了。

五、源代码和演示样例project


源代码地址:

https://github.com/cartzhang/unity_lab/blob/master/ExportFbx/UnityAssetExportFBX/Assets/Editor/OBJExport/EditorObjExporter.cs

演示样例project地址:
https://github.com/cartzhang/unity_lab/tree/master/ExportFbx/UnityAssetExportFBX

博客图片地址:
https://github.com/cartzhang/unity_lab/tree/master/ExportFbx/Img

Github readme:
https://github.com/cartzhang/unity_lab/blob/master/ExportFbx/Unity%20asset%E6%96%87%E4%BB%B6%20%E5%AF%BC%E5%87%BAOBJ.md

六、參考

【1】http://www.xuanyusong.com/archives/3618

【2】https://forums.inxile-entertainment.com/viewtopic.php?t=13724

【3】http://www.cnblogs.com/Niger123/p/4261763.html

【4】http://prog3.com/sbdm/download/download/akof1314/9097153

【5】http://wiki.unity3d.com/index.php?

title=ObjExporter

【6】https://github.com/KellanHiggins/UnityFBXExporter/tree/master/Assets/Packages/UnityFBXExporter

七。最后但不是不重要


Asset导出成FBX的格式:https://github.com/cartzhang/UnityFBXExporter

与上面介绍的不是一个方法。可是思路都一样。这个源代码能够把纹理和材质都匹配上去,当然我也做了略微的改动。修复了之前的小bug。



非常感谢,欢迎留言!!

2017-12-13 14:31:00 weixin_33982670 阅读数 88
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System;

struct ObjMaterial
{
    public string name;
    public string textureName;
}

public class EditorObjExporter : ScriptableObject
{
    private static int vertexOffset = 0;
    private static int normalOffset = 0;
    private static int uvOffset = 0;


    //User should probably be able to change this. It is currently left as an excercise for
    //the reader.
    private static string targetFolder = "ExportedObj";


    private static string MeshToString(MeshFilter mf, Dictionary<string, ObjMaterial> materialList)
    {
        Debug.Assert(null != mf);
        Mesh m = mf.sharedMesh;
        Material[] mats = mf.GetComponent<Renderer>().sharedMaterials;

        StringBuilder sb = new StringBuilder();
        if (null == m)
            return sb.ToString();

        sb.Append("g ").Append(mf.name).Append("\n");
        foreach (Vector3 lv in m.vertices)
        {
            Vector3 wv = mf.transform.TransformPoint(lv);

            //This is sort of ugly - inverting x-component since we're in
            //a different coordinate system than "everyone" is "used to".
            sb.Append(string.Format("v {0} {1} {2}\n", -wv.x, wv.y, wv.z));
        }
        sb.Append("\n");

        foreach (Vector3 lv in m.normals)
        {
            Vector3 wv = mf.transform.TransformDirection(lv);

            sb.Append(string.Format("vn {0} {1} {2}\n", -wv.x, wv.y, wv.z));
        }
        sb.Append("\n");

        foreach (Vector3 v in m.uv)
        {
            sb.Append(string.Format("vt {0} {1}\n", v.x, v.y));
        }

        for (int material = 0; material < m.subMeshCount; material++)
        {
            sb.Append("\n");
            sb.Append("usemtl ").Append(mats[material].name).Append("\n");
            sb.Append("usemap ").Append(mats[material].name).Append("\n");

            //See if this material is already in the materiallist.
            try
            {
                ObjMaterial objMaterial = new ObjMaterial();

                objMaterial.name = mats[material].name;

                if (mats[material].mainTexture)
                    objMaterial.textureName = AssetDatabase.GetAssetPath(mats[material].mainTexture);
                else
                    objMaterial.textureName = null;

                materialList.Add(objMaterial.name, objMaterial);
            }
            catch (ArgumentException)
            {
                //Already in the dictionary
            }


            int[] triangles = m.GetTriangles(material);
            for (int i = 0; i < triangles.Length; i += 3)
            {
                //Because we inverted the x-component, we also needed to alter the triangle winding.
                sb.Append(string.Format("f {1}/{1}/{1} {0}/{0}/{0} {2}/{2}/{2}\n",
                    triangles[i] + 1 + vertexOffset, triangles[i + 1] + 1 + normalOffset, triangles[i + 2] + 1 + uvOffset));
            }
        }

        vertexOffset += m.vertices.Length;
        normalOffset += m.normals.Length;
        uvOffset += m.uv.Length;

        return sb.ToString();
    }

    private static void Clear()
    {
        vertexOffset = 0;
        normalOffset = 0;
        uvOffset = 0;
    }

    private static Dictionary<string, ObjMaterial> PrepareFileWrite()
    {
        Clear();

        return new Dictionary<string, ObjMaterial>();
    }

    private static void MaterialsToFile(Dictionary<string, ObjMaterial> materialList, string folder, string filename)
    {
        using (StreamWriter sw = new StreamWriter(folder + Path.DirectorySeparatorChar + filename + ".mtl"))
        {
            foreach (KeyValuePair<string, ObjMaterial> kvp in materialList)
            {
                sw.Write("\n");
                sw.Write("newmtl {0}\n", kvp.Key);
                sw.Write("Ka  0.6 0.6 0.6\n");
                sw.Write("Kd  0.6 0.6 0.6\n");
                sw.Write("Ks  0.9 0.9 0.9\n");
                sw.Write("d  1.0\n");
                sw.Write("Ns  0.0\n");
                sw.Write("illum 2\n");

                if (kvp.Value.textureName != null)
                {
                    string destinationFile = kvp.Value.textureName;


                    int stripIndex = destinationFile.LastIndexOf(Path.DirectorySeparatorChar);

                    if (stripIndex >= 0)
                        destinationFile = destinationFile.Substring(stripIndex + 1).Trim();


                    string relativeFile = destinationFile;

                    destinationFile = folder + Path.DirectorySeparatorChar + destinationFile;

                    Debug.Log("Copying texture from " + kvp.Value.textureName + " to " + destinationFile);

                    try
                    {
                        //Copy the source file
                        File.Copy(kvp.Value.textureName, destinationFile);
                    }
                    catch
                    {

                    }


                    sw.Write("map_Kd {0}", relativeFile);
                }

                sw.Write("\n\n\n");
            }
        }
    }

    private static void MeshToFile(MeshFilter mf, string folder, string filename)
    {
        Dictionary<string, ObjMaterial> materialList = PrepareFileWrite();

        using (StreamWriter sw = new StreamWriter(folder + Path.DirectorySeparatorChar + filename + ".obj"))
        {
            sw.Write("mtllib ./" + filename + ".mtl\n");

            sw.Write(MeshToString(mf, materialList));
        }

        MaterialsToFile(materialList, folder, filename);
    }

    private static void MeshesToFile(MeshFilter[] mf, string folder, string filename)
    {
        Dictionary<string, ObjMaterial> materialList = PrepareFileWrite();

        using (StreamWriter sw = new StreamWriter(folder + Path.DirectorySeparatorChar + filename + ".obj"))
        {
            sw.Write("mtllib ./" + filename + ".mtl\n");

            for (int i = 0; i < mf.Length; i++)
            {
                sw.Write(MeshToString(mf[i], materialList));
            }
        }

        MaterialsToFile(materialList, folder, filename);
    }

    private static bool CreateTargetFolder()
    {
        try
        {
            System.IO.Directory.CreateDirectory(targetFolder);
        }
        catch
        {
            EditorUtility.DisplayDialog("Error!", "Failed to create target folder!", "");
            return false;
        }

        return true;
    }

    [MenuItem("Custom/Export/Export whole selection to single OBJ")]
    static void ExportWholeSelectionToSingle()
    {
        if (!CreateTargetFolder())
            return;


        Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);

        if (selection.Length == 0)
        {
            EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
            return;
        }

        int exportedObjects = 0;

        ArrayList mfList = new ArrayList();

        for (int i = 0; i < selection.Length; i++)
        {
            Component[] meshfilter = selection[i].GetComponentsInChildren(typeof(MeshFilter));

            for (int m = 0; m < meshfilter.Length; m++)
            {
                exportedObjects++;
                mfList.Add(meshfilter[m]);
            }
        }

        if (exportedObjects > 0)
        {
            MeshFilter[] mf = new MeshFilter[mfList.Count];

            for (int i = 0; i < mfList.Count; i++)
            {
                mf[i] = (MeshFilter)mfList[i];
            }

            string filename = EditorSceneManager.GetActiveScene().name + "_" + exportedObjects;

            int stripIndex = filename.LastIndexOf(Path.DirectorySeparatorChar);

            if (stripIndex >= 0)
                filename = filename.Substring(stripIndex + 1).Trim();

            MeshesToFile(mf, targetFolder, filename);


            EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects to " + filename, "");
        }
        else
            EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
    }



    [MenuItem("Custom/Export/Export each selected to single OBJ")]
    static void ExportEachSelectionToSingle()
    {
        if (!CreateTargetFolder())
            return;

        Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);

        if (selection.Length == 0)
        {
            EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
            return;
        }

        int exportedObjects = 0;


        for (int i = 0; i < selection.Length; i++)
        {
            // 没有meshFilter,添加一个meshFilter.
            SkinnedMeshRenderer[] meshfilterRender = selection[i].GetComponentsInChildren<SkinnedMeshRenderer>();
            for (int j = 0; j < meshfilterRender.Length; j++)
            {   
                if (meshfilterRender[j].GetComponent<MeshFilter>() == null)
                {
                    meshfilterRender[j].gameObject.AddComponent<MeshFilter>();
                    meshfilterRender[j].GetComponent<MeshFilter>().sharedMesh = Instantiate(meshfilterRender[j].sharedMesh);
                }
            }

            Component[] meshfilter = selection[i].GetComponentsInChildren<MeshFilter>();
            MeshFilter[] mf = new MeshFilter[meshfilter.Length];
            int m = 0;
            for (; m < meshfilter.Length; m++)
            {
                exportedObjects++;
                mf[m] = (MeshFilter)meshfilter[m];
            }

            MeshesToFile(mf, targetFolder, selection[i].name + "_" + i);
        }

        if (exportedObjects > 0)
        {
            EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects", "");
        }
        else
            EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
    }
}

将脚本命名为ObjExporter.cs,放在项目的Editor目录下,然后完成以下几个步骤

步骤一
3738081-2fff98c03fffb7b5.png
image.png
步骤二
3738081-d45bcfe9a089e098.png
image.png

步骤三
3738081-c3495696e91e282d.png
image.png
2017-03-08 18:34:00 cartzhang 阅读数 14773

本文章由cartzhang编写,转载请注明出处。 所有权利保留。
文章链接:http://blog.csdn.net/cartzhang/article/details/60878354
作者:cartzhang

一、前言


美术想要一个把unity中*.asset的模型导出来,导成3D Max可以打开的模式,fbx或obj.

需要导出的格式:

这里写图片描述
图1

也就是需要一个工具,个人觉得这个问题,肯定之前Unity的前辈就有解决方法了。于是乎网上一通下载和测试。

二、解包工具集合


网络上找来了各种测试,但是没有一个适合我的,很多都是失败,打不开。
参考宣雨松的博客,找了还是没有结果。

这里写图片描述
图3

解包工具有很多种类,
disunity github地址: https://github.com/ata4/disunity

还有就是AssetAssetsExport,还有Unity Studio.
别人的博客里面都有比较多的介绍和说明,这里就详细说了。

最后还网上wiki里,找到了一个合适的我自己的解包。
http://wiki.unity3d.com/index.php?title=ObjExporter

三、初步成果


找到了一个网站:http://wiki.unity3d.com/index.php?title=ObjExporter

可以导出部分对象。
如下图:

这里写图片描述
图0

而原来unity中模型是这个样子的。

这里写图片描述
图4

导出的只有武器和头盔,没有人物主体body.

四、bug修改

其实也不能算bug,也许人家没有这样的需要呢。

 Component[] meshfilter = selection[i].GetComponentsInChildren<MeshFilter>();
            MeshFilter[] mf = new MeshFilter[meshfilter.Length];
            int m = 0;
            for (; m < meshfilter.Length; m++)
            {
                exportedObjects++;
                mf[m] = (MeshFilter)meshfilter[m];
            }


代码中是要查找所有组件中的MeshFilter,发现SkinnedMeshRender组件居然没有这个MeshFilter这个组件,所以总会导出少一个,而这个居然是人的主体。

这里写图片描述
图5

本来说让美术自己添加一个MeshFilter组件,然后根据mesh render中的mesh自己来添加一个对应的mesh.

既然是程序,那就想办法,思路很明显,既然是有meshrender,就从这入手呗。

代码还是不难度。

// 没有meshFilter,添加一个meshFilter.
            SkinnedMeshRenderer[] meshfilterRender = selection[i].GetComponentsInChildren<SkinnedMeshRenderer>();
            for (int j = 0; j < meshfilterRender.Length; j++)
            {   
                if (meshfilterRender[j].GetComponent<MeshFilter>() == null)
                {
                    meshfilterRender[j].gameObject.AddComponent<MeshFilter>();
                    meshfilterRender[j].GetComponent<MeshFilter>().sharedMesh = Instantiate(meshfilterRender[j].sharedMesh);
                }
            }


这样修改过,就会自动在没有MeshFilter,但是有skinnedMeshRender组件的节点下,添加一个MeshFilter,然后就可以正常导出成.obj文件,与.FBX是类似的,都可以被3D max编辑使用。

这里写图片描述
图7

最后的在VS中看的模型,因为没有安装3Dmax.

这里写图片描述
图6

虽然看起来简陋,但是满足他们小需要,就好了。

贴出主要的代码:

/*
Based on ObjExporter.cs, this "wrapper" lets you export to .OBJ directly from the editor menu.

This should be put in your "Editor"-folder. Use by selecting the objects you want to export, and select
the appropriate menu item from "Custom->Export". Exported models are put in a folder called
"ExportedObj" in the root of your Unity-project. Textures should also be copied and placed in the
same folder.
N.B. there may be a bug so if the custom option doesn't come up refer to this thread http://answers.unity3d.com/questions/317951/how-to-use-editorobjexporter-obj-saving-script-fro.html 

Updated for Unity 5.3

2017-03-07
@cartzhang
fixed can not create obj file in folder.
*/

using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System;

struct ObjMaterial
{
    public string name;
    public string textureName;
}

public class EditorObjExporter : ScriptableObject
{
    private static int vertexOffset = 0;
    private static int normalOffset = 0;
    private static int uvOffset = 0;


    //User should probably be able to change this. It is currently left as an excercise for
    //the reader.
    private static string targetFolder = "ExportedObj";


    private static string MeshToString(MeshFilter mf, Dictionary<string, ObjMaterial> materialList)
    {
        Debug.Assert(null != mf);
        Mesh m = mf.sharedMesh;
        Material[] mats = mf.GetComponent<Renderer>().sharedMaterials;

        StringBuilder sb = new StringBuilder();
        if (null == m)
            return sb.ToString();

        sb.Append("g ").Append(mf.name).Append("\n");
        foreach (Vector3 lv in m.vertices)
        {
            Vector3 wv = mf.transform.TransformPoint(lv);

            //This is sort of ugly - inverting x-component since we're in
            //a different coordinate system than "everyone" is "used to".
            sb.Append(string.Format("v {0} {1} {2}\n", -wv.x, wv.y, wv.z));
        }
        sb.Append("\n");

        foreach (Vector3 lv in m.normals)
        {
            Vector3 wv = mf.transform.TransformDirection(lv);

            sb.Append(string.Format("vn {0} {1} {2}\n", -wv.x, wv.y, wv.z));
        }
        sb.Append("\n");

        foreach (Vector3 v in m.uv)
        {
            sb.Append(string.Format("vt {0} {1}\n", v.x, v.y));
        }

        for (int material = 0; material < m.subMeshCount; material++)
        {
            sb.Append("\n");
            sb.Append("usemtl ").Append(mats[material].name).Append("\n");
            sb.Append("usemap ").Append(mats[material].name).Append("\n");

            //See if this material is already in the materiallist.
            try
            {
                ObjMaterial objMaterial = new ObjMaterial();

                objMaterial.name = mats[material].name;

                if (mats[material].mainTexture)
                    objMaterial.textureName = AssetDatabase.GetAssetPath(mats[material].mainTexture);
                else
                    objMaterial.textureName = null;

                materialList.Add(objMaterial.name, objMaterial);
            }
            catch (ArgumentException)
            {
                //Already in the dictionary
            }


            int[] triangles = m.GetTriangles(material);
            for (int i = 0; i < triangles.Length; i += 3)
            {
                //Because we inverted the x-component, we also needed to alter the triangle winding.
                sb.Append(string.Format("f {1}/{1}/{1} {0}/{0}/{0} {2}/{2}/{2}\n",
                    triangles[i] + 1 + vertexOffset, triangles[i + 1] + 1 + normalOffset, triangles[i + 2] + 1 + uvOffset));
            }
        }

        vertexOffset += m.vertices.Length;
        normalOffset += m.normals.Length;
        uvOffset += m.uv.Length;

        return sb.ToString();
    }

    private static void Clear()
    {
        vertexOffset = 0;
        normalOffset = 0;
        uvOffset = 0;
    }

    private static Dictionary<string, ObjMaterial> PrepareFileWrite()
    {
        Clear();

        return new Dictionary<string, ObjMaterial>();
    }

    private static void MaterialsToFile(Dictionary<string, ObjMaterial> materialList, string folder, string filename)
    {
        using (StreamWriter sw = new StreamWriter(folder + Path.DirectorySeparatorChar + filename + ".mtl"))
        {
            foreach (KeyValuePair<string, ObjMaterial> kvp in materialList)
            {
                sw.Write("\n");
                sw.Write("newmtl {0}\n", kvp.Key);
                sw.Write("Ka  0.6 0.6 0.6\n");
                sw.Write("Kd  0.6 0.6 0.6\n");
                sw.Write("Ks  0.9 0.9 0.9\n");
                sw.Write("d  1.0\n");
                sw.Write("Ns  0.0\n");
                sw.Write("illum 2\n");

                if (kvp.Value.textureName != null)
                {
                    string destinationFile = kvp.Value.textureName;


                    int stripIndex = destinationFile.LastIndexOf(Path.DirectorySeparatorChar);

                    if (stripIndex >= 0)
                        destinationFile = destinationFile.Substring(stripIndex + 1).Trim();


                    string relativeFile = destinationFile;

                    destinationFile = folder + Path.DirectorySeparatorChar + destinationFile;

                    Debug.Log("Copying texture from " + kvp.Value.textureName + " to " + destinationFile);

                    try
                    {
                        //Copy the source file
                        File.Copy(kvp.Value.textureName, destinationFile);
                    }
                    catch
                    {

                    }


                    sw.Write("map_Kd {0}", relativeFile);
                }

                sw.Write("\n\n\n");
            }
        }
    }

    private static void MeshToFile(MeshFilter mf, string folder, string filename)
    {
        Dictionary<string, ObjMaterial> materialList = PrepareFileWrite();

        using (StreamWriter sw = new StreamWriter(folder + Path.DirectorySeparatorChar + filename + ".obj"))
        {
            sw.Write("mtllib ./" + filename + ".mtl\n");

            sw.Write(MeshToString(mf, materialList));
        }

        MaterialsToFile(materialList, folder, filename);
    }

    private static void MeshesToFile(MeshFilter[] mf, string folder, string filename)
    {
        Dictionary<string, ObjMaterial> materialList = PrepareFileWrite();

        using (StreamWriter sw = new StreamWriter(folder + Path.DirectorySeparatorChar + filename + ".obj"))
        {
            sw.Write("mtllib ./" + filename + ".mtl\n");

            for (int i = 0; i < mf.Length; i++)
            {
                sw.Write(MeshToString(mf[i], materialList));
            }
        }

        MaterialsToFile(materialList, folder, filename);
    }

    private static bool CreateTargetFolder()
    {
        try
        {
            System.IO.Directory.CreateDirectory(targetFolder);
        }
        catch
        {
            EditorUtility.DisplayDialog("Error!", "Failed to create target folder!", "");
            return false;
        }

        return true;
    }

    [MenuItem("Custom/Export/Export whole selection to single OBJ")]
    static void ExportWholeSelectionToSingle()
    {
        if (!CreateTargetFolder())
            return;


        Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);

        if (selection.Length == 0)
        {
            EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
            return;
        }

        int exportedObjects = 0;

        ArrayList mfList = new ArrayList();

        for (int i = 0; i < selection.Length; i++)
        {
            Component[] meshfilter = selection[i].GetComponentsInChildren(typeof(MeshFilter));

            for (int m = 0; m < meshfilter.Length; m++)
            {
                exportedObjects++;
                mfList.Add(meshfilter[m]);
            }
        }

        if (exportedObjects > 0)
        {
            MeshFilter[] mf = new MeshFilter[mfList.Count];

            for (int i = 0; i < mfList.Count; i++)
            {
                mf[i] = (MeshFilter)mfList[i];
            }

            string filename = EditorSceneManager.GetActiveScene().name + "_" + exportedObjects;

            int stripIndex = filename.LastIndexOf(Path.DirectorySeparatorChar);

            if (stripIndex >= 0)
                filename = filename.Substring(stripIndex + 1).Trim();

            MeshesToFile(mf, targetFolder, filename);


            EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects to " + filename, "");
        }
        else
            EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
    }



    [MenuItem("Custom/Export/Export each selected to single OBJ")]
    static void ExportEachSelectionToSingle()
    {
        if (!CreateTargetFolder())
            return;

        Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);

        if (selection.Length == 0)
        {
            EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
            return;
        }

        int exportedObjects = 0;


        for (int i = 0; i < selection.Length; i++)
        {
            // 没有meshFilter,添加一个meshFilter.
            SkinnedMeshRenderer[] meshfilterRender = selection[i].GetComponentsInChildren<SkinnedMeshRenderer>();
            for (int j = 0; j < meshfilterRender.Length; j++)
            {   
                if (meshfilterRender[j].GetComponent<MeshFilter>() == null)
                {
                    meshfilterRender[j].gameObject.AddComponent<MeshFilter>();
                    meshfilterRender[j].GetComponent<MeshFilter>().sharedMesh = Instantiate(meshfilterRender[j].sharedMesh);
                }
            }

            Component[] meshfilter = selection[i].GetComponentsInChildren<MeshFilter>();
            MeshFilter[] mf = new MeshFilter[meshfilter.Length];
            int m = 0;
            for (; m < meshfilter.Length; m++)
            {
                exportedObjects++;
                mf[m] = (MeshFilter)meshfilter[m];
            }

            MeshesToFile(mf, targetFolder, selection[i].name + "_" + i);
        }

        if (exportedObjects > 0)
        {
            EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects", "");
        }
        else
            EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
    }

}


可以直接复制到直接的项目中使用。

五、怎么使用呢?


首先把代码拷贝到项目中,直接下载工程也行。

步骤一

这里写图片描述
图8

步骤二

这里写图片描述
图10

步骤三

这里写图片描述
图11

就可以使用你的模型编辑工具来查看了。

五、源码和示例工程


源码地址:

https://github.com/cartzhang/unity_lab/blob/master/ExportFbx/UnityAssetExportFBX/Assets/Editor/OBJExport/EditorObjExporter.cs

示例工程地址:
https://github.com/cartzhang/unity_lab/tree/master/ExportFbx/UnityAssetExportFBX

博客图片地址:
https://github.com/cartzhang/unity_lab/tree/master/ExportFbx/Img

Github readme:
https://github.com/cartzhang/unity_lab/blob/master/ExportFbx/Unity%20asset%E6%96%87%E4%BB%B6%20%E5%AF%BC%E5%87%BAOBJ.md

六、参考

【1】http://www.xuanyusong.com/archives/3618

【2】https://forums.inxile-entertainment.com/viewtopic.php?t=13724

【3】http://www.cnblogs.com/Niger123/p/4261763.html

【4】http://prog3.com/sbdm/download/download/akof1314/9097153

【5】http://wiki.unity3d.com/index.php?title=ObjExporter

【6】https://github.com/KellanHiggins/UnityFBXExporter/tree/master/Assets/Packages/UnityFBXExporter

七,最后但不是不重要


Asset导出成FBX的格式:https://github.com/cartzhang/UnityFBXExporter

与上面介绍的不是一个方法,但是思路都一样。这个源码可以把纹理和材质都匹配上去,当然我也做了稍微的修改,修复了之前的小bug。

非常感谢,欢迎留言!!

Unity3D——Asset Server搭建

阅读数 604

http://blog.csdn.net/amazonzx/article/details/7980117AssetServer是目前Unity内部自带的资源版本管理工具,类似于我们平时所熟知的SVN,perForce,但对于目前的Unity,AssetServer要比SVN和perForce等版本控制软件要好用一些,因为Unity3.x版本对于SVN等软件的支持并不是很好,在多人协同工作时,...

博文 来自: qq_31997391

unity3D Asset Server搭建

阅读数 780

本系列文章由Amazonzx编写,欢迎转载,转载请注明出处。http://blog.csdn.net/amazonzx/article/details/7980117AssetServer是目前Unity内部自带的资源版本管理工具,类似于我们平时所熟知的SVN,perForce,但对于目前的Unity,AssetServer要比SVN和perForce等版本控制

博文 来自: qq617119142

Unity3d批量读取非Resources文件夹下的asset

阅读数 11223

例如读取Assets/Jsons/下所有的文本文件

博文 来自: zhuangyou123

Unity3D asset bundle 格式简析

阅读数 1761

http://blog.codingnow.com/2014/08/unity3d_asset_bundle.html Unity3D的assetbundle的格式并没有公开。但为了做更好的差异更新,我们还是希望了解其打包格式。这样可以制作专门的差异比较合并工具,会比直接做二进制差异比较效果好的多。因为可以把assetbundle内的数据拆分为独立单元,只对变更的单元做差异比

博文 来自: OnafioO

Unity3D asset bundle 格式解析

阅读数 521

转自云风的Blog:http://blog.codingnow.com/2014/08/unity3d_asset_bundle.htmlUnity3Dassetbundle格式简析Unity3D的assetbundle的格式并没有公开。但为了做更好的差异更新,我们还是希望了解其打包格式。这样可以制作专门的差异比较合并工具,会比直接做二进制差异比较效果好的多。因为可以把asset

博文 来自: u013895270
没有更多推荐了,返回首页