存档

2016年1月 的存档

UnityEditor下文件操作方法汇总(Unity3D开发之二十四)

2016年1月27日 没有评论

猴子原创,欢迎转载。转载请注明: 转载自Cocos2Der-CSDN,谢谢!
原文地址: http://blog.csdn.net/cocos2der/article/details/50595585

最近经常需要些一个编译工作脚本,经常操作一个文件。下面是一个汇总了的文件操作方法。

using UnityEngine;

#if UNITY_EDITOR
using UnityEditor;
using System;
using System.IO;
using System.Threading;

public static class FileStaticAPI
{
    /// 检测文件是否存在Application.dataPath目录
    public static bool IsFileExists (string fileName)
    {
        if (fileName.Equals (string.Empty)) {
            return false;
        }

        return File.Exists (GetFullPath (fileName));
    }

    /// 在Application.dataPath目录下创建文件
    public static void CreateFile (string fileName)
    {
        if (!IsFileExists (fileName)) {
            CreateFolder (fileName.Substring (0, fileName.LastIndexOf ('/')));

#if UNITY_4 || UNITY_5
            FileStream stream = File.Create (GetFullPath (fileName));
            stream.Close ();
#else
            File.Create (GetFullPath (fileName));
#endif
        }

    }

    /// 写入数据到对应文件
    public static void Write (string fileName, string contents)
    {
        CreateFolder (fileName.Substring (0, fileName.LastIndexOf ('/')));

        TextWriter tw = new StreamWriter (GetFullPath (fileName), false);
        tw.Write (contents);
        tw.Close (); 

        AssetDatabase.Refresh ();
    }

    /// 从对应文件读取数据
    public static string Read (string fileName)
    {
#if !UNITY_WEBPLAYER
        if (IsFileExists (fileName)) {
            return File.ReadAllText (GetFullPath (fileName));
        } else {
            return "";
        }
#endif

#if UNITY_WEBPLAYER
        Debug.LogWarning("FileStaticAPI::CopyFolder is innored under wep player platfrom");
#endif
    }

    /// 复制文件
    public static void CopyFile (string srcFileName, string destFileName)
    {
        if (IsFileExists (srcFileName) && !srcFileName.Equals (destFileName)) {
            int index = destFileName.LastIndexOf ("/");
            string filePath = string.Empty;

            if (index != -1) {
                filePath = destFileName.Substring (0, index);
            }

            if (!Directory.Exists (GetFullPath (filePath))) {
                Directory.CreateDirectory (GetFullPath (filePath));
            }

            File.Copy (GetFullPath (srcFileName), GetFullPath (destFileName), true);

            AssetDatabase.Refresh ();
        }
    }

    /// 删除文件
    public static void DeleteFile (string fileName)
    {
        if (IsFileExists (fileName)) {
            File.Delete (GetFullPath (fileName));

            AssetDatabase.Refresh ();
        }
    }

    /// 检测是否存在文件夹
    public static bool IsFolderExists (string folderPath)
    {
        if (folderPath.Equals (string.Empty)) {
            return false;
        }

        return Directory.Exists (GetFullPath (folderPath));
    }

    /// 创建文件夹
    public static void CreateFolder (string folderPath)
    {
        if (!IsFolderExists (folderPath)) {
            Directory.CreateDirectory (GetFullPath (folderPath));

            AssetDatabase.Refresh ();
        }
    }

    /// 复制文件夹
    public static void CopyFolder (string srcFolderPath, string destFolderPath)
    {

#if !UNITY_WEBPLAYER
        if (!IsFolderExists (srcFolderPath)) {
            return;
        }

        CreateFolder (destFolderPath);


        srcFolderPath = GetFullPath (srcFolderPath);
        destFolderPath = GetFullPath (destFolderPath);

        // 创建所有的对应目录
        foreach (string dirPath in Directory.GetDirectories(srcFolderPath, "*", SearchOption.AllDirectories)) {
            Directory.CreateDirectory (dirPath.Replace (srcFolderPath, destFolderPath));
        }

        // 复制原文件夹下所有内容到目标文件夹,直接覆盖
        foreach (string newPath in Directory.GetFiles(srcFolderPath, "*.*", SearchOption.AllDirectories)) {

            File.Copy (newPath, newPath.Replace (srcFolderPath, destFolderPath), true);
        }

        AssetDatabase.Refresh ();
#endif

#if UNITY_WEBPLAYER
        Debug.LogWarning("FileStaticAPI::CopyFolder is innored under wep player platfrom");
#endif
    }

