Unity Remote与Snow Leopard下Unity for iPhone冲突的解决

调试Unity for iPhone的游戏时候时常莫名其妙的卡死,能正常使用也得看运气.官方Unity for iPhone v1.6的更新日志里也写了这是已知问题.存在于苹果雪豹系统中.官方也一直没修正这个问题.不会要等到Unity3.0发布吧.别啊..

几经搜索后得到一些消息,比如降级回Leopard,给iPhone使用2.2.1OS,可这些都不靠谱.谁不升级用新的啊?!对不?呵呵.

打开Unity Remote项目文件.找到Setup.h,将默认的一些帧率,画质等进行降低设置.Remote端的图像是Jpg,默认压缩率是50,将它降低些,比如25,修改JpegQuality处.如果你不需要重力感应调试可以将它的帧率调整为0,也可以降低整体图像的帧率.我进行这样的设置 FPS = 40, AccelerometerFPS = 15.

Build到iPhone,应该会改良很多.希望大家一起探讨解决开发中遇到的各种问题.

将Unity3D中的地形导出为*.Obj模型文件

将下面的脚本放在你项目目录下资源文件夹的Editor里.
要导出地形,首先在你的场景中选中地形对象.如果没选,他将用于当前场景中可用的地形.从Terrain菜单下选择Export To Obj… ,在分辨率窗口,选择你要四边形还是三角形网格结构.同样也可以选择要导出地形的分辨率,有高中低等等.点击Export,选择要保存的位置和文件名.Obj文件将被导出.要注意如果选择大面积的Full地形导出,最终的Obj文件将非常大,而且也要导出很久.

下面为ExportTerrain.js脚本.

import System.IO;
import System.Text;

enum SaveFormat {Triangles, Quads}
enum SaveResolution {Full, Half, Quarter, Eighth, Sixteenth}

class ExportTerrain extends EditorWindow {
    var saveFormat = SaveFormat.Triangles;
    var saveResolution = SaveResolution.Half;
    static var terrain : TerrainData;
    static var terrainPos : Vector3;
    
    var tCount : int;
    var counter : int;
    var totalCount : int;
    
    @MenuItem ("Terrain/Export To Obj...")
    static function Init () {
        terrain = null;
        var terrainObject : Terrain = Selection.activeObject as Terrain;
        if (!terrainObject) {
            terrainObject = Terrain.activeTerrain;
        }
        if (terrainObject) {
            terrain = terrainObject.terrainData;
            terrainPos = terrainObject.transform.position;
        }
        EditorWindow.GetWindow(ExportTerrain).Show();
    }
    
    function OnGUI () {
        if (!terrain) {
            GUILayout.Label("No terrain found");
            if (GUILayout.Button("Cancel")) {
                EditorWindow.GetWindow(ExportTerrain).Close();
            }
            return;
        }
        saveFormat = EditorGUILayout.EnumPopup("Export Format", saveFormat);
        saveResolution = EditorGUILayout.EnumPopup("Resolution", saveResolution);
        
        if (GUILayout.Button("Export")) {
            Export();
        }
    }
    
