Unity的改进预览之"新建脚本对话框"

Unity启动了一个叫”NinjaCamp”的项目,旨在快速的扩展改进Unity,集大家的智慧,任意发挥,迸发出新的创意想法,择优保留.

下面我们介绍的是新建脚本对话框功能.目的是改进现在新建脚本的流程.如下图:

可以看到,可以在所选的目录中创建新的脚本.也可以重命名脚本,如果是C#脚本,重命名的时候类名也会随之改变.也能很方便的讲脚本赋予游戏对象.

当你选择添加脚本,这个窗口就会出现并且将悬浮在Unity最上方.这时候可以单击任何游戏对象或者预设将脚本附加过去,也可以点击项目面板任意文件夹来选择保存该脚本的地方.也可以选择你需要的回调函数,免去手写的麻烦.

上面这个图里的想法添加一个更明显方便的方法给添加组件功能.当然.这个还在开发中.

转载请注明来自1Vr.Cn.
原文:http://blogs.unity3d.com/2011/03/23/ninjacamp-iii-new-script-dialog

Unity烘焙材质到贴图脚本

这个脚本可以将复杂的材质,比如有法线贴图的材质进行”烘焙”,转变为单一的贴图,可用来将Unity的游戏移植到移动平台时候使用.

将这个脚本放Editor文件夹里,使用时选择一个Material材质,然后在菜单种”Custom/Bake Material”打开并调整照明和其他参数,点击Bake按钮,就会生成一个单一的贴图.

脚本:BakeMaterial.js
class BakeMaterialSettings
{
    private static var kEditorPrefsName = "BakeMaterialSettings";
    
    static var kBakingLayerShouldBeUnusedInScene = 30;
    static var kStandardTexNames = new Array ("_MainTex", "_BumpMap", "_Detail", "_ParallaxMap", "_Parallax");

    var bakeAlpha = false;
    var bakeMainTexAsWhite = false;
    var minTextureResolution = 8;
    var maxTextureResolution = 2048;

    var emptyScene = false;
    var useCustomLights = false;
    var ambient = Color.black;
    
    static var kLights = 3;
    var enableLight = new boolean[kLights];
    var colorLight = new Color[kLights];
    var dirLight = new Vector2[kLights];
    
    function BakeMaterialSettings ()
    {
        Load ();
    }
    
    function Load ()
    {
        bakeAlpha = EditorPrefs.GetBool(kEditorPrefsName + ".bakeAlpha");
        bakeMainTexAsWhite = EditorPrefs.GetBool(kEditorPrefsName + ".bakeMainTexAsWhite");
        minTextureResolution = EditorPrefs.GetInt(kEditorPrefsName + ".minTextureResolution", 8);
        maxTextureResolution = EditorPrefs.GetInt(kEditorPrefsName + ".maxTextureResolution", 2048);

        emptyScene = EditorPrefs.GetBool(kEditorPrefsName + ".emptyScene");
        useCustomLights = EditorPrefs.GetBool(kEditorPrefsName + ".useCustomLights");
        ambient.r = EditorPrefs.GetFloat(kEditorPrefsName + ".ambient.r");
        ambient.g = EditorPrefs.GetFloat(kEditorPrefsName + ".ambient.g");
        ambient.b = EditorPrefs.GetFloat(kEditorPrefsName + ".ambient.b");
        ambient.a = EditorPrefs.GetFloat(kEditorPrefsName + ".ambient.a", 1.0f);
        
        for (var q = 0; q < kLights; ++q)
        {
            enableLight[q] = EditorPrefs.GetBool(kEditorPrefsName + ".enableLight" + q);
            colorLight[q].r = EditorPrefs.GetFloat(kEditorPrefsName + ".color.r" + q, 0.5f);
            colorLight[q].g = EditorPrefs.GetFloat(kEditorPrefsName + ".color.g" + q, 0.5f);
            colorLight[q].b = EditorPrefs.GetFloat(kEditorPrefsName + ".color.b" + q, 0.5f);
            colorLight[q].a = EditorPrefs.GetFloat(kEditorPrefsName + ".color.a" + q, 1.0f);
            dirLight[q].x = EditorPrefs.GetFloat(kEditorPrefsName + ".dir.x" + q);
            dirLight[q].y = EditorPrefs.GetFloat(kEditorPrefsName + ".dir.y" + q);
        }
    }
    