    /// 删除文件夹
    public static void DeleteFolder (string folderPath)
    {
        #if !UNITY_WEBPLAYER
        if (IsFolderExists (folderPath)) {

            Directory.Delete (GetFullPath (folderPath), true);

            AssetDatabase.Refresh ();
        }
        #endif

        #if UNITY_WEBPLAYER
        Debug.LogWarning("FileStaticAPI::DeleteFolder is innored under wep player platfrom");
        #endif
    }

    /// 返回Application.dataPath下完整目录
    private static string GetFullPath (string srcName)
    {
        if (srcName.Equals (string.Empty)) {
            return Application.dataPath;
        }

        if (srcName [0].Equals ('/')) {
            srcName.Remove (0, 1);
        }

        return Application.dataPath + "/" + srcName;
    }

    /// 在Assets下创建目录
    public static void CreateAssetFolder (string assetFolderPath)
    {
        if (!IsFolderExists (assetFolderPath)) {
            int index = assetFolderPath.IndexOf ("/");
            int offset = 0;
            string parentFolder = "Assets";
            while (index != -1) {
                if (!Directory.Exists (GetFullPath (assetFolderPath.Substring (0, index)))) {
                    string guid = AssetDatabase.CreateFolder (parentFolder, assetFolderPath.Substring (offset, index - offset));
                    // 将GUID(全局唯一标识符)转换为对应的资源路径。
                    AssetDatabase.GUIDToAssetPath (guid);
                }
                offset = index + 1;
                parentFolder = "Assets/" + assetFolderPath.Substring (0, offset - 1);
                index = assetFolderPath.IndexOf ("/", index + 1);
            }

            AssetDatabase.Refresh ();
        }
    }

    /// 复制Assets下内容
    public static void CopyAsset (string srcAssetName, string destAssetName)
    {
        if (IsFileExists (srcAssetName) && !srcAssetName.Equals (destAssetName)) {
            int index = destAssetName.LastIndexOf ("/");
            string filePath = string.Empty;

            if (index != -1) {
                filePath = destAssetName.Substring (0, index + 1);
                //Create asset folder if needed
                CreateAssetFolder (filePath);
            }


            AssetDatabase.CopyAsset (GetFullAssetPath (srcAssetName), GetFullAssetPath (destAssetName));
            AssetDatabase.Refresh ();
        }
    }

    /// 删除Assets下内容
    public static void DeleteAsset (string assetName)
    {
        if (IsFileExists (assetName)) {
            AssetDatabase.DeleteAsset (GetFullAssetPath (assetName));           
            AssetDatabase.Refresh ();
        }
    }

    /// 获取Assets下完整路径
    private static string GetFullAssetPath (string assetName)
    {
        if (assetName.Equals (string.Empty)) {
            return "Assets/";
        }

        if (assetName [0].Equals ('/')) {
            assetName.Remove (0, 1);
        }

        return "Assets/" + assetName;
    }
}

#endif

需要的可以拿去用用。

分类: 未分类 标签:

hexo + azure 搭建博客

2016年1月19日 没有评论

Azure 创建主机

首先是要在Azure上创建一个虚拟主机。由于我在中国,所以服务器要选择东亚。系统我选的是Ubuntu,因为之前用过,比较熟悉。创建时,先在本地生成一个OpenSSL的匹配串。

openssl.exe req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout myPrivateKey.key -out myCert.pem

生成后在创建虚拟机时,选择myCert.pem文件,这就可以将其配置成默认登陆串。

更改适当的用户名后,确定创建,在一段时间的等待后,虚拟机就创建好了。

登陆

我使用的是Mac系统,所以倒不用配置许多必备的环境。直接运行:

ssh -i myPrivateKey.key -p 22 user@hostname.cloudapp.net 

即可登陆。登陆后,在服务器端生成一个ssh的rsa,用来作免密码登陆。具体方式是运行:

ssh-keygen -t rsa

回答一堆小问题后,rsa即可生成,将私钥id_rsa,拷贝一份到客户端放置到~/.ssh目录下。将公钥id_rsa.pub更名为authorized_keys。这样客户端就可以不用输入密码直接登陆了。

配置Git

由于要起一个HTTP服务,所以得先配个nginx。使用命令

sudo apt-get install nginx

nginx会安装到/etc/nginx目录中,而它的启动程序放到了/user/sbin/nginx。常用命令:

  • sudo nginx 启动
  • sudo nginx -s quie 退出
  • sudo nginx -s reload 重启
  • sudo nginx -t 检查配置错误

值得一提的是Azure有一个坑,如果你希望外网能方位到nginx需要在Azure云服务中开放80端口,在终端点做设置。另外,Azure主机是ping不通的,不要困惑。