    function Export () {
        var fileName = EditorUtility.SaveFilePanel("Export .obj file", "", "Terrain", "obj");
        var w = terrain.heightmapWidth;
        var h = terrain.heightmapHeight;
        var meshScale = terrain.size;
        var tRes = Mathf.Pow(2, parseInt(saveResolution));
        meshScale = Vector3(meshScale.x/(w-1)*tRes, meshScale.y, meshScale.z/(h-1)*tRes);
        var uvScale = Vector2(1.0/(w-1), 1.0/(h-1));
        var tData = terrain.GetHeights(0, 0, w, h);
        
        w = (w-1) / tRes + 1;
        h = (h-1) / tRes + 1;
        var tVertices = new Vector3[w * h];
        var tUV = new Vector2[w * h];
        if (saveFormat == SaveFormat.Triangles) {
            var tPolys = new int[(w-1) * (h-1) * 6];
        }
        else {
            tPolys = new int[(w-1) * (h-1) * 4];
        }
        
        // Build vertices and UVs
        for (y = 0; y < h; y++) {
            for (x = 0; x < w; x++) {
                tVertices[y*w + x] = Vector3.Scale(meshScale, Vector3(x, tData[x*tRes,y*tRes], y)) + terrainPos;
                tUV[y*w + x] = Vector2.Scale(Vector2(x*tRes, y*tRes), uvScale);
            }
        }
    
        var index = 0;
        if (saveFormat == SaveFormat.Triangles) {
            // Build triangle indices: 3 indices into vertex array for each triangle
            for (y = 0; y < h-1; y++) {
                for (x = 0; x < w-1; x++) {
                    // For each grid cell output two triangles
                    tPolys[index++] = (y     * w) + x;
                    tPolys[index++] = ((y+1) * w) + x;
                    tPolys[index++] = (y     * w) + x + 1;
        
                    tPolys[index++] = ((y+1) * w) + x;
                    tPolys[index++] = ((y+1) * w) + x + 1;
                    tPolys[index++] = (y     * w) + x + 1;
                }
            }
        }
        else {
            // Build quad indices: 4 indices into vertex array for each quad
            for (y = 0; y < h-1; y++) {
                for (x = 0; x < w-1; x++) {
                    // For each grid cell output one quad
                    tPolys[index++] = (y     * w) + x;
                    tPolys[index++] = ((y+1) * w) + x;
                    tPolys[index++] = ((y+1) * w) + x + 1;
                    tPolys[index++] = (y     * w) + x + 1;
                }
            }  
        }
    
        // Export to .obj
        try {
            var sw = new StreamWriter(fileName);
            sw.WriteLine("# Unity terrain OBJ File");
            
            // Write vertices
            System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
            counter = tCount = 0;
            totalCount = (tVertices.Length*2 + (saveFormat == SaveFormat.Triangles? tPolys.Length/3 : tPolys.Length/4)) / 1000;
            for (i = 0; i < tVertices.Length; i++) {
                UpdateProgress();
                var sb = StringBuilder("v ", 20);
                // StringBuilder stuff is done this way because it's faster than using the "{0} {1} {2}"etc. format
                // Which is important when you're exporting huge terrains.
                sb.Append(tVertices[i].x.ToString()).Append(" ").
                   Append(tVertices[i].y.ToString()).Append(" ").
                   Append(tVertices[i].z.ToString());
                sw.WriteLine(sb);
            }
            // Write UVs
            for (i = 0; i < tUV.Length; i++) {
                UpdateProgress();
                sb = StringBuilder("vt ", 22);
                sb.Append(tUV[i].x.ToString()).Append(" ").
                   Append(tUV[i].y.ToString());
                sw.WriteLine(sb);
            }
            if (saveFormat == SaveFormat.Triangles) {
                // Write triangles
                for (i = 0; i < tPolys.Length; i += 3) {
                    UpdateProgress();
                    sb = StringBuilder("f ", 43);
                    sb.Append(tPolys[i]+1).Append("/").Append(tPolys[i]+1).Append(" ").
                       Append(tPolys[i+1]+1).Append("/").Append(tPolys[i+1]+1).Append(" ").
                       Append(tPolys[i+2]+1).Append("/").Append(tPolys[i+2]+1);
                    sw.WriteLine(sb);
                }
            }
            else {
                // Write quads
                for (i = 0; i < tPolys.Length; i += 4) {
                    UpdateProgress();
                    sb = StringBuilder("f ", 57);
                    sb.Append(tPolys[i]+1).Append("/").Append(tPolys[i]+1).Append(" ").
                       Append(tPolys[i+1]+1).Append("/").Append(tPolys[i+1]+1).Append(" ").
                       Append(tPolys[i+2]+1).Append("/").Append(tPolys[i+2]+1).Append(" ").
                       Append(tPolys[i+3]+1).Append("/").Append(tPolys[i+3]+1);
                    sw.WriteLine(sb);
                }      
            }
        }
        catch (err) {
            Debug.Log("Error saving file: " + err.Message);
        }
        sw.Close();
        
        terrain = null;
        EditorUtility.ClearProgressBar();
        EditorWindow.GetWindow(ExportTerrain).Close();
    }
    
    function UpdateProgress () {
        if (counter++ == 1000) {
            counter = 0;
            EditorUtility.DisplayProgressBar("Saving...", "", Mathf.InverseLerp(0, totalCount, ++tCount));
        }
    }
}

将3D建模软件建立的地形导入Unity3D中作为地形.

将下面的脚本放在项目文件的资源目录的Editor文件夹下.
Terrain菜单下出现Object to Terrain时,在场景视图或者层次面板选择一个对象,这个对象将转变外高度图用于地形.这个对象必须包含网格.如图:

这个函数用的是和网格轴相同的包围盒,如果对象在x轴和z轴有旋转.或者y轴有90度以上旋转时.可能会有奇怪的结果.
你可能要调整地形的高度匹配你的网格对象(可以使用Terrain菜单下的Set Resolution…).

下面为Object2Terrain.js脚本内容

@MenuItem ("Terrain/Object to Terrain")

static function Object2Terrain () {
    // See if a valid object is selected
    var obj = Selection.activeObject as GameObject;
    if (obj == null) {
        EditorUtility.DisplayDialog("No object selected", "Please select an object.", "Cancel");
        return;
    }
    if (obj.GetComponent(MeshFilter) == null) {
        EditorUtility.DisplayDialog("No mesh selected", "Please select an object with a mesh.", "Cancel");
        return;
    }
    else if ((obj.GetComponent(MeshFilter) as MeshFilter).sharedMesh == null) {
        EditorUtility.DisplayDialog("No mesh selected", "Please select an object with a valid mesh.", "Cancel");
        return;  
    }
    if (Terrain.activeTerrain == null) {
        EditorUtility.DisplayDialog("No terrain found", "Please make sure a terrain exists.", "Cancel");
        return;
    }  
    var terrain = Terrain.activeTerrain.terrainData;
    
    // If there's no mesh collider, add one (and then remove it later when done)
    var addedCollider = false;
    var addedMesh = false;
    var objCollider = obj.collider as MeshCollider;
    if (objCollider == null) {
        objCollider = obj.AddComponent(MeshCollider);
        addedCollider = true;
    }
    else if (objCollider.sharedMesh == null) {
        objCollider.sharedMesh = (obj.GetComponent(MeshFilter) as MeshFilter).sharedMesh;
        addedMesh = true;
    }
    
    Undo.RegisterUndo (terrain, "Object to Terrain");

    var resolutionX = terrain.heightmapWidth;
    var resolutionZ = terrain.heightmapHeight;
    var heights = terrain.GetHeights(0, 0, resolutionX, resolutionZ);
    
    // Use bounds a bit smaller than the actual object; otherwise raycasting tends to miss at the edges
    var objectBounds = objCollider.bounds;
    var leftEdge = objectBounds.center.x - objectBounds.extents.x + .01;
    var bottomEdge = objectBounds.center.z - objectBounds.extents.z + .01;
    var stepX = (objectBounds.size.x - .019) / resolutionX;
    var stepZ = (objectBounds.size.z - .019) / resolutionZ;
    
    // Set up raycast vars
    var y = objectBounds.center.y + objectBounds.extents.y + .01;
    var hit : RaycastHit;
    var ray = new Ray(Vector3.zero, -Vector3.up);
    var rayDistance = objectBounds.size.y + .02;
    var heightFactor = 1.0 / rayDistance;
        
    // Do raycasting samples over the object to see what terrain heights should be
    var z = bottomEdge;
    for (zCount = 0; zCount < resolutionZ; zCount++) {
        var x = leftEdge;
        for (xCount = 0; xCount < resolutionX; xCount++) {
            ray.origin = Vector3(x, y, z);
            if (objCollider.Raycast(ray, hit, rayDistance)) {
                heights[zCount, xCount] = 1.0 - (y - hit.point.y)*heightFactor;
            }
            else {
                heights[zCount, xCount] = 0.0;
            }
            x += stepX;
        }
        z += stepZ;
    }
    
    terrain.SetHeights(0, 0, heights);
    
    if (addedMesh) {
        objCollider.sharedMesh = null;
    }
    if (addedCollider) {
        DestroyImmediate(objCollider);
    }
}