    function Save ()
    {
        EditorPrefs.SetBool(kEditorPrefsName + ".bakeAlpha", bakeAlpha);
        EditorPrefs.SetBool(kEditorPrefsName + ".bakeMainTexAsWhite", bakeMainTexAsWhite);
        EditorPrefs.SetInt(kEditorPrefsName + ".minTextureResolution", minTextureResolution);
        EditorPrefs.SetInt(kEditorPrefsName + ".maxTextureResolution", maxTextureResolution);

        EditorPrefs.GetBool(kEditorPrefsName + ".emptyScene", emptyScene);
        EditorPrefs.SetBool(kEditorPrefsName + ".useCustomLights", useCustomLights);
        EditorPrefs.SetFloat(kEditorPrefsName + ".ambient.r", ambient.r);
        EditorPrefs.SetFloat(kEditorPrefsName + ".ambient.g", ambient.g);
        EditorPrefs.SetFloat(kEditorPrefsName + ".ambient.b", ambient.b);
        EditorPrefs.SetFloat(kEditorPrefsName + ".ambient.a", ambient.a);

        for (var q = 0; q < kLights; ++q)
        {
            EditorPrefs.SetBool(kEditorPrefsName + ".enableLight" + q, enableLight[q]);
            EditorPrefs.SetFloat(kEditorPrefsName + ".color.r" + q, colorLight[q].r);
            EditorPrefs.SetFloat(kEditorPrefsName + ".color.g" + q, colorLight[q].g);
            EditorPrefs.SetFloat(kEditorPrefsName + ".color.b" + q, colorLight[q].b);
            EditorPrefs.SetFloat(kEditorPrefsName + ".color.a" + q, colorLight[q].a);
            EditorPrefs.SetFloat(kEditorPrefsName + ".dir.x" + q, dirLight[q].x);
            EditorPrefs.SetFloat(kEditorPrefsName + ".dir.y" + q, dirLight[q].y);
        }
    }
}

class BakeMaterial extends EditorWindow
{
    private static var kMateriBakeNodeName = "__MateriaBakeSetup";
    private static var kWindowMinSize = Vector2 (300, 386);
    
    private static var settings : BakeMaterialSettings;
    private static var visible : boolean = false;
    
    private var camera : GameObject;
    private var plane : GameObject;
    private var previewTexture : Texture;
    private var lights : GameObject[] = new GameObject[BakeMaterialSettings.kLights];
    private var stateChanged = false;
    
    private var texViewScrollPosition = Vector2.zero;
    private var lastMaterial : Material;
    
    private var originalScene = "";
    
    private var scheduleBakeOnNextUpdate = false;

    
    private function SetupScene ()
    {
        DestroyScene ();
        var oldGo = GameObject.Find(kMateriBakeNodeName);
        if (oldGo)
            DestroyImmediate (oldGo);
        camera = new GameObject (kMateriBakeNodeName, Camera);
        plane = GameObject.CreatePrimitive (PrimitiveType.Plane);

        var cam = camera;
        cam.camera.backgroundColor = Color.black;
        cam.camera.clearFlags = CameraClearFlags.SolidColor;
        cam.camera.orthographic = true;
        cam.camera.orthographicSize = 5.0;
        cam.camera.cullingMask = 1 << settings.kBakingLayerShouldBeUnusedInScene;
        
        plane.transform.parent = cam.transform;
        plane.transform.position = Vector3.forward * 10.0;
        plane.transform.rotation = Quaternion.Euler (0, 0, 180) * Quaternion.Euler (-90, 0, 0);
        plane.layer = settings.kBakingLayerShouldBeUnusedInScene;
        
        for (var l in lights)
        {
            l = new GameObject ("Light", Light);
            l.light.type = LightType.Directional;
            l.light.cullingMask = 1 << settings.kBakingLayerShouldBeUnusedInScene;
            l.transform.parent = cam.transform;
            l.active = false;
        }
    }
    
    private function UpdateScene (m : Material)
    {
        for (q = 0; q < settings.kLights; ++q)
        {
            lights[q].active = settings.useCustomLights & settings.enableLight[q];
            lights[q].light.color = settings.colorLight[q];
            lights[q].transform.rotation =
                Quaternion.AngleAxis(settings.dirLight[q].x, Vector3.up) *
                Quaternion.AngleAxis(settings.dirLight[q].y, Vector3.right);
        }
        
        if (settings.useCustomLights)
            RenderSettings.ambientLight = settings.ambient;
        else if (settings.emptyScene)
            RenderSettings.ambientLight = Color.white;
            
        plane.renderer.material = m;
    }
        
    private function DestroyScene ()
    {
        GameObject.DestroyImmediate (camera);
        GameObject.DestroyImmediate (plane);
        GameObject.DestroyImmediate (previewTexture);
    }

    function UpdateMaterialPreview (m : Material) : RenderTexture
    {
        if (!m)
            return;
        
        var saveAmbientLight = RenderSettings.ambientLight;
        var saveMainTexture = m.mainTexture;
        if (settings.bakeMainTexAsWhite)
            m.mainTexture = null;
    
        
        // setup
        if (!camera)
            SetupScene ();
        camera.SetActiveRecursively(true);
        UpdateScene (m);
        
        var res = FindLargestTextureResolution (plane.renderer.sharedMaterial, settings.minTextureResolution, settings.maxTextureResolution);
        var rt = RenderCameraToRenderTexture (camera.camera, res.x, res.y);
        
        // restore
        camera.SetActiveRecursively(false);
        RenderSettings.ambientLight = saveAmbientLight;
        m.mainTexture = saveMainTexture;
        
        previewTexture = rt;
        return rt;
    }
    
    function CaptureMaterial(m : Material)
    {
        var matAssetPath = AssetDatabase.GetAssetPath (m);
        var assetPath = System.IO.Path.Combine (System.IO.Path.GetDirectoryName (matAssetPath), System.IO.Path.GetFileNameWithoutExtension (matAssetPath));

        var rt = UpdateMaterialPreview (m);
        RenderTextureToPNG (rt, settings.bakeAlpha, assetPath + ".png");
    }

    function OnEnable ()
    {
        if (!settings)
            settings = new BakeMaterialSettings ();
        SetupScene ();
        visible = true;
    }
    
    function OnDisable ()
    {
        DestroyScene ();
        settings.Save ();
        visible = false;
    }

    static function GetTargetMaterial () : Material
    {
        return EditorUtility.InstanceIDToObject (Selection.activeInstanceID) as Material;
    }

    function OnSelectionChange ()
    {
        Repaint ();
    }

    function Update ()
    {
        var rebuildScene = false;
        if (scheduleBakeOnNextUpdate)
        {
            Bake ();
            scheduleBakeOnNextUpdate = false;
            rebuildScene = true;
        }
        
        if (originalScene == "" && EditorApplication.currentScene == "")
            settings.emptyScene = true;
            
        if (settings.emptyScene && originalScene == "" && EditorApplication.currentScene != "")
        {
            DestroyScene ();
            if (EditorApplication.SaveCurrentSceneIfUserWantsTo ())
            {
                originalScene = EditorApplication.currentScene;
                EditorApplication.NewScene ();
            }
            else
                settings.emptyScene = false;
            rebuildScene = true;            
        }
        else if (!settings.emptyScene && originalScene != "")
        {
            EditorApplication.OpenScene (originalScene);
            rebuildScene = true;
            originalScene = "";
        }
        
        if (rebuildScene)
        {
            SetupScene ();
        }
        
        if (rebuildScene || stateChanged || !settings.emptyScene)
        {
            UpdateMaterialPreview (lastMaterial);
            Repaint ();
            stateChanged = false;
        }
    }
    
    function OnGUI ()
    {
        var material = GetTargetMaterial ();
        if (lastMaterial != material)
            UpdateMaterialPreview (material);
        if (material)
            lastMaterial = material;
        
        EditorGUILayout.BeginHorizontal();
            EditorGUILayout.BeginVertical(GUILayout.MaxWidth(200));
                if (!(originalScene == "" && EditorApplication.currentScene == ""))
                {
                    settings.emptyScene = !EditorGUILayout.BeginToggleGroup("Scene ligthing", !settings.emptyScene);
                    EditorGUILayout.EndToggleGroup();
                }
                settings.useCustomLights = EditorGUILayout.BeginToggleGroup("Custom lighting", settings.useCustomLights);
                if (settings.useCustomLights)
                {
                    EditorGUI.indentLevel = 1;
                    settings.ambient = EditorGUILayout.ColorField("Ambient", settings.ambient);
                    for (var q = 0; q < settings.kLights; ++q)
                    {
                        settings.enableLight[q] = EditorGUILayout.BeginToggleGroup("Light", settings.enableLight[q]);
                        EditorGUI.indentLevel = 2;
                            settings.colorLight[q] = EditorGUILayout.ColorField("Color", settings.colorLight[q]);
                            settings.dirLight[q] = EditorGUILayout.Vector2Field("Direction", settings.dirLight[q]);
                        EditorGUILayout.EndToggleGroup();
                    }
                }
                EditorGUI.indentLevel = 0;
                EditorGUILayout.EndToggleGroup();
                
                settings.bakeAlpha = EditorGUILayout.Toggle("Bake Alpha", settings.bakeAlpha);
                settings.bakeMainTexAsWhite = !EditorGUILayout.Toggle("MainTex", !settings.bakeMainTexAsWhite);
                settings.minTextureResolution = EditorGUILayout.IntField("Min Resolution", settings.minTextureResolution);
                settings.maxTextureResolution = EditorGUILayout.IntField("Max Resolution", settings.maxTextureResolution);
                settings.minTextureResolution = Mathf.Max(2, settings.minTextureResolution);
                settings.maxTextureResolution = Mathf.Max(settings.minTextureResolution, settings.maxTextureResolution);

                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button("Bake"))
                {
                    CaptureMaterial (lastMaterial);
                }
                if (GUILayout.Button("Bake Selected"))
                {
                    scheduleBakeOnNextUpdate = true;
                }
                EditorGUILayout.EndHorizontal();
                
            EditorGUILayout.EndVertical();
            
            texViewScrollPosition = EditorGUILayout.BeginScrollView (texViewScrollPosition);
                var r = GUILayoutUtility.GetAspectRect(1.0f);
                if (previewTexture)
                    EditorGUI.DrawPreviewTexture(r, previewTexture);
            EditorGUILayout.EndScrollView();        
        EditorGUILayout.EndHorizontal();
        
        if (GUI.changed)
        {
            stateChanged = true;
        }
    }
    
    @MenuItem("Custom/Bake Material ...", false, 5)
    static function CreateBakeEditor()
    {
        var window = EditorWindow.GetWindow(BakeMaterial);
        window.title = "Bake Material";
        window.minSize = kWindowMinSize;
        window.Show();
    }

    @MenuItem("Custom/Bake Selected Materials", false, 4)
    static function Bake()
    {
        var instanceIDs = Selection.instanceIDs;
        var currentScene = EditorApplication.currentScene;
    
        var wasAlreadyVisible = BakeMaterial.visible;
        var window = EditorWindow.GetWindow(BakeMaterial);
        
        if (window.settings.emptyScene)
        {
            if (!EditorApplication.SaveCurrentSceneIfUserWantsTo ())
                return;
            EditorApplication.NewScene ();
        }
        
        window.SetupScene ();
        for (var i in instanceIDs)
        {
            var m : Material = EditorUtility.InstanceIDToObject (i) as Material;
            if (m)
                window.CaptureMaterial (m);
        }
        window.DestroyScene ();
        
        if (window.settings.emptyScene && currentScene)
        {
            EditorApplication.OpenScene (currentScene);
        }
        
        if (!wasAlreadyVisible)
            window.Close ();
    }
    
    static function FindLargestTextureResolution (m : Material, minTexRes : int, maxTexRes : int) : Vector2
    {
        var res = Vector2 (minTexRes, minTexRes);
        for (var n in BakeMaterialSettings.kStandardTexNames)
        {
            if (!m.HasProperty (n))
                continue;
        
            var t : Texture = m.GetTexture (n);
            if (!t)
                continue;
            
            res.x = Mathf.Max (res.x, t.width);
            res.y = Mathf.Max (res.y, t.height);
        }
        res.x = Mathf.Min (res.x, maxTexRes);
        res.y = Mathf.Min (res.y, maxTexRes);
        return res;
    }
    
    static function RenderCameraToRenderTexture (cam : Camera, width : int, height : int) : RenderTexture
    {
        var rt = cam.camera.targetTexture;
        if (rt && rt.width != width && rt.height != height)
            DestroyImmediate(rt);
        if (!rt)
            rt = new RenderTexture (width, height, 24);
        cam.camera.targetTexture = rt;
        cam.camera.Render ();
        return rt;
    }
    
    static function RenderTextureToPNG (rt : RenderTexture, bakeAlpha : boolean, assetPath : String)
    {
        RenderTexture.active = rt;
        
        var screenShot = new Texture2D (rt.width, rt.height, bakeAlpha? TextureFormat.ARGB32 : TextureFormat.RGB24, false);
        screenShot.ReadPixels (Rect (0, 0, rt.width, rt.height), 0, 0);
        
        RenderTexture.active = null;
        
        var bytes = screenShot.EncodeToPNG ();
        System.IO.File.WriteAllBytes (assetPath, bytes);
        
        AssetDatabase.ImportAsset (assetPath, ImportAssetOptions.ForceUpdate);
    }
}

Unity中从目前选择对象创建预设(Prefab)脚本

在Unity中选择一个游戏对象,然后通过这个脚本来快速生成一个Prefab预设.
将脚本放在Editor文件夹中,选择游戏对象,支持多选,然后选择菜单GameObject > Create Prefab From Selection,就会在根目录生成一个预设,名字和选择的游戏对象一样.当已经存在同名的预设,将会提示是否覆盖.

脚本名:CreatePrefabFromSelected.cs

using UnityEditor;
using UnityEngine;
using System.Collections;

class CreatePrefabFromSelected : ScriptableObject
{
    const string menuTitle = "GameObject/Create Prefab From Selected";

    /// <summary>
    /// Creates a prefab from the selected game object.
    /// </summary>
    [MenuItem(menuTitle)]
    static void CreatePrefab()
    {
        GameObject[] obj = Selection.gameObjects;

        foreach (GameObject go in obj)
        {
            string name = go.name;
            string localPath = "Assets/" + name + ".prefab";

            if (AssetDatabase.LoadAssetAtPath(localPath, typeof(GameObject)))
            {
                if (EditorUtility.DisplayDialog("Are you sure?", "The prefab already exists. Do you want to overwrite it?", "Yes", "No"))
                {
                    createNew(go, localPath);
                }
            }
            else
            {
                createNew(go, localPath);
            }

        }
    }

    static void createNew(GameObject obj, string localPath)
    {
        Object prefab = EditorUtility.CreateEmptyPrefab(localPath);
        EditorUtility.ReplacePrefab(obj, prefab);
        AssetDatabase.Refresh();

        DestroyImmediate(obj);
        GameObject clone = EditorUtility.InstantiatePrefab(prefab) as GameObject;
    }

    /// <summary>
    /// Validates the menu.
    /// </summary>
    /// <remarks>The item will be disabled if no game object is selected.</remarks>
    [MenuItem(menuTitle, true)]
    static bool ValidateCreatePrefab()
    {
        return Selection.activeGameObject != null;
    }
}

原文:http://www.unifycommunity.com/wiki/index.php?title=CreatePrefabFromSelected

Unity网格资源导出为*.Obj格式模型脚本

这个脚本可以导出Unity的网格资源为模型文件,是利用bjExporter.MeshToString()创建一个文本字符串构成的Obj文件.或者用ObjExporter.MeshToFile()直接保存为一个文件.

将下面2个脚本保存再Editor文件夹中,然后选择一个网格对象,在Custom菜单中根据需要选择导出为Obj文件.文件将被保存在项目目录的ExportedObj文件夹中.

文件名: ObjExporter.cs


using UnityEngine;
using System.Collections;
using System.IO;
using System.Text;

public class ObjExporter {

    public static string MeshToString(MeshFilter mf) {
        Mesh m = mf.mesh;
        Material[] mats = mf.renderer.sharedMaterials;
        
        StringBuilder sb = new StringBuilder();
        
        sb.Append("g ").Append(mf.name).Append("
");
        foreach(Vector3 v in m.vertices) {
            sb.Append(string.Format("v {0} {1} {2}
",v.x,v.y,v.z));
        }
        sb.Append("
");
        foreach(Vector3 v in m.normals) {
            sb.Append(string.Format("vn {0} {1} {2}
",v.x,v.y,v.z));
        }
        sb.Append("
");
        foreach(Vector3 v in m.uv) {
            sb.Append(string.Format("vt {0} {1}
",v.x,v.y));
        }
        for (int material=0; material < m.subMeshCount; material ++) {
            sb.Append("
");
            sb.Append("usemtl ").Append(mats[material].name).Append("
");
            sb.Append("usemap ").Append(mats[material].name).Append("
");
                
            int[] triangles = m.GetTriangles(material);
            for (int i=0;i<triangles.Length;i+=3) {
                sb.Append(string.Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}
",
                    triangles[i]+1, triangles[i+1]+1, triangles[i+2]+1));
            }
        }
        return sb.ToString();
    }
    
    public static void MeshToFile(MeshFilter mf, string filename) {
        using (StreamWriter sw = new StreamWriter(filename))
        {
            sw.Write(MeshToString(mf));
        }
    }
}

文件名: EditorObjExporter.cs
/*
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. */

using UnityEngine;
using UnityEditor;
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)
    {
        Mesh m = mf.sharedMesh;
        Material[] mats = mf.renderer.sharedMaterials;
        
        StringBuilder sb = new StringBuilder();
        
        sb.Append("g ").Append(mf.name).Append("
");
        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}
",-wv.x,wv.y,wv.z));
        }
        sb.Append("
");
        
        foreach(Vector3 lv in m.normals)
        {
            Vector3 wv = mf.transform.TransformDirection(lv);
        
            sb.Append(string.Format("vn {0} {1} {2}
",-wv.x,wv.y,wv.z));
        }
        sb.Append("
");
        
        foreach(Vector3 v in m.uv)
        {
            sb.Append(string.Format("vt {0} {1}
",v.x,v.y));
        }
        
        for (int material=0; material < m.subMeshCount; material ++) {
            sb.Append("
");
            sb.Append("usemtl ").Append(mats[material].name).Append("
");
            sb.Append("usemap ").Append(mats[material].name).Append("
");
                
            //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 = EditorUtility.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}
",
                    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 + "/" + filename + ".mtl"))
        {
            foreach( KeyValuePair<string, ObjMaterial> kvp in materialList )
            {
                sw.Write("
");
                sw.Write("newmtl {0}
", kvp.Key);
                sw.Write("Ka  0.6 0.6 0.6
");
                sw.Write("Kd  0.6 0.6 0.6
");
                sw.Write("Ks  0.9 0.9 0.9
");
                sw.Write("d  1.0
");
                sw.Write("Ns  0.0
");
                sw.Write("illum 2
");
                
                if (kvp.Value.textureName != null)
                {
                    string destinationFile = kvp.Value.textureName;
                
                
                    int stripIndex = destinationFile.LastIndexOf('/');//FIXME: Should be Path.PathSeparator;
        
           if (stripIndex >= 0)
                        destinationFile = destinationFile.Substring(stripIndex + 1).Trim();
                    
                    
                    string relativeFile = destinationFile;
                    
                    destinationFile = folder + "/" + 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("

");
            }
        }
    }
    
    private static void MeshToFile(MeshFilter mf, string folder, string filename)
    {
        Dictionary<string, ObjMaterial> materialList = PrepareFileWrite();
    
        using (StreamWriter sw = new StreamWriter(folder +"/" + filename + ".obj"))
        {
            sw.Write("mtllib ./" + filename + ".mtl
");
        
            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 +"/" + filename + ".obj"))
        {
            sw.Write("mtllib ./" + filename + ".mtl
");
        
            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 all MeshFilters in selection to separate OBJs")]
    static void ExportSelectionToSeparate()
    {
        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++)
        {
         Component[] meshfilter = selection[i].GetComponentsInChildren(typeof(MeshFilter));
        
         for (int m = 0; m < meshfilter.Length; m++)
         {
          exportedObjects++;
          MeshToFile((MeshFilter)meshfilter[m], targetFolder, selection[i].name + "_" + i + "_" + m);
         }
        }
        
        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!", "");
    }
    
    [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 = EditorApplication.currentScene + "_" + exportedObjects;
        
         int stripIndex = filename.LastIndexOf('/');//FIXME: Should be Path.PathSeparator
        
         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++)
        {
         Component[] meshfilter = selection[i].GetComponentsInChildren(typeof(MeshFilter));
        
         MeshFilter[] mf = new MeshFilter[meshfilter.Length];
        
         for (int m = 0; 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!", "");
    }
    
}

原文:http://www.unifycommunity.com/wiki/index.php?title=ObjExporter

奥迪A8l专题天气应用程序-奥迪风云(for iOS)上架

Unity越来越好,越来越火,想必都在努力的让她产生财富.最近忙了这个应用程序.有兴趣的可以下载玩玩.

应用简介:

为风云人物预报万千气象-《奥迪风云》,”奥迪风云”将奥迪的品牌魅力注入天气资讯, 带来耳目一新的使用体验.下载并安装此款应用程序, 即可纵观奥迪风云, 气象万千中, 更可享受保养车提示,洗车指数等实用功能.不仅如此, 您还可以通过奥迪风云应用程序浏览奥迪官方网站, 查询奥迪经销商信息, 查看产品的价格装备, 从而近距离感受奥迪带给您的科技与品牌魅力.
2.0版本说明:
1:修正已知Bug,完善城市添加修改功能.
2:添加整合iPad版本.

全Unity引擎开发,利用Google天气Api接口进行天气获取,XML解析.Unity不只是可以做游戏,做应用软件也是没问题的.呵呵.

视频演示:http://v.youku.com/v_show/id_XMjUzMDc5NjQ0.html

视频2:
http://player.youku.com/player.php/sid/XMjY5MzI5NjAw/v.swf

Appstore下载: http://itunes.apple.com/app/id421807014?mt=8

Unity v3.3发布!Android赶上来了.

Unity刚刚发布了3.3版本更新.距上次3.2版本更新间隔太短了.似乎不是很爽.按照这个速度再更新几次.不是又要掏银子升级了么!

Unity Android(安卓)版现在分为 基础版和专业版.
iOS和Android的Remote(远程调试)已经在免费版本中开放,所以现在你可以测试你的移动平台游戏了.
30天的专业版试用同样包含了iOS专业版和Android专业版的试用.

Android版照比预发布版的变化
Native Activity does not support screen orientation changes during runtime.
Remote refuses to connect to the Editor once the device was disconnected.
Improvement performance of audio handling (mixing) on Tegra based devices.
On-screen keyboard has been completely re-written to support NativeActivity, and handle .hideInput=true;.
Editor detects minimum OS / API version before trying to launch the application.
AndroidJavaObject et al as proper documentation.
Added documentation of the AndroidInput class.
Added correct mapping of CIRCLE button on Xperia Play; must use latest firmware on the device.
Deprecated WWW.oggVorbis property.
Remote: Fixed crash in editor caused by editor side of Android Remote.
Fixed broken detection code for Android SDK API-10 (and API-11).
Fixed problems with threading and finalizers in AndroidJNI et al.
Performance improvements of animation skinning on Tegra based devices.
Support for NativeActivity / SonyEricsson Xperia Play.
Added support for Screen.SetResolution.
Fully dynamic linkage to Mono; Mono is now available from plugins etc.
AndroidManifest.xml attribute minSdkVersion exposed under Player Settings / Other Settings.
Fixed exception when using AndroidJavaObject.Get().
Fixed occasional rendering issues on some Qualcomm based devices (e.g. HTC Vision).
Fixed various network issues (local IP, ping, HavePublicAddress etc).
Fixed various touch input related issues (Input.multiTouchEnabled, virtual touches, stale touch IDs).
Added the Windows registry keys for the JDK lookup on x64 machines.
其他改变
iPhone和Android Remote现在在Unity免费版中也可以用了.
Fixed case 392244: Incorrect handling of tall mode of Game View in Android Remote.
Fixed case 388824: Android Remote flickering.
Fixed case 388828: Android Remote crashes due to buffer overflow.
Fixed case 392869: With Build&Run only remove previous installation when update is not possible (otherwise keep PlayerPrefs etc).
Fixed case 392194: Fixed AndroidJNI lookup of inner (nested) classes.
Fixed case 392099: Touch data reset when resuming application ; fixes stale touches after pause.
Fixed case 392922: Fixed problems when using AndroidJNI and Assembly Stripping.
Fixed case 392847: Flickering rendering problem (mostly seen on GUI elements) or things not being rendered at all.
Fixed case 392831: Input.GetMouseButtonDown(0) and TouchPhase.Began being out of sync.
Fixed case 391064: Changed the message presented to Samsung users when the firmware is outdated.
Fixed case 391739: Text input is now available in NativeActivity mode.
iOS: Fixed VAO cleanup (case 392221: memory leak when using GUI.Label).
iOS: Fixed MSAA+discard support: discard read buffer, not draw; discard stencil too.
Graphics: Improved performance of fixed-function emulation under GLES2.0.
Audio: Correct audio CPU usage displayed in the Profiler.
Audio: Fixed WWW.audioClip (wait for the entire clip to download).
Fixed case 377132: Fixed rare audio bug where one shots are looping.
Fixed case 391171: Better handling of orthographic scene view camera.
Fixed case 383402: Fixed continued bouncing OS X Dock icon when using modal progress bars.
Fixed case 391471: Fixed editor error messages on Windows with some RenderTexture configurations.
Network: Fixed error when reading 32 bit network view IDs size.
Remote: Proper icons for the Android Remote.
Fixed case 388502: AnimationEvents trigger twice when the event pauses animation then starts it again from coroutine.
Fixed case 391106: Font security warnings showing up.
Fixed case 390822: Add implementation for IsDirectoryCreated on iOS.
Fixed case 388828: Various crash fixes for Android Remote.
Fixed case 373197: iPhone Remote prints excessively to the editor console.
Fixed case 388824: Unity Remote white-flickers if you reconnect it to the editor.
Fixed case 389248: Unity Remote refuses to connect to the Editor once the device was disconnected.
iOS: Xcode 4 is now recognized as proper build tool.
iOS: Added soft debugger support.

翻译中.原文http://unity3d.com/unity/whats-new/unity-3.3

Unity for Flash3D要来了.

今天在旧金山Flash的一个游戏大会上,Adobe宣布公开一个代号为”Molehill”的FlashPlayer.它包含了一个非常有趣的新功能,就是对3D的硬件加速支持!

Molehill公开了一个以显卡底层着色为基础的介面.Adobe把重点放在底层开发真的不错.不过这次预发行没有含带一个3D引擎,场景创建工具,模型和动画饿导入到出工具,物理,照明或者光影烘焙工具等等.

恩….是不是觉得上面这些名字听起来很熟悉,是不是有点觉得是Unity.

在过去的几个月里,Unity的工程师一直在调研Unity支持输出FlashPlayer的功能.该项调研结果非常好,现在已经进入了全面生成开发中.

对于Unity的用户来说,无疑遇到了很多问题,例如:

将在Unity Flash版中支持所有Unity的功能函数?
什么时候能发布?
什么时候能有个测试版本?
授权费是多少呢?
它能做A,B或者C么?

这些和其他很多问题.目前还不能得到答案.不过Unity都可以做到而且和其他平台发布做的一样好一样快.

下面有些你现在就可以知道的答案.

问:这会不会意味着Unity的网页播放器的终结?

绝对不是!Flash和Unity的网页播放器都有自己的长处.虽然Unity能发布成Flash播放器且能支持Unity所有功能,但是有很多其他方面的事情Unity更适合!而这只是开发者最后自己决定,是发布到只有Flash播放器中,还是只发布到Unity播放器中,还是两者都包含.

问:我可以用什么语言编程?

答:你将有两个选择:
1.对于会Flash的人,我们会提供一个API能直接使用Flash的ActionScript.像这样:
var go:GameObject = new GameObject(“Just normal ActionScript 3 code”);
2.对于会Unity的人,你可以使用你早已习惯的C#,JavaScript或Boo,在发布的时候他们会自动转换为ActionScript.

这是一个重要的事情,希望大家看到这个消息后会比以往任何事情都兴奋雀跃!

威阿翻译,原文http://blogs.unity3d.com/2011/02/27/unity-flash-3d-on-the-web/comment-page-2/#comment-22391,转载请注明来自1Vr.Cn,否则MJJ.哈哈.

XML格式化及阅读工具"XML Notepad 2007"分享

微软出品的工具,别看标题日期貌似老点,但是小巧可爱啊.

搜寻获得的XPath,还附带了Prefix & Namespace真的很方便.支持多种语法显示和数型结构排列并提供了大量编写XML所需的工具.

下载地址:http://www.microsoft.com/downloads/en/details.aspx?familyid=72d6aa49-787d-4118-ba5f-4f30fe913628&displaylang=en

ps:无聊改QQ签名成XML,结果被说成”心没了”和”想ML”,晕菜…