后续相关配置可以参考这篇文字

大体逻辑是,在服务器配置一个Git仓库,用来存放导出的网页,这样当hexo更改后,可以直接导出部署,配置如下:

deploy:
    type: git
    message: update
    repo: yourname@hostIP:/home/path/blog.git
    branch: master

最后再创建一个Git hook。每当收到提交,就将自己分支目录的东西复制到网站输出目录,说白了,就是通过git转一道手,实现自动更新网站的功能。

实现代码如下:

$ cd ~/blog.git/hooks
$ touch post-receive
$ vi post-receive

脚本内容:

#!/bin/bash -l
GIT_REPO=/home/git/blog.git
TMP_GIT_CLONE=/tmp/blog
PUBLIC_WWW=/var/www/blog

rm -rf ${TMP_GIT_CLONE}
git clone $GIT_REPO $TMP_GIT_CLONE
rm -rf ${PUBLIC_WWW}/*
cp -rf ${TMP_GIT_CLONE}/* ${PUBLIC_WWW}

更改权限:

$ chmod +x post-receive
$ sudo chmod 775 -R /var/www/blog

分类: 未分类 标签:

Unity3d 开发(七)AssetBundle组织目录

2016年1月19日 没有评论

本文探讨如何配置一个AssetBundle更为合理。

对于结构为

Unity3d 开发(七)AssetBundle组织目录

的目录结构,其中shared是Hero目录下需要用到的公用资源,即公有依赖。可采用如下的打包策略

整个目录打包

将整个100001目录标记为一个包,打包完成后它将依赖shared。

Assets:
- Assets/Characters/Hero/100001/Model/Materials/bianshi_w.mat
- Assets/Characters/Hero/100001/Model/Materials/bianshi_01.mat
- Assets/Characters/Hero/100001/Model/bianshi_01.jpg
- Assets/Characters/Hero/100001/Model/100001.FBX
- Assets/Characters/Hero/100001/100001.prefab
- Assets/Characters/Hero/100001/override.overrideController
- Assets/Characters/Hero/100001/Model/bianshi_w.JPG
Dependencies:
- AssetBundles/Android/shared

大小为120k,shared为4K

只标记预设

将用到的预设标记为一个包,其他资源自动依赖。

Assets:
- Assets/Characters/Hero/100001/100001.prefab
Dependencies:
- AssetBundles/Android/shared

大小为117k,shared为4K

可见当包中文件多时,是会增加大小的。我比照了载入文件,它们完全一致。我可以推测多出来的字节数应该为文件标识,即为了能够根据不同的名字找到资源,需要承担存储标签的开销。

所以结论是:在无需区分文件时,尽量不要在Bundle中区分不同的文件。

DLC适配

如果需要为资源设置多种模型、材质、贴图匹配,就要更改Variant的配置。所以就需要将资源单分成一个包,并为其指定变量类型。

结论

综上所述,打包的目录结构应该是为

  • 预设文件单独指定包。
  • 资源目录如果不需区分DLC则不用处理。如需区分资源要整目录打包,并设置变量,然后在载入时更改AssetBundleManager.ActiveVariants

分类: 未分类 标签:

Unity3d 开发(六) 5.x AssetBundle使用

2016年1月12日 没有评论

本文基于官方文章编写,原文地址

干啥的

AssetBundle是在编辑模式下创建的一系列文件,它们可以被动态加载到Unity运行时中。所以它支持流式加载和异步载入模型,纹理,声音甚至是整个场景,除了唯一的例外:脚本。

优点

  • 动态加载释放一个资源。
  • 发布新的DLC。
  • 可定制的下载需要的部分。
  • 根据用户的位置,语言,喜好配置不同的资源包。
  • 在不重装的情况下修复、更改、升级新资源。

缺点

  • 它完全是下载的,所以如果其中混入了无用资源,会造成存储和带宽的双重浪费。

特性

有些需要注意的地方:

  • AssetBundle会被完整的下载和缓存。
  • AssetBundle中的Asset可以根据需要载入。
  • 在Bundle中的Asset可以依赖其他Asset
  • 在Bundle中的Asset可以和其他Asset共享依赖
  • Each AssetBundle has some technical overhead, both in the size of the file and the need to manage that file.
  • AssetBundle需要区分目标平台

当组织AssetBundle时,需要在过多的、却需要追踪版本的小Bundle,和很少的、却包含冗余信息的大Bundle之间做取舍。

另外,AssetBundle的内容会被针对目标平台编译和优化,因此它需要区分平台编译。

依赖

通常说来,一个资源都跟其他资源有关联,有的引用了好几个材质,有的共用一个材质。因此需要通过一个组织来管理。

资源依赖不会丢失。如果一个资源未被任何Bundle标记,依赖资源会在生成时跟随选中的Bundle自动打包。这可以很方便的防止丢东西,然而会导致资源重复。例如:

现在有两个石头使用相同的材质。
如果它们被分开到不同的Bundle中,那么材质就是隐式的包含。
它会被重复打包两次!

这种情况下,就产生了冗余数据,而且也破坏了共享资源,因为每一个Bundle中持有了自己的材质。为了避免这种情况,材质需要精确的被指定Bundle。它可以独自作为一个材质,或者与其他资源共用一个Bundle,无论哪种情况,这个石头打的Bundle要依赖于材质的Bundle。

依赖和其他信息被存储在Manifest文件中。这个文件十分像一个项目AssetBundle的内容表。当打包资源时,Unity生成大量的文件。这些文件的数据信息都被存入Manifest中。每个平台都会有且只有一个Manifest,它列出了从项目中针对当前目标平台创建的所有AssetBundle,以及它们的所依赖的存储和追踪信息。有了这个Manifest,可以查询到所有的Bundle文件。

Bundle有个AssetBundle Variants设置,它被用来支持定制化的参数。与预定义宏类似,它可以对一个单独对象重映射项目中不同的资源。这可以灵活控制语言、地区、或者用户偏好。AssetBundle Variants能持有资源对象所需的配置参数,并从依据这个值映射到其他需要的对象上。

总结一下,AssetBundle是包含模型、材质、纹理、场景等资源的文件。它由Unity在编辑时创建,并可以在运行使用。它可以加载本地或远程的资源,它也有配置值,根据这个值能够根据用户偏好映射场景中的对象。

使用

由于能够远程下载,跟包走的预存AssetBundle并没有太大的意义。只有在无法下载内容时,默认语言和本地化的备用包才会被使用。同样包中区分平台的资源也没有意义,因为在打包时就配置过了。

简单的工作流:

  • 在编辑器中组织和设置AssetBundles.
  • 打包AssetBundles.
  • 上传AssetBundles到外部存储.
  • 在运行时下载AssetBundles.
  • 加载AssetBundles中的资源.

设置 AssetBundle

选中资源后,可以在Inspector中设置对应的AssetBundle分类,如下图所示:

Unity3d 开发(六) 5.x AssetBundle使用

所有资源必须是小写字母,支持层级命名,例如:aaa/bbb

设置变量

通过设备变量,可以实现定制化的资源控制例如,variant区分了相同资源的不同版本,所以它可以为同一个资源提供不同的解决方案:SD资源和HD资源,或者不同面数的模型。当然它也支持不同的语言,区域,主题。我们可以指定文件夹为对应类型:

Unity3d 开发(六) 5.x AssetBundle使用
由于想要整体替换资源,对应目录下文件需要完全一致。因此在这个粒度下,需要完全覆盖所有资源。

Unity3d 开发(六) 5.x AssetBundle使用

使用AssetBundleManager

AssetBundle Manager是为Unity3d提供高级接口的资源管理插件,可以在这里下载。然后将目录放置到Asset目录下即可,成功后如图所示:

Unity3d 开发(六) 5.x AssetBundle使用

点击Simulation Mode允许编辑器在没有编译资源时直接使用AssetBundle。当这个模式激活时,编辑器会查看注册到AssetBundle的资源,并引用它们的目录,假设它们在AssetBundle中,这样可以极大的简化开发流程。

为了测试Variant,AssetBundle需要被构建和部署。此时需要用到Local Asset Server。当它开启时,AssetBundle必须被构建和放置到Assets的同级目录AssetBundles中:

Unity3d 开发(六) 5.x AssetBundle使用

有了Bundle,就跟外网环境相同了。

运行时使用

AssetBundle Manager的API包括:

  • Initialize() Initializes the AssetBundle manifest object.
  • LoadAssetAsync() Loads a given asset from a given AssetBundle and handles all the dependencies.
  • LoadLevelAsync() Loads a given scene from a given AssetBundle and handles all the dependencies.
  • LoadDependencies() Loads all the dependent AssetBundles for a given AssetBundle.
  • BaseDownloadingURL Sets the base downloading url which is used for automatic downloading dependencies.
  • SimulateAssetBundleInEditor Sets Simulation Mode in the Editor.
  • Variants Sets the active variant.
  • RemapVariantName() Resolves the correct AssetBundle according to the active variant.

分类: 未分类 标